From d5b2a5cdf10a5f0b4f8c7b36058e6bc7a431495b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 9 Apr 2019 12:10:04 +0200 Subject: [PATCH 001/613] wip --- src/vs/base/common/worker/simpleWorker.ts | 4 +- .../browser/workerExtensions.contribution.ts | 49 +++++++++++++++++++ .../worker/extensionWorker.ts | 20 ++++++++ src/vs/workbench/workbench.main.ts | 2 + 4 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts create mode 100644 src/vs/workbench/contrib/workerExtensions/worker/extensionWorker.ts diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index fa19fff26e927..7cccfe184ad09 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -12,12 +12,12 @@ const INITIALIZE = '$initialize'; export interface IWorker { getId(): number; - postMessage(message: string): void; + postMessage(message: any, transfer?: Transferable[]): void; dispose(): void; } export interface IWorkerCallback { - (message: string): void; + (message: any): void; } export interface IWorkerFactory { diff --git a/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts b/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts new file mode 100644 index 0000000000000..646daa6375ce6 --- /dev/null +++ b/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; +import { IWorker } from 'vs/base/common/worker/simpleWorker'; +import { Event, Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; + +interface IWorker2 extends IWorker { + onMessage: Event; +} + +class WorkerExtensionHost extends Disposable implements IWorkbenchContribution { + + readonly worker: IWorker2; + + constructor() { + super(); + + const emitter = new Emitter(); + const worker = new DefaultWorkerFactory('WorkerExtensionHost').create( + 'vs/workbench/contrib/workerExtensions/worker/extensionWorker', + msg => emitter.fire(msg), + err => console.error(err) + ); + + this.worker = { + dispose() { worker.dispose(); }, + getId() { return worker.getId(); }, + postMessage(msg, transfer?) { worker.postMessage(msg, transfer); }, + onMessage: emitter.event + }; + + this._register(worker); + this._register(emitter); + } + +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + WorkerExtensionHost, + LifecyclePhase.Ready +); + diff --git a/src/vs/workbench/contrib/workerExtensions/worker/extensionWorker.ts b/src/vs/workbench/contrib/workerExtensions/worker/extensionWorker.ts new file mode 100644 index 0000000000000..1f4d6a5e17f86 --- /dev/null +++ b/src/vs/workbench/contrib/workerExtensions/worker/extensionWorker.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; + + +class ExtensionWorker implements IRequestHandler { + + readonly _requestHandlerBrand: any; + + constructor() { + console.log('HERE'); + } +} + +export function create(): IRequestHandler { + return new ExtensionWorker(); +} diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 09627b78382f3..f9fe0b0bf4e37 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -322,4 +322,6 @@ import 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution'; // Issues import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; +import 'vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution'; + //#endregion From 8118a2b2929ccb39a010988413f6aaa440b5b986 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 9 Apr 2019 15:12:32 +0200 Subject: [PATCH 002/613] implement IMEssagePassingProtocol for web workers --- src/vs/base/common/worker/simpleWorker.ts | 4 +- src/vs/base/worker/workerMain.ts | 4 +- .../browser/workerExtensions.contribution.ts | 39 +++++++++------- .../worker/extensionHostWorker.ts | 46 +++++++++++++++++++ .../worker/extensionWorker.ts | 20 -------- 5 files changed, 73 insertions(+), 40 deletions(-) create mode 100644 src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts delete mode 100644 src/vs/workbench/contrib/workerExtensions/worker/extensionWorker.ts diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index 7cccfe184ad09..f0008eb4f9f56 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -204,8 +204,8 @@ export class SimpleWorkerClient extends Disposable { )); this._protocol = new SimpleWorkerProtocol({ - sendMessage: (msg: string): void => { - this._worker.postMessage(msg); + sendMessage: (msg: any, transfer?: Transferable[]): void => { + this._worker.postMessage(msg, transfer); }, handleMessage: (method: string, args: any[]): Promise => { // Intentionally not supporting worker -> main requests diff --git a/src/vs/base/worker/workerMain.ts b/src/vs/base/worker/workerMain.ts index 0ec8b7834dc9d..8d51252951a73 100644 --- a/src/vs/base/worker/workerMain.ts +++ b/src/vs/base/worker/workerMain.ts @@ -20,8 +20,8 @@ let loadCode = function (moduleId: string) { require([moduleId], function (ws) { setTimeout(function () { - let messageHandler = ws.create((msg: any) => { - (self).postMessage(msg); + let messageHandler = ws.create((msg: any, transfer?: Transferable[]) => { + (self).postMessage(msg, transfer); }, null); self.onmessage = (e) => messageHandler.onmessage(e.data); diff --git a/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts b/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts index 646daa6375ce6..3b7185edeffc3 100644 --- a/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts +++ b/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts @@ -7,39 +7,46 @@ import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchCo import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; -import { IWorker } from 'vs/base/common/worker/simpleWorker'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; - -interface IWorker2 extends IWorker { - onMessage: Event; -} +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { VSBuffer } from 'vs/base/common/buffer'; class WorkerExtensionHost extends Disposable implements IWorkbenchContribution { - readonly worker: IWorker2; + readonly protocol: IMessagePassingProtocol; constructor() { super(); - const emitter = new Emitter(); + const emitter = new Emitter(); const worker = new DefaultWorkerFactory('WorkerExtensionHost').create( - 'vs/workbench/contrib/workerExtensions/worker/extensionWorker', - msg => emitter.fire(msg), + 'vs/workbench/contrib/workerExtensions/worker/extensionHostWorker', + data => { + if (data instanceof ArrayBuffer) { + emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); + } else { + console.warn('UNKNOWN data received', data); + } + }, err => console.error(err) ); - this.worker = { - dispose() { worker.dispose(); }, - getId() { return worker.getId(); }, - postMessage(msg, transfer?) { worker.postMessage(msg, transfer); }, - onMessage: emitter.event + this.protocol = { + onMessage: emitter.event, + send: vsbuf => { + const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); + worker.postMessage(data, [data]); + } }; + // this._register(worker); this._register(emitter); - } + // this.protocol.send(VSBuffer.fromString('HELLO from Main')); + // this.protocol.onMessage(buff => console.log(buff.toString())); + } } Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( diff --git a/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts b/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts new file mode 100644 index 0000000000000..569eaf8ae2c91 --- /dev/null +++ b/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { Emitter } from 'vs/base/common/event'; + +class ExtensionWorker implements IRequestHandler { + + // worker-contract + readonly _requestHandlerBrand: any; + readonly onmessage: (data: any) => any; + + // protocol + readonly protocol: IMessagePassingProtocol; + + constructor(postMessage: (message: any, transfer?: Transferable[]) => any) { + + const emitter = new Emitter(); + this.onmessage = data => { + if (data instanceof ArrayBuffer) { + emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); + } else { + console.warn('UNKNOWN data received', data); + } + }; + + this.protocol = { + onMessage: emitter.event, + send: vsbuf => { + const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); + postMessage(data, [data]); + } + }; + } +} + +export function create(postMessage: (message: any, transfer?: Transferable[]) => any): IRequestHandler { + const res = new ExtensionWorker(postMessage); + // res.protocol.onMessage(buff => console.log(buff.toString())); + // res.protocol.send(VSBuffer.fromString('HELLO from WORKER')); + return res; +} diff --git a/src/vs/workbench/contrib/workerExtensions/worker/extensionWorker.ts b/src/vs/workbench/contrib/workerExtensions/worker/extensionWorker.ts deleted file mode 100644 index 1f4d6a5e17f86..0000000000000 --- a/src/vs/workbench/contrib/workerExtensions/worker/extensionWorker.ts +++ /dev/null @@ -1,20 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; - - -class ExtensionWorker implements IRequestHandler { - - readonly _requestHandlerBrand: any; - - constructor() { - console.log('HERE'); - } -} - -export function create(): IRequestHandler { - return new ExtensionWorker(); -} From 9487e5f247473bcf74de4c430cc6de0f871e4111 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 9 Apr 2019 15:54:06 +0200 Subject: [PATCH 003/613] implement extension host starter interface --- .../browser/workerExtensions.contribution.ts | 81 ++++++++++++------- .../worker/extensionHostWorker.ts | 4 +- .../extensions/common/extensionHostStarter.ts | 14 ++++ .../electron-browser/extensionHost.ts | 8 +- .../extensionHostProcessManager.ts | 2 +- 5 files changed, 71 insertions(+), 38 deletions(-) create mode 100644 src/vs/workbench/services/extensions/common/extensionHostStarter.ts diff --git a/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts b/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts index 3b7185edeffc3..f9fae164be6b6 100644 --- a/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts +++ b/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts @@ -7,45 +7,70 @@ import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchCo import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; +import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensionHostStarter'; -class WorkerExtensionHost extends Disposable implements IWorkbenchContribution { +export class ExtensionHostWebWorker extends Disposable implements IExtensionHostStarter { - readonly protocol: IMessagePassingProtocol; + private _protocol?: IMessagePassingProtocol; + private readonly _onDidCrashed = new Emitter<[number, string | null]>(); - constructor() { - super(); + readonly onCrashed: Event<[number, string | null]> = this._onDidCrashed.event; + + start(): Promise { + + if (!this._protocol) { - const emitter = new Emitter(); - const worker = new DefaultWorkerFactory('WorkerExtensionHost').create( - 'vs/workbench/contrib/workerExtensions/worker/extensionHostWorker', - data => { - if (data instanceof ArrayBuffer) { - emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); - } else { - console.warn('UNKNOWN data received', data); + const emitter = new Emitter(); + const worker = new DefaultWorkerFactory('WorkerExtensionHost').create( + 'vs/workbench/contrib/workerExtensions/worker/extensionHostWorker', data => { + if (data instanceof ArrayBuffer) { + emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); + } else { + console.warn('UNKNOWN data received', data); + } + }, err => { + this._onDidCrashed.fire([81, err]); + console.error(err); } - }, - err => console.error(err) - ); - - this.protocol = { - onMessage: emitter.event, - send: vsbuf => { - const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); - worker.postMessage(data, [data]); - } - }; - - // - this._register(worker); - this._register(emitter); + ); + + this._protocol = { + onMessage: emitter.event, + send: vsbuf => { + const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); + worker.postMessage(data, [data]); + } + }; + + // + this._register(worker); + this._register(emitter); + } // this.protocol.send(VSBuffer.fromString('HELLO from Main')); // this.protocol.onMessage(buff => console.log(buff.toString())); + return Promise.resolve(this._protocol); + } + + getInspectPort(): number | undefined { + return undefined; + } +} + + + +class WorkerExtensionHost extends Disposable implements IWorkbenchContribution { + + constructor() { + super(); + new ExtensionHostWebWorker().start().then(protocol => { + protocol.send(VSBuffer.fromString('HELLO from Main')); + protocol.onMessage(buff => console.log(buff.toString())); + }); } } diff --git a/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts b/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts index 569eaf8ae2c91..1655e57c92657 100644 --- a/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts @@ -40,7 +40,7 @@ class ExtensionWorker implements IRequestHandler { export function create(postMessage: (message: any, transfer?: Transferable[]) => any): IRequestHandler { const res = new ExtensionWorker(postMessage); - // res.protocol.onMessage(buff => console.log(buff.toString())); - // res.protocol.send(VSBuffer.fromString('HELLO from WORKER')); + res.protocol.onMessage(buff => console.log(buff.toString())); + res.protocol.send(VSBuffer.fromString('HELLO from WORKER')); return res; } diff --git a/src/vs/workbench/services/extensions/common/extensionHostStarter.ts b/src/vs/workbench/services/extensions/common/extensionHostStarter.ts new file mode 100644 index 0000000000000..32551aaba2d69 --- /dev/null +++ b/src/vs/workbench/services/extensions/common/extensionHostStarter.ts @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; + +export interface IExtensionHostStarter { + readonly onCrashed: Event<[number, string | null]>; + start(): Promise | null; + getInspectPort(): number | undefined; + dispose(): void; +} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 3757b8515d4af..a1429e88ed599 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -38,13 +38,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { parseExtensionDevOptions } from '../common/extensionDevOptions'; import { VSBuffer } from 'vs/base/common/buffer'; import { IExtensionHostDebugService } from 'vs/workbench/services/extensions/common/extensionHostDebug'; - -export interface IExtensionHostStarter { - readonly onCrashed: Event<[number, string | null]>; - start(): Promise | null; - getInspectPort(): number | undefined; - dispose(): void; -} +import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensionHostStarter'; export class ExtensionHostProcessWorker implements IExtensionHostStarter { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index 6d1976eb9b229..c66747f77c0ca 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -13,7 +13,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ExtHostCustomersRegistry } from 'vs/workbench/api/common/extHostCustomers'; import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; -import { IExtensionHostStarter } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; +import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensionHostStarter'; import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol'; From a623755f4a121e9075502bfc2f449e929d0f8b98 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 9 Apr 2019 20:11:19 +0200 Subject: [PATCH 004/613] wip --- .../electron-browser/workbench/workbench.html | 18 +++++++------- .../worker/extensionHostWorker.ts | 12 ++++++++++ .../worker/extensionLoader.ts | 24 +++++++++++++++++++ 3 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 src/vs/workbench/contrib/workerExtensions/worker/extensionLoader.ts diff --git a/src/vs/code/electron-browser/workbench/workbench.html b/src/vs/code/electron-browser/workbench/workbench.html index 1323257775817..56f63b41d481e 100644 --- a/src/vs/code/electron-browser/workbench/workbench.html +++ b/src/vs/code/electron-browser/workbench/workbench.html @@ -1,13 +1,13 @@ - - - - - - + + + + + + - - - \ No newline at end of file + + + diff --git a/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts b/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts index 1655e57c92657..e7883f3cdf2ff 100644 --- a/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts @@ -7,6 +7,7 @@ import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter } from 'vs/base/common/event'; +import { importWrappedScript } from 'vs/workbench/contrib/workerExtensions/worker/extensionLoader'; class ExtensionWorker implements IRequestHandler { @@ -42,5 +43,16 @@ export function create(postMessage: (message: any, transfer?: Transferable[]) => const res = new ExtensionWorker(postMessage); res.protocol.onMessage(buff => console.log(buff.toString())); res.protocol.send(VSBuffer.fromString('HELLO from WORKER')); + + const source = `Object.defineProperty(exports, "__esModule", { value: true }); +const model = require('./model'); +const vscode = require('vscode'); +function activate() { +\tconsole.log('HELLO'); +} +exports.activate = activate;`; + + importWrappedScript(source, 'somepath'); + return res; } diff --git a/src/vs/workbench/contrib/workerExtensions/worker/extensionLoader.ts b/src/vs/workbench/contrib/workerExtensions/worker/extensionLoader.ts new file mode 100644 index 0000000000000..088fa872920bb --- /dev/null +++ b/src/vs/workbench/contrib/workerExtensions/worker/extensionLoader.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +self['extensions'] = {}; +function wrap(name: string, script: string) { + //https://github.com/nodejs/node/blob/master/lib/internal/modules/cjs/loader.js#L125 + return `self['extensions']['${name}']= (function (exports, require) { ${script}\n});`; +} + +export function importWrappedScript(scriptSrc: string, scriptPath: string) { + + importScripts(`data:text/javascript;charset=utf-8,${encodeURIComponent(wrap(scriptPath, scriptSrc))}`); + + // const fn = new Function('exports', 'require', 'module', scriptSrc); + // const exports = Object.create(null); + // const thisRequire = function (path: string) { + // console.log(path); + // }; + // fn(exports, thisRequire, undefined); + // console.log(exports); +} From 92a43a7d0383879955211ebd1b8603731a479793 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 10 Apr 2019 10:01:13 +0200 Subject: [PATCH 005/613] update sample --- .../workerExtensions/browser/workerExtensions.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts b/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts index f9fae164be6b6..b3dbeb9827a95 100644 --- a/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts +++ b/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts @@ -51,8 +51,8 @@ export class ExtensionHostWebWorker extends Disposable implements IExtensionHost this._register(emitter); } - // this.protocol.send(VSBuffer.fromString('HELLO from Main')); - // this.protocol.onMessage(buff => console.log(buff.toString())); + // this._protocol.send(VSBuffer.fromString('HELLO from Main')); + // this._protocol.onMessage(buff => console.log(buff.toString())); return Promise.resolve(this._protocol); } From 24ef8bd5e9966b7722ecf0fd745c3288784e7d00 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 10 Apr 2019 18:35:57 +0200 Subject: [PATCH 006/613] send init data, wire up starter --- .../browser/workerExtensions.contribution.ts | 81 --------- .../worker/extensionHostWorker.ts | 76 ++++++-- .../electron-browser/extensionService.ts | 21 ++- .../webWorkerExtensionHostStarter.ts | 164 ++++++++++++++++++ src/vs/workbench/workbench.main.ts | 2 - tslint.json | 2 +- 6 files changed, 238 insertions(+), 108 deletions(-) delete mode 100644 src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts create mode 100644 src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts diff --git a/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts b/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts deleted file mode 100644 index b3dbeb9827a95..0000000000000 --- a/src/vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution.ts +++ /dev/null @@ -1,81 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; -import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensionHostStarter'; - -export class ExtensionHostWebWorker extends Disposable implements IExtensionHostStarter { - - private _protocol?: IMessagePassingProtocol; - private readonly _onDidCrashed = new Emitter<[number, string | null]>(); - - readonly onCrashed: Event<[number, string | null]> = this._onDidCrashed.event; - - start(): Promise { - - if (!this._protocol) { - - const emitter = new Emitter(); - const worker = new DefaultWorkerFactory('WorkerExtensionHost').create( - 'vs/workbench/contrib/workerExtensions/worker/extensionHostWorker', data => { - if (data instanceof ArrayBuffer) { - emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); - } else { - console.warn('UNKNOWN data received', data); - } - }, err => { - this._onDidCrashed.fire([81, err]); - console.error(err); - } - ); - - this._protocol = { - onMessage: emitter.event, - send: vsbuf => { - const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); - worker.postMessage(data, [data]); - } - }; - - // - this._register(worker); - this._register(emitter); - } - - // this._protocol.send(VSBuffer.fromString('HELLO from Main')); - // this._protocol.onMessage(buff => console.log(buff.toString())); - return Promise.resolve(this._protocol); - } - - getInspectPort(): number | undefined { - return undefined; - } -} - - - -class WorkerExtensionHost extends Disposable implements IWorkbenchContribution { - - constructor() { - super(); - new ExtensionHostWebWorker().start().then(protocol => { - protocol.send(VSBuffer.fromString('HELLO from Main')); - protocol.onMessage(buff => console.log(buff.toString())); - }); - } -} - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( - WorkerExtensionHost, - LifecyclePhase.Ready -); - diff --git a/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts b/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts index e7883f3cdf2ff..0e6081d7b2168 100644 --- a/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts @@ -7,7 +7,21 @@ import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter } from 'vs/base/common/event'; -import { importWrappedScript } from 'vs/workbench/contrib/workerExtensions/worker/extensionLoader'; +import { IExitFn } from 'vs/workbench/services/extensions/node/extensionHostMain'; +import { isMessageOfType, MessageType, createMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; + +// worker-self +declare namespace self { + function close(): void; +} + +// do not allow extensions to call terminate +const nativeTerminate: IExitFn = self.close.bind(self); +self.close = () => console.trace('An extension called terminate and this was prevented'); +let onTerminate = nativeTerminate; + +//todo@joh do not allow extensions to call postMessage and other globals... class ExtensionWorker implements IRequestHandler { @@ -20,39 +34,65 @@ class ExtensionWorker implements IRequestHandler { constructor(postMessage: (message: any, transfer?: Transferable[]) => any) { - const emitter = new Emitter(); + let emitter = new Emitter(); + let terminating = false; + this.onmessage = data => { - if (data instanceof ArrayBuffer) { - emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); - } else { + if (!(data instanceof ArrayBuffer)) { console.warn('UNKNOWN data received', data); + return; + } + + const msg = VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength)); + if (isMessageOfType(msg, MessageType.Terminate)) { + // handle terminate-message right here + terminating = true; + onTerminate(); + return; } + + // emit non-terminate messages to the outside + emitter.fire(msg); }; this.protocol = { onMessage: emitter.event, send: vsbuf => { - const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); - postMessage(data, [data]); + if (!terminating) { + const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); + postMessage(data, [data]); + } } }; } } +interface IRendererConnection { + protocol: IMessagePassingProtocol; + initData: IInitData; +} +function connectToRenderer(protocol: IMessagePassingProtocol): Promise { + return new Promise(resolve => { + const once = protocol.onMessage(raw => { + once.dispose(); + const initData = JSON.parse(raw.toString()); + protocol.send(createMessageOfType(MessageType.Initialized)); + resolve({ protocol, initData }); + }); + protocol.send(createMessageOfType(MessageType.Ready)); + }); +} + export function create(postMessage: (message: any, transfer?: Transferable[]) => any): IRequestHandler { const res = new ExtensionWorker(postMessage); - res.protocol.onMessage(buff => console.log(buff.toString())); - res.protocol.send(VSBuffer.fromString('HELLO from WORKER')); - - const source = `Object.defineProperty(exports, "__esModule", { value: true }); -const model = require('./model'); -const vscode = require('vscode'); -function activate() { -\tconsole.log('HELLO'); -} -exports.activate = activate;`; - importWrappedScript(source, 'somepath'); + connectToRenderer(res.protocol).then(data => { + console.log('INIT_DATA', data.initData); + + data.protocol.onMessage(msg => { + // console.log('SOME MSG', msg.toString()); + }); + }); return res; } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 8483f12b35c57..5cfe7f0024d12 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -35,6 +35,7 @@ import { Schemas } from 'vs/base/common/network'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; +import { WebWorkerExtensionHostStarter } from './webWorkerExtensionHostStarter'; const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); @@ -438,12 +439,20 @@ export class ExtensionService extends Disposable implements IExtensionService { autoStart = true; extensions = this.getExtensions(); } - - const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._extensionHostLogsLocation); - const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents); - extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); - extHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ target: extHostProcessManager, isResponsive: responsiveState === ResponsiveState.Responsive }); }); - this._extensionHostProcessManagers.push(extHostProcessManager); + { + const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._extensionHostLogsLocation); + const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents); + extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); + extHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ target: extHostProcessManager, isResponsive: responsiveState === ResponsiveState.Responsive }); }); + this._extensionHostProcessManagers.push(extHostProcessManager); + } + { + const extHostWebWorkerWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, autoStart, extensions, this._extensionHostLogsLocation); + const extHostWebWorkerManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostWebWorkerWorker, null, initialActivationEvents); + extHostWebWorkerManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); + extHostWebWorkerManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ target: extHostWebWorkerManager, isResponsive: responsiveState === ResponsiveState.Responsive }); }); + this._extensionHostProcessManagers.push(extHostWebWorkerManager); + } } private _onExtensionHostCrashed(code: number, signal: string | null): void { diff --git a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts new file mode 100644 index 0000000000000..fc843bab3b890 --- /dev/null +++ b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts @@ -0,0 +1,164 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensionHostStarter'; +import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import * as platform from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; +import product from 'vs/platform/product/node/product'; +import pkg from 'vs/platform/product/node/package'; + +export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { + + private _protocol?: IMessagePassingProtocol; + private _isTerminating?: boolean; + private _toDispose: IDisposable[] = []; + + private readonly _onDidCrashed = new Emitter<[number, string | null]>(); + readonly onCrashed: Event<[number, string | null]> = this._onDidCrashed.event; + + constructor( + private readonly _autoStart: boolean, + private readonly _extensions: Promise, + private readonly _extensionHostLogsLocation: URI, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, + @ILabelService private readonly _labelService: ILabelService, + @ILogService private readonly _logService: ILogService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + ) { + + } + + async start(): Promise { + + if (!this._protocol) { + + const emitter = new Emitter(); + const worker = new DefaultWorkerFactory('WorkerExtensionHost').create( + 'vs/workbench/contrib/workerExtensions/worker/extensionHostWorker', data => { + if (data instanceof ArrayBuffer) { + emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); + } else { + console.warn('UNKNOWN data received', data); + } + }, err => { + this._onDidCrashed.fire([81, err]); + console.error(err); + } + ); + + // keep for cleanup + this._toDispose.push(worker, emitter); + + const protocol: IMessagePassingProtocol = { + onMessage: emitter.event, + send: vsbuf => { + const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); + worker.postMessage(data, [data]); + } + }; + + // extension host handshake happens below + // (1) <== wait for: Ready + // (2) ==> send: init data + // (3) <== wait for: Initialized + + await Event.toPromise(Event.filter(protocol.onMessage, msg => isMessageOfType(msg, MessageType.Ready))); + protocol.send(VSBuffer.fromString(JSON.stringify(await this._createExtHostInitData()))); + await Event.toPromise(Event.filter(protocol.onMessage, msg => isMessageOfType(msg, MessageType.Initialized))); + + this._protocol = protocol; + } + return this._protocol; + + } + + dispose(): void { + if (!this._protocol) { + // nothing else to do + dispose(this._toDispose); + return; + } + if (!this._isTerminating) { + this._isTerminating = true; + + this._protocol.send(createMessageOfType(MessageType.Terminate)); + setTimeout(() => dispose(this._toDispose), 10 * 1000); + } + } + + getInspectPort(): number | undefined { + return undefined; + } + + private _createExtHostInitData(): Promise { + return Promise.all([this._telemetryService.getTelemetryInfo(), this._extensions]) + .then(([telemetryInfo, extensionDescriptions]) => { + const workspace = this._contextService.getWorkspace(); + const r: IInitData = { + commit: product.commit, + version: pkg.version, + parentPid: process.pid, + environment: { + isExtensionDevelopmentDebug: false, // < todo@joh + appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, + appSettingsHome: this._environmentService.appSettingsHome ? URI.file(this._environmentService.appSettingsHome) : undefined, + appName: product.nameLong, + appUriScheme: product.urlProtocol, + appLanguage: platform.language, + extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, + extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, + globalStorageHome: URI.file(this._environmentService.globalStorageHome), + userHome: URI.file(this._environmentService.userHome) + }, + workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { + configuration: workspace.configuration || undefined, + id: workspace.id, + name: this._labelService.getWorkspaceLabel(workspace) + }, + resolvedExtensions: [], + hostExtensions: [], + extensions: extensionDescriptions, + telemetryInfo, + logLevel: this._logService.getLevel(), + logsLocation: this._extensionHostLogsLocation, + autoStart: this._autoStart + }; + return r; + }); + } +} + +class WorkerExtensionHost extends Disposable implements IWorkbenchContribution { + + constructor() { + super(); + // new ExtensionHostWebWorker().start().then(protocol => { + + // }); + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + WorkerExtensionHost, + LifecyclePhase.Ready +); + diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 6a4257f81d7af..1232c5b68bf16 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -329,6 +329,4 @@ import 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution'; // Issues import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; -import 'vs/workbench/contrib/workerExtensions/browser/workerExtensions.contribution'; - //#endregion diff --git a/tslint.json b/tslint.json index f45bfed2444db..b18e616921cc5 100644 --- a/tslint.json +++ b/tslint.json @@ -461,7 +461,7 @@ "restrictions": [ "vs/nls", "vs/css!./**/*", - "**/vs/base/**/{common,browser,node,electron-browser}/**", + "**/vs/base/**/{common,browser,worker,node,electron-browser}/**", "**/vs/platform/**/{common,browser,node,electron-browser}/**", "**/vs/editor/**", "**/vs/workbench/{common,browser,node,electron-browser,api}/**", From 4aaaf3dc6aa28ed9bc15ad26175af68775a94d06 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 11 Apr 2019 10:02:18 +0200 Subject: [PATCH 007/613] move things around --- .../electron-browser/webWorkerExtensionHostStarter.ts | 3 ++- .../extensions}/worker/extensionHostWorker.ts | 0 .../extensions}/worker/extensionLoader.ts | 0 3 files changed, 2 insertions(+), 1 deletion(-) rename src/vs/workbench/{contrib/workerExtensions => services/extensions}/worker/extensionHostWorker.ts (100%) rename src/vs/workbench/{contrib/workerExtensions => services/extensions}/worker/extensionLoader.ts (100%) diff --git a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts index fc843bab3b890..e16de3ab4e1a0 100644 --- a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts @@ -53,11 +53,12 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { const emitter = new Emitter(); const worker = new DefaultWorkerFactory('WorkerExtensionHost').create( - 'vs/workbench/contrib/workerExtensions/worker/extensionHostWorker', data => { + 'vs/workbench/services/extensions/worker/extensionHostWorker', data => { if (data instanceof ArrayBuffer) { emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); } else { console.warn('UNKNOWN data received', data); + this._onDidCrashed.fire([77, 'UNKNOWN data received']); } }, err => { this._onDidCrashed.fire([81, err]); diff --git a/src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts similarity index 100% rename from src/vs/workbench/contrib/workerExtensions/worker/extensionHostWorker.ts rename to src/vs/workbench/services/extensions/worker/extensionHostWorker.ts diff --git a/src/vs/workbench/contrib/workerExtensions/worker/extensionLoader.ts b/src/vs/workbench/services/extensions/worker/extensionLoader.ts similarity index 100% rename from src/vs/workbench/contrib/workerExtensions/worker/extensionLoader.ts rename to src/vs/workbench/services/extensions/worker/extensionLoader.ts From 1038ae7f40eb32b01d4e4a077c9e6a3ecc5160e7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 11 Apr 2019 13:00:34 +0200 Subject: [PATCH 008/613] wip --- .../electron-browser/extensionService.ts | 2 +- .../webWorkerExtensionHostStarter.ts | 6 +- .../extensions/worker/extHost.api.impl.ts | 897 ++++++++++++++++++ .../worker/extHostExtensionService.ts | 721 ++++++++++++++ .../extensions/worker/extensionHostMain.ts | 163 ++++ .../extensions/worker/extensionHostWorker.ts | 43 +- tslint.json | 13 + 7 files changed, 1833 insertions(+), 12 deletions(-) create mode 100644 src/vs/workbench/services/extensions/worker/extHost.api.impl.ts create mode 100644 src/vs/workbench/services/extensions/worker/extHostExtensionService.ts create mode 100644 src/vs/workbench/services/extensions/worker/extensionHostMain.ts diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 5cfe7f0024d12..42136f9e2c9f0 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -447,7 +447,7 @@ export class ExtensionService extends Disposable implements IExtensionService { this._extensionHostProcessManagers.push(extHostProcessManager); } { - const extHostWebWorkerWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, autoStart, extensions, this._extensionHostLogsLocation); + const extHostWebWorkerWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, /* autoStart, */ extensions, this._extensionHostLogsLocation); const extHostWebWorkerManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostWebWorkerWorker, null, initialActivationEvents); extHostWebWorkerManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); extHostWebWorkerManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ target: extHostWebWorkerManager, isResponsive: responsiveState === ResponsiveState.Responsive }); }); diff --git a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts index e16de3ab4e1a0..b8bec5612c824 100644 --- a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts @@ -35,7 +35,7 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { readonly onCrashed: Event<[number, string | null]> = this._onDidCrashed.event; constructor( - private readonly _autoStart: boolean, + // private readonly _autoStart: boolean, private readonly _extensions: Promise, private readonly _extensionHostLogsLocation: URI, @ITelemetryService private readonly _telemetryService: ITelemetryService, @@ -137,11 +137,11 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { }, resolvedExtensions: [], hostExtensions: [], - extensions: extensionDescriptions, + extensions: [], // < todo@joh extensionDescriptions, telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: this._extensionHostLogsLocation, - autoStart: this._autoStart + autoStart: true// < todo@joh this._autoStart }; return r; }); diff --git a/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts b/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts new file mode 100644 index 0000000000000..50c5ed11a0d9b --- /dev/null +++ b/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts @@ -0,0 +1,897 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import * as errors from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import * as path from 'vs/base/common/path'; +import Severity from 'vs/base/common/severity'; +import { URI } from 'vs/base/common/uri'; +import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; +import { OverviewRulerLane } from 'vs/editor/common/model'; +import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; +import { score } from 'vs/editor/common/modes/languageSelector'; +import * as files from 'vs/platform/files/common/files'; +import { ExtHostContext, IInitData, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands'; +import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard'; +import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; +import { ExtHostComments } from 'vs/workbench/api/common/extHostComments'; +import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; +// import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService'; +import { ExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations'; +import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics'; +import { ExtHostDialogs } from 'vs/workbench/api/common/extHostDialogs'; +import { ExtHostDocumentContentProvider } from 'vs/workbench/api/common/extHostDocumentContentProviders'; +import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostDocumentSaveParticipant'; +import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { ExtensionActivatedByAPI } from 'vs/workbench/api/common/extHostExtensionActivator'; +import { ExtHostExtensionService } from './extHostExtensionService'; +import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem'; +import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService'; +import { ExtHostHeapService } from 'vs/workbench/api/common/extHostHeapService'; +import { ExtHostLanguageFeatures, ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures'; +import { ExtHostLanguages } from 'vs/workbench/api/common/extHostLanguages'; +import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; +import { ExtHostMessageService } from 'vs/workbench/api/common/extHostMessageService'; +import { ExtHostOutputService, PushOutputChannelFactory } from 'vs/workbench/api/common/extHostOutput'; +// import { LogOutputChannelFactory } from 'vs/workbench/api/node/extHostOutputService'; +import { ExtHostProgress } from 'vs/workbench/api/common/extHostProgress'; +import { ExtHostQuickOpen } from 'vs/workbench/api/common/extHostQuickOpen'; +import { ExtHostSCM } from 'vs/workbench/api/common/extHostSCM'; +// import { ExtHostSearch, registerEHSearchProviders } from 'vs/workbench/api/node/extHostSearch'; +import { ExtHostStatusBar } from 'vs/workbench/api/common/extHostStatusBar'; +import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; +// import { ExtHostTask } from 'vs/workbench/api/node/extHostTask'; +// import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService'; +import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors'; +import { ExtHostTreeViews } from 'vs/workbench/api/common/extHostTreeViews'; +import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; +import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; +import { ExtHostUrls } from 'vs/workbench/api/common/extHostUrls'; +import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; +import { ExtHostWindow } from 'vs/workbench/api/common/extHostWindow'; +import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; +import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier'; +import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; +import * as vscode from 'vscode'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { originalFSPath } from 'vs/base/common/resources'; +// import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer'; +import { withNullAsUndefined } from 'vs/base/common/types'; +import { values } from 'vs/base/common/collections'; +// import { Schemas } from 'vs/base/common/network'; + +export interface IExtensionApiFactory { + (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; +} + +function proposedApiFunction(extension: IExtensionDescription, fn: T): T { + if (extension.enableProposedApi) { + return fn; + } else { + return throwProposedApiError.bind(null, extension) as any as T; + } +} + +/** + * This method instantiates and returns the extension API surface + */ +export function createApiFactory( + initData: IInitData, + rpcProtocol: IMainContext, + extHostWorkspace: ExtHostWorkspace, + extHostConfiguration: ExtHostConfiguration, + extensionService: ExtHostExtensionService, + extHostLogService: ExtHostLogService, + extHostStorage: ExtHostStorage, + schemeTransformer: ISchemeTransformer | null, + outputChannelName: string +): IExtensionApiFactory { + + // Addressable instances + rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService); + const extHostHeapService = rpcProtocol.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService()); + const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, new ExtHostDecorations(rpcProtocol)); + const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol)); + const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol)); + const extHostDocumentsAndEditors = rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(rpcProtocol)); + const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors)); + const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService)); + const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadTextEditors))); + const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors)); + const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, extHostLogService)); + const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService)); + rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace); + rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration); + const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol)); + const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, schemeTransformer, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics, extHostLogService)); + const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures)); + const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors)); + const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands)); + // const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService)); + // const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands)); + const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService)); + const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments)); + // const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer, extHostLogService)); + // const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService)); + const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol)); + rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService); + const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress))); + const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, new ExtHostOutputService(PushOutputChannelFactory, initData.logsLocation, rpcProtocol)); + rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage); + // if (initData.remoteAuthority) { + // extHostTask.registerTaskSystem(Schemas.vscodeRemote, { + // scheme: Schemas.vscodeRemote, + // authority: initData.remoteAuthority, + // platform: process.platform + // }); + + // registerEHSearchProviders(extHostSearch, extHostLogService); + + // const cliServer = new CLIServer(extHostCommands); + // process.env['VSCODE_IPC_HOOK_CLI'] = cliServer.ipcHandlePath; + // } + + // todo@joh + const proxy: any = new Proxy({}, {}); + rpcProtocol.set(ExtHostContext.ExtHostTerminalService, proxy); + rpcProtocol.set(ExtHostContext.ExtHostDebugService, proxy); + rpcProtocol.set(ExtHostContext.ExtHostSearch, proxy); + rpcProtocol.set(ExtHostContext.ExtHostTask, proxy); + + // Check that no named customers are missing + const expected: ProxyIdentifier[] = values(ExtHostContext); + rpcProtocol.assertRegistered(expected); + + // Other instances + const extHostClipboard = new ExtHostClipboard(rpcProtocol); + const extHostMessageService = new ExtHostMessageService(rpcProtocol); + const extHostDialogs = new ExtHostDialogs(rpcProtocol); + const extHostStatusBar = new ExtHostStatusBar(rpcProtocol); + const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments); + + // Register an output channel for exthost log + extHostOutputService.createOutputChannelFromLogFile(outputChannelName, extHostLogService.logFile); + + // Register API-ish commands + ExtHostApiCommands.register(extHostCommands); + + return function (extension: IExtensionDescription, extensionRegistry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode { + + // Check document selectors for being overly generic. Technically this isn't a problem but + // in practice many extensions say they support `fooLang` but need fs-access to do so. Those + // extension should specify then the `file`-scheme, e.g `{ scheme: 'fooLang', language: 'fooLang' }` + // We only inform once, it is not a warning because we just want to raise awareness and because + // we cannot say if the extension is doing it right or wrong... + const checkSelector = (function () { + let done = (!extension.isUnderDevelopment); + function informOnce(selector: vscode.DocumentSelector) { + if (!done) { + console.info(`Extension '${extension.identifier.value}' uses a document selector without scheme. Learn more about this: https://go.microsoft.com/fwlink/?linkid=872305`); + done = true; + } + } + return function perform(selector: vscode.DocumentSelector): vscode.DocumentSelector { + if (Array.isArray(selector)) { + selector.forEach(perform); + } else if (typeof selector === 'string') { + informOnce(selector); + } else { + if (typeof selector.scheme === 'undefined') { + informOnce(selector); + } + if (!extension.enableProposedApi && typeof selector.exclusive === 'boolean') { + throwProposedApiError(extension); + } + } + return selector; + }; + })(); + + + // namespace: commands + const commands: typeof vscode.commands = { + registerCommand(id: string, command: (...args: any[]) => T | Thenable, thisArgs?: any): vscode.Disposable { + return extHostCommands.registerCommand(true, id, command, thisArgs); + }, + registerTextEditorCommand(id: string, callback: (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit, ...args: any[]) => void, thisArg?: any): vscode.Disposable { + return extHostCommands.registerCommand(true, id, (...args: any[]): any => { + const activeTextEditor = extHostEditors.getActiveTextEditor(); + if (!activeTextEditor) { + console.warn('Cannot execute ' + id + ' because there is no active text editor.'); + return undefined; + } + + return activeTextEditor.edit((edit: vscode.TextEditorEdit) => { + args.unshift(activeTextEditor, edit); + callback.apply(thisArg, args); + + }).then((result) => { + if (!result) { + console.warn('Edits from command ' + id + ' were not applied.'); + } + }, (err) => { + console.warn('An error occurred while running command ' + id, err); + }); + }); + }, + registerDiffInformationCommand: proposedApiFunction(extension, (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => { + return extHostCommands.registerCommand(true, id, async (...args: any[]): Promise => { + const activeTextEditor = extHostEditors.getActiveTextEditor(); + if (!activeTextEditor) { + console.warn('Cannot execute ' + id + ' because there is no active text editor.'); + return undefined; + } + + const diff = await extHostEditors.getDiffInformation(activeTextEditor.id); + callback.apply(thisArg, [diff, ...args]); + }); + }), + executeCommand(id: string, ...args: any[]): Thenable { + return extHostCommands.executeCommand(id, ...args); + }, + getCommands(filterInternal: boolean = false): Thenable { + return extHostCommands.getCommands(filterInternal); + } + }; + + // namespace: env + const env: typeof vscode.env = Object.freeze({ + get machineId() { return initData.telemetryInfo.machineId; }, + get sessionId() { return initData.telemetryInfo.sessionId; }, + get language() { return initData.environment.appLanguage; }, + get appName() { return initData.environment.appName; }, + get appRoot() { return initData.environment.appRoot!.fsPath; }, + get uriScheme() { return initData.environment.appUriScheme; }, + get logLevel() { + checkProposedApiEnabled(extension); + return typeConverters.LogLevel.to(extHostLogService.getLevel()); + }, + get onDidChangeLogLevel() { + checkProposedApiEnabled(extension); + return Event.map(extHostLogService.onDidChangeLogLevel, l => typeConverters.LogLevel.to(l)); + }, + get clipboard(): vscode.Clipboard { + return extHostClipboard; + }, + openExternal(uri: URI) { + return extHostWindow.openUri(uri); + } + }); + + // namespace: extensions + const extensions: typeof vscode.extensions = { + getExtension(extensionId: string): Extension | undefined { + const desc = extensionRegistry.getExtensionDescription(extensionId); + if (desc) { + return new Extension(extensionService, desc); + } + return undefined; + }, + get all(): Extension[] { + return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc)); + }, + get onDidChange() { + return extensionRegistry.onDidChange; + } + }; + + // namespace: languages + const languages: typeof vscode.languages = { + createDiagnosticCollection(name?: string): vscode.DiagnosticCollection { + return extHostDiagnostics.createDiagnosticCollection(name); + }, + get onDidChangeDiagnostics() { + return extHostDiagnostics.onDidChangeDiagnostics; + }, + getDiagnostics: (resource?: vscode.Uri) => { + return extHostDiagnostics.getDiagnostics(resource); + }, + getLanguages(): Thenable { + return extHostLanguages.getLanguages(); + }, + setTextDocumentLanguage(document: vscode.TextDocument, languageId: string): Thenable { + return extHostLanguages.changeLanguage(document.uri, languageId); + }, + match(selector: vscode.DocumentSelector, document: vscode.TextDocument): number { + return score(typeConverters.LanguageSelector.from(selector), document.uri, document.languageId, true); + }, + registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { + return extHostLanguageFeatures.registerCodeActionProvider(extension, checkSelector(selector), provider, metadata); + }, + registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { + return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider); + }, + registerCodeInsetProvider(selector: vscode.DocumentSelector, provider: vscode.CodeInsetProvider): vscode.Disposable { + checkProposedApiEnabled(extension); + return extHostLanguageFeatures.registerCodeInsetProvider(extension, checkSelector(selector), provider); + }, + registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { + return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider); + }, + registerDeclarationProvider(selector: vscode.DocumentSelector, provider: vscode.DeclarationProvider): vscode.Disposable { + return extHostLanguageFeatures.registerDeclarationProvider(extension, checkSelector(selector), provider); + }, + registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable { + return extHostLanguageFeatures.registerImplementationProvider(extension, checkSelector(selector), provider); + }, + registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable { + return extHostLanguageFeatures.registerTypeDefinitionProvider(extension, checkSelector(selector), provider); + }, + registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable { + return extHostLanguageFeatures.registerHoverProvider(extension, checkSelector(selector), provider, extension.identifier); + }, + registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { + return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider); + }, + registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable { + return extHostLanguageFeatures.registerReferenceProvider(extension, checkSelector(selector), provider); + }, + registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable { + return extHostLanguageFeatures.registerRenameProvider(extension, checkSelector(selector), provider); + }, + registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, metadata?: vscode.DocumentSymbolProviderMetadata): vscode.Disposable { + return extHostLanguageFeatures.registerDocumentSymbolProvider(extension, checkSelector(selector), provider, metadata); + }, + registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable { + return extHostLanguageFeatures.registerWorkspaceSymbolProvider(extension, provider); + }, + registerDocumentFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable { + return extHostLanguageFeatures.registerDocumentFormattingEditProvider(extension, checkSelector(selector), provider); + }, + registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable { + return extHostLanguageFeatures.registerDocumentRangeFormattingEditProvider(extension, checkSelector(selector), provider); + }, + registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacters: string[]): vscode.Disposable { + return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(extension, checkSelector(selector), provider, [firstTriggerCharacter].concat(moreTriggerCharacters)); + }, + registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, firstItem?: string | vscode.SignatureHelpProviderMetadata, ...remaining: string[]): vscode.Disposable { + if (typeof firstItem === 'object') { + return extHostLanguageFeatures.registerSignatureHelpProvider(extension, checkSelector(selector), provider, firstItem); + } + return extHostLanguageFeatures.registerSignatureHelpProvider(extension, checkSelector(selector), provider, typeof firstItem === 'undefined' ? [] : [firstItem, ...remaining]); + }, + registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable { + return extHostLanguageFeatures.registerCompletionItemProvider(extension, checkSelector(selector), provider, triggerCharacters); + }, + registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { + return extHostLanguageFeatures.registerDocumentLinkProvider(extension, checkSelector(selector), provider); + }, + registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { + return extHostLanguageFeatures.registerColorProvider(extension, checkSelector(selector), provider); + }, + registerFoldingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { + return extHostLanguageFeatures.registerFoldingRangeProvider(extension, checkSelector(selector), provider); + }, + registerSelectionRangeProvider(selector: vscode.DocumentSelector, provider: vscode.SelectionRangeProvider): vscode.Disposable { + return extHostLanguageFeatures.registerSelectionRangeProvider(extension, selector, provider); + }, + registerCallHierarchyProvider(selector: vscode.DocumentSelector, provider: vscode.CallHierarchyItemProvider): vscode.Disposable { + checkProposedApiEnabled(extension); + return extHostLanguageFeatures.registerCallHierarchyProvider(extension, selector, provider); + }, + setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => { + return extHostLanguageFeatures.setLanguageConfiguration(language, configuration); + } + }; + + // namespace: window + const window: typeof vscode.window = { + get activeTextEditor() { + return extHostEditors.getActiveTextEditor(); + }, + get visibleTextEditors() { + return extHostEditors.getVisibleTextEditors(); + }, + get activeTerminal(): vscode.Terminal { + throw new Error('not implemented'); + // return extHostTerminalService.activeTerminal; + }, + get terminals(): vscode.Terminal[] { + throw new Error('not implemented'); + // return extHostTerminalService.terminals; + }, + showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Thenable { + let documentPromise: Promise; + if (URI.isUri(documentOrUri)) { + documentPromise = Promise.resolve(workspace.openTextDocument(documentOrUri)); + } else { + documentPromise = Promise.resolve(documentOrUri); + } + return documentPromise.then(document => { + return extHostEditors.showTextDocument(document, columnOrOptions, preserveFocus); + }); + }, + createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType { + return extHostEditors.createTextEditorDecorationType(options); + }, + onDidChangeActiveTextEditor(listener, thisArg?, disposables?) { + return extHostEditors.onDidChangeActiveTextEditor(listener, thisArg, disposables); + }, + onDidChangeVisibleTextEditors(listener, thisArg, disposables) { + return extHostEditors.onDidChangeVisibleTextEditors(listener, thisArg, disposables); + }, + onDidChangeTextEditorSelection(listener: (e: vscode.TextEditorSelectionChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) { + return extHostEditors.onDidChangeTextEditorSelection(listener, thisArgs, disposables); + }, + onDidChangeTextEditorOptions(listener: (e: vscode.TextEditorOptionsChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) { + return extHostEditors.onDidChangeTextEditorOptions(listener, thisArgs, disposables); + }, + onDidChangeTextEditorVisibleRanges(listener: (e: vscode.TextEditorVisibleRangesChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) { + return extHostEditors.onDidChangeTextEditorVisibleRanges(listener, thisArgs, disposables); + }, + onDidChangeTextEditorViewColumn(listener, thisArg?, disposables?) { + return extHostEditors.onDidChangeTextEditorViewColumn(listener, thisArg, disposables); + }, + onDidCloseTerminal(listener, thisArg?, disposables?) { + throw new Error('not implemented'); + // return extHostTerminalService.onDidCloseTerminal(listener, thisArg, disposables); + }, + onDidOpenTerminal(listener, thisArg?, disposables?) { + throw new Error('not implemented'); + // return extHostTerminalService.onDidOpenTerminal(listener, thisArg, disposables); + }, + onDidChangeActiveTerminal(listener, thisArg?, disposables?) { + throw new Error('not implemented'); + // return extHostTerminalService.onDidChangeActiveTerminal(listener, thisArg, disposables); + }, + onDidChangeTerminalDimensions(listener, thisArg?, disposables?) { + throw new Error('not implemented'); + // return extHostTerminalService.onDidChangeTerminalDimensions(listener, thisArg, disposables); + }, + get state() { + return extHostWindow.state; + }, + onDidChangeWindowState(listener, thisArg?, disposables?) { + return extHostWindow.onDidChangeWindowState(listener, thisArg, disposables); + }, + showInformationMessage(message: string, first: vscode.MessageOptions | string | vscode.MessageItem, ...rest: Array) { + return extHostMessageService.showMessage(extension, Severity.Info, message, first, rest); + }, + showWarningMessage(message: string, first: vscode.MessageOptions | string | vscode.MessageItem, ...rest: Array) { + return extHostMessageService.showMessage(extension, Severity.Warning, message, first, rest); + }, + showErrorMessage(message: string, first: vscode.MessageOptions | string | vscode.MessageItem, ...rest: Array) { + return extHostMessageService.showMessage(extension, Severity.Error, message, first, rest); + }, + showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken): any { + return extHostQuickOpen.showQuickPick(items, !!extension.enableProposedApi, options, token); + }, + showWorkspaceFolderPick(options: vscode.WorkspaceFolderPickOptions) { + return extHostQuickOpen.showWorkspaceFolderPick(options); + }, + showInputBox(options?: vscode.InputBoxOptions, token?: vscode.CancellationToken) { + return extHostQuickOpen.showInput(options, token); + }, + showOpenDialog(options) { + return extHostDialogs.showOpenDialog(options); + }, + showSaveDialog(options) { + return extHostDialogs.showSaveDialog(options); + }, + createStatusBarItem(position?: vscode.StatusBarAlignment, priority?: number): vscode.StatusBarItem { + return extHostStatusBar.createStatusBarEntry(extension.identifier, position, priority); + }, + setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable): vscode.Disposable { + return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable); + }, + withScmProgress(task: (progress: vscode.Progress) => Thenable) { + console.warn(`[Deprecation Warning] function 'withScmProgress' is deprecated and should no longer be used. Use 'withProgress' instead.`); + return extHostProgress.withProgress(extension, { location: extHostTypes.ProgressLocation.SourceControl }, (progress, token) => task({ report(n: number) { /*noop*/ } })); + }, + withProgress(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; worked?: number }>, token: vscode.CancellationToken) => Thenable) { + return extHostProgress.withProgress(extension, options, task); + }, + createOutputChannel(name: string): vscode.OutputChannel { + return extHostOutputService.createOutputChannel(name); + }, + createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel { + return extHostWebviews.createWebviewPanel(extension, viewType, title, showOptions, options); + }, + createTerminal(nameOrOptions?: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal { + throw new Error('not implemented'); + // if (typeof nameOrOptions === 'object') { + // return extHostTerminalService.createTerminalFromOptions(nameOrOptions); + // } + // return extHostTerminalService.createTerminal(nameOrOptions, shellPath, shellArgs); + }, + createTerminalRenderer(name: string): vscode.TerminalRenderer { + throw new Error('not implemented'); + // return extHostTerminalService.createTerminalRenderer(name); + }, + registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider): vscode.Disposable { + return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension); + }, + createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider }): vscode.TreeView { + return extHostTreeViews.createTreeView(viewId, options, extension); + }, + registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => { + return extHostWebviews.registerWebviewPanelSerializer(viewType, serializer); + }, + registerDecorationProvider: proposedApiFunction(extension, (provider: vscode.DecorationProvider) => { + return extHostDecorations.registerDecorationProvider(provider, extension.identifier); + }), + registerUriHandler(handler: vscode.UriHandler) { + return extHostUrls.registerUriHandler(extension.identifier, handler); + }, + createQuickPick(): vscode.QuickPick { + return extHostQuickOpen.createQuickPick(extension.identifier, !!extension.enableProposedApi); + }, + createInputBox(): vscode.InputBox { + return extHostQuickOpen.createInputBox(extension.identifier); + } + }; + + // namespace: workspace + const workspace: typeof vscode.workspace = { + get rootPath() { + return extHostWorkspace.getPath(); + }, + set rootPath(value) { + throw errors.readonly(); + }, + getWorkspaceFolder(resource) { + return extHostWorkspace.getWorkspaceFolder(resource); + }, + get workspaceFolders() { + return extHostWorkspace.getWorkspaceFolders(); + }, + get name() { + return extHostWorkspace.name; + }, + set name(value) { + throw errors.readonly(); + }, + updateWorkspaceFolders: (index, deleteCount, ...workspaceFoldersToAdd) => { + return extHostWorkspace.updateWorkspaceFolders(extension, index, deleteCount || 0, ...workspaceFoldersToAdd); + }, + onDidChangeWorkspaceFolders: function (listener, thisArgs?, disposables?) { + return extHostWorkspace.onDidChangeWorkspace(listener, thisArgs, disposables); + }, + asRelativePath: (pathOrUri, includeWorkspace?) => { + return extHostWorkspace.getRelativePath(pathOrUri, includeWorkspace); + }, + findFiles: (include, exclude, maxResults?, token?) => { + return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(withNullAsUndefined(exclude)), maxResults, extension.identifier, token); + }, + findTextInFiles: (query: vscode.TextSearchQuery, optionsOrCallback: vscode.FindTextInFilesOptions | ((result: vscode.TextSearchResult) => void), callbackOrToken?: vscode.CancellationToken | ((result: vscode.TextSearchResult) => void), token?: vscode.CancellationToken) => { + let options: vscode.FindTextInFilesOptions; + let callback: (result: vscode.TextSearchResult) => void; + + if (typeof optionsOrCallback === 'object') { + options = optionsOrCallback; + callback = callbackOrToken as (result: vscode.TextSearchResult) => void; + } else { + options = {}; + callback = optionsOrCallback; + token = callbackOrToken as vscode.CancellationToken; + } + + return extHostWorkspace.findTextInFiles(query, options || {}, callback, extension.identifier, token); + }, + saveAll: (includeUntitled?) => { + return extHostWorkspace.saveAll(includeUntitled); + }, + applyEdit(edit: vscode.WorkspaceEdit): Thenable { + return extHostEditors.applyWorkspaceEdit(edit); + }, + createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): vscode.FileSystemWatcher => { + return extHostFileSystemEvent.createFileSystemWatcher(typeConverters.GlobPattern.from(pattern), ignoreCreate, ignoreChange, ignoreDelete); + }, + get textDocuments() { + return extHostDocuments.getAllDocumentData().map(data => data.document); + }, + set textDocuments(value) { + throw errors.readonly(); + }, + openTextDocument(uriOrFileNameOrOptions?: vscode.Uri | string | { language?: string; content?: string; }) { + let uriPromise: Thenable; + + const options = uriOrFileNameOrOptions as { language?: string; content?: string; }; + if (typeof uriOrFileNameOrOptions === 'string') { + uriPromise = Promise.resolve(URI.file(uriOrFileNameOrOptions)); + } else if (uriOrFileNameOrOptions instanceof URI) { + uriPromise = Promise.resolve(uriOrFileNameOrOptions); + } else if (!options || typeof options === 'object') { + uriPromise = extHostDocuments.createDocumentData(options); + } else { + throw new Error('illegal argument - uriOrFileNameOrOptions'); + } + + return uriPromise.then(uri => { + return extHostDocuments.ensureDocumentData(uri).then(() => { + return extHostDocuments.getDocument(uri); + }); + }); + }, + onDidOpenTextDocument: (listener, thisArgs?, disposables?) => { + return extHostDocuments.onDidAddDocument(listener, thisArgs, disposables); + }, + onDidCloseTextDocument: (listener, thisArgs?, disposables?) => { + return extHostDocuments.onDidRemoveDocument(listener, thisArgs, disposables); + }, + onDidChangeTextDocument: (listener, thisArgs?, disposables?) => { + return extHostDocuments.onDidChangeDocument(listener, thisArgs, disposables); + }, + onDidSaveTextDocument: (listener, thisArgs?, disposables?) => { + return extHostDocuments.onDidSaveDocument(listener, thisArgs, disposables); + }, + onWillSaveTextDocument: (listener, thisArgs?, disposables?) => { + return extHostDocumentSaveParticipant.getOnWillSaveTextDocumentEvent(extension)(listener, thisArgs, disposables); + }, + onDidChangeConfiguration: (listener: (_: any) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => { + return configProvider.onDidChangeConfiguration(listener, thisArgs, disposables); + }, + getConfiguration(section?: string, resource?: vscode.Uri): vscode.WorkspaceConfiguration { + resource = arguments.length === 1 ? undefined : resource; + return configProvider.getConfiguration(section, resource, extension.identifier); + }, + registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider) { + return extHostDocumentContentProviders.registerTextDocumentContentProvider(scheme, provider); + }, + registerTaskProvider: (type: string, provider: vscode.TaskProvider) => { + throw new Error('not implemented'); + // return extHostTask.registerTaskProvider(extension, provider); + }, + registerFileSystemProvider(scheme, provider, options) { + return extHostFileSystem.registerFileSystemProvider(scheme, provider, options); + }, + registerFileSearchProvider: proposedApiFunction(extension, (scheme: string, provider: vscode.FileSearchProvider) => { + throw new Error('not implemented'); + // return extHostSearch.registerFileSearchProvider(scheme, provider); + }), + registerTextSearchProvider: proposedApiFunction(extension, (scheme: string, provider: vscode.TextSearchProvider) => { + throw new Error('not implemented'); + // return extHostSearch.registerTextSearchProvider(scheme, provider); + }), + registerDocumentCommentProvider: proposedApiFunction(extension, (provider: vscode.DocumentCommentProvider) => { + return extHostComment.registerDocumentCommentProvider(extension.identifier, provider); + }), + registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => { + return extHostComment.registerWorkspaceCommentProvider(extension.identifier, provider); + }), + registerRemoteAuthorityResolver: proposedApiFunction(extension, (authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver) => { + return extensionService.registerRemoteAuthorityResolver(authorityPrefix, resolver); + }), + registerResourceLabelFormatter: proposedApiFunction(extension, (formatter: vscode.ResourceLabelFormatter) => { + return extHostFileSystem.registerResourceLabelFormatter(formatter); + }), + onDidRenameFile: proposedApiFunction(extension, (listener: (e: vscode.FileRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => { + return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables); + }), + onWillRenameFile: proposedApiFunction(extension, (listener: (e: vscode.FileWillRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => { + return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables); + }) + }; + + // namespace: scm + const scm: typeof vscode.scm = { + get inputBox() { + return extHostSCM.getLastInputBox(extension)!; // Strict null override - Deprecated api + }, + createSourceControl(id: string, label: string, rootUri?: vscode.Uri) { + return extHostSCM.createSourceControl(extension, id, label, rootUri); + } + }; + + const comment: typeof vscode.comment = { + createCommentController(id: string, label: string) { + return extHostComment.createCommentController(extension, id, label); + } + }; + + // // namespace: debug + // const debug: typeof vscode.debug = { + // get activeDebugSession() { + // return extHostDebugService.activeDebugSession; + // }, + // get activeDebugConsole() { + // return extHostDebugService.activeDebugConsole; + // }, + // get breakpoints() { + // return extHostDebugService.breakpoints; + // }, + // onDidStartDebugSession(listener, thisArg?, disposables?) { + // return extHostDebugService.onDidStartDebugSession(listener, thisArg, disposables); + // }, + // onDidTerminateDebugSession(listener, thisArg?, disposables?) { + // return extHostDebugService.onDidTerminateDebugSession(listener, thisArg, disposables); + // }, + // onDidChangeActiveDebugSession(listener, thisArg?, disposables?) { + // return extHostDebugService.onDidChangeActiveDebugSession(listener, thisArg, disposables); + // }, + // onDidReceiveDebugSessionCustomEvent(listener, thisArg?, disposables?) { + // return extHostDebugService.onDidReceiveDebugSessionCustomEvent(listener, thisArg, disposables); + // }, + // onDidChangeBreakpoints(listener, thisArgs?, disposables?) { + // return extHostDebugService.onDidChangeBreakpoints(listener, thisArgs, disposables); + // }, + // registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider) { + // return extHostDebugService.registerDebugConfigurationProvider(debugType, provider); + // }, + // registerDebugAdapterDescriptorFactory(debugType: string, factory: vscode.DebugAdapterDescriptorFactory) { + // return extHostDebugService.registerDebugAdapterDescriptorFactory(extension, debugType, factory); + // }, + // registerDebugAdapterTrackerFactory(debugType: string, factory: vscode.DebugAdapterTrackerFactory) { + // return extHostDebugService.registerDebugAdapterTrackerFactory(debugType, factory); + // }, + // startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession) { + // return extHostDebugService.startDebugging(folder, nameOrConfig, parentSession); + // }, + // addBreakpoints(breakpoints: vscode.Breakpoint[]) { + // return extHostDebugService.addBreakpoints(breakpoints); + // }, + // removeBreakpoints(breakpoints: vscode.Breakpoint[]) { + // return extHostDebugService.removeBreakpoints(breakpoints); + // } + // }; + + // const tasks: typeof vscode.tasks = { + // registerTaskProvider: (type: string, provider: vscode.TaskProvider) => { + // return extHostTask.registerTaskProvider(extension, provider); + // }, + // fetchTasks: (filter?: vscode.TaskFilter): Thenable => { + // return extHostTask.fetchTasks(filter); + // }, + // executeTask: (task: vscode.Task): Thenable => { + // return extHostTask.executeTask(extension, task); + // }, + // get taskExecutions(): vscode.TaskExecution[] { + // return extHostTask.taskExecutions; + // }, + // onDidStartTask: (listeners, thisArgs?, disposables?) => { + // return extHostTask.onDidStartTask(listeners, thisArgs, disposables); + // }, + // onDidEndTask: (listeners, thisArgs?, disposables?) => { + // return extHostTask.onDidEndTask(listeners, thisArgs, disposables); + // }, + // onDidStartTaskProcess: (listeners, thisArgs?, disposables?) => { + // return extHostTask.onDidStartTaskProcess(listeners, thisArgs, disposables); + // }, + // onDidEndTaskProcess: (listeners, thisArgs?, disposables?) => { + // return extHostTask.onDidEndTaskProcess(listeners, thisArgs, disposables); + // } + // }; + + return { + version: initData.version, + // namespaces + commands, + get debug(): typeof vscode.debug { throw new Error('not implemented'); }, + env, + extensions, + languages, + scm, + comment, + get tasks(): typeof vscode.tasks { throw new Error('not implemented'); }, + window, + workspace, + // types + Breakpoint: extHostTypes.Breakpoint, + CancellationTokenSource: CancellationTokenSource, + CodeAction: extHostTypes.CodeAction, + CodeActionKind: extHostTypes.CodeActionKind, + CodeActionTrigger: extHostTypes.CodeActionTrigger, + CodeLens: extHostTypes.CodeLens, + CodeInset: extHostTypes.CodeInset, + Color: extHostTypes.Color, + ColorInformation: extHostTypes.ColorInformation, + ColorPresentation: extHostTypes.ColorPresentation, + CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState, + CompletionItem: extHostTypes.CompletionItem, + CompletionItemKind: extHostTypes.CompletionItemKind, + CompletionList: extHostTypes.CompletionList, + CompletionTriggerKind: extHostTypes.CompletionTriggerKind, + ConfigurationTarget: extHostTypes.ConfigurationTarget, + DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable, + DebugAdapterServer: extHostTypes.DebugAdapterServer, + DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior, + Diagnostic: extHostTypes.Diagnostic, + DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation, + DiagnosticSeverity: extHostTypes.DiagnosticSeverity, + DiagnosticTag: extHostTypes.DiagnosticTag, + Disposable: extHostTypes.Disposable, + DocumentHighlight: extHostTypes.DocumentHighlight, + DocumentHighlightKind: extHostTypes.DocumentHighlightKind, + DocumentLink: extHostTypes.DocumentLink, + DocumentSymbol: extHostTypes.DocumentSymbol, + EndOfLine: extHostTypes.EndOfLine, + EventEmitter: Emitter, + CustomExecution: extHostTypes.CustomExecution, + FileChangeType: extHostTypes.FileChangeType, + FileSystemError: extHostTypes.FileSystemError, + FileType: files.FileType, + FoldingRange: extHostTypes.FoldingRange, + FoldingRangeKind: extHostTypes.FoldingRangeKind, + FunctionBreakpoint: extHostTypes.FunctionBreakpoint, + Hover: extHostTypes.Hover, + IndentAction: languageConfiguration.IndentAction, + Location: extHostTypes.Location, + LogLevel: extHostTypes.LogLevel, + MarkdownString: extHostTypes.MarkdownString, + OverviewRulerLane: OverviewRulerLane, + ParameterInformation: extHostTypes.ParameterInformation, + Position: extHostTypes.Position, + ProcessExecution: extHostTypes.ProcessExecution, + ProgressLocation: extHostTypes.ProgressLocation, + QuickInputButtons: extHostTypes.QuickInputButtons, + Range: extHostTypes.Range, + RelativePattern: extHostTypes.RelativePattern, + ResolvedAuthority: extHostTypes.ResolvedAuthority, + Selection: extHostTypes.Selection, + SelectionRange: extHostTypes.SelectionRange, + ShellExecution: extHostTypes.ShellExecution, + ShellQuoting: extHostTypes.ShellQuoting, + SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind, + SignatureHelp: extHostTypes.SignatureHelp, + SignatureInformation: extHostTypes.SignatureInformation, + SnippetString: extHostTypes.SnippetString, + SourceBreakpoint: extHostTypes.SourceBreakpoint, + SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType, + StatusBarAlignment: extHostTypes.StatusBarAlignment, + SymbolInformation: extHostTypes.SymbolInformation, + SymbolKind: extHostTypes.SymbolKind, + Task: extHostTypes.Task, + Task2: extHostTypes.Task, + TaskGroup: extHostTypes.TaskGroup, + TaskPanelKind: extHostTypes.TaskPanelKind, + TaskRevealKind: extHostTypes.TaskRevealKind, + TaskScope: extHostTypes.TaskScope, + TextDocumentSaveReason: extHostTypes.TextDocumentSaveReason, + TextEdit: extHostTypes.TextEdit, + TextEditorCursorStyle: TextEditorCursorStyle, + TextEditorLineNumbersStyle: extHostTypes.TextEditorLineNumbersStyle, + TextEditorRevealType: extHostTypes.TextEditorRevealType, + TextEditorSelectionChangeKind: extHostTypes.TextEditorSelectionChangeKind, + ThemeColor: extHostTypes.ThemeColor, + ThemeIcon: extHostTypes.ThemeIcon, + TreeItem: extHostTypes.TreeItem, + TreeItem2: extHostTypes.TreeItem, + TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState, + Uri: URI, + ViewColumn: extHostTypes.ViewColumn, + WorkspaceEdit: extHostTypes.WorkspaceEdit, + // proposed + CallHierarchyDirection: extHostTypes.CallHierarchyDirection, + CallHierarchyItem: extHostTypes.CallHierarchyItem + }; + }; +} + +class Extension implements vscode.Extension { + + private _extensionService: ExtHostExtensionService; + private _identifier: ExtensionIdentifier; + + public id: string; + public extensionPath: string; + public packageJSON: IExtensionDescription; + + constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription) { + this._extensionService = extensionService; + this._identifier = description.identifier; + this.id = description.identifier.value; + this.extensionPath = path.normalize(originalFSPath(description.extensionLocation)); + this.packageJSON = description; + } + + get isActive(): boolean { + return this._extensionService.isActivated(this._identifier); + } + + get exports(): T { + if (this.packageJSON.api === 'none') { + return undefined!; // Strict nulloverride - Public api + } + return this._extensionService.getExtensionExports(this._identifier); + } + + activate(): Thenable { + return this._extensionService.activateByIdWithErrors(this._identifier, new ExtensionActivatedByAPI(false)).then(() => this.exports); + } +} diff --git a/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts b/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts new file mode 100644 index 0000000000000..082edbd5aa2a2 --- /dev/null +++ b/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts @@ -0,0 +1,721 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as path from 'vs/base/common/path'; +import { originalFSPath } from 'vs/base/common/resources'; +import { Barrier } from 'vs/base/common/async'; +import { dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { TernarySearchTree } from 'vs/base/common/map'; +import { URI } from 'vs/base/common/uri'; +import { ILogService } from 'vs/platform/log/common/log'; +import { createApiFactory, IExtensionApiFactory } from './extHost.api.impl'; +// import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor'; +import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; +import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension } from 'vs/workbench/api/common/extHostExtensionActivator'; +import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; +import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; +import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; +import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import * as errors from 'vs/base/common/errors'; +import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import * as vscode from 'vscode'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { Schemas } from 'vs/base/common/network'; +// import { withNullAsUndefined } from 'vs/base/common/types'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures'; +import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento'; +// import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths'; + +interface ITestRunner { + run(testsRoot: string, clb: (error: Error, failures?: number) => void): void; +} + +export interface IHostUtils { + exit(code?: number): void; + exists(path: string): Promise; + realpath(path: string): Promise; +} + +export class ExtHostExtensionService implements ExtHostExtensionServiceShape { + + private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000; + + private readonly _hostUtils: IHostUtils; + private readonly _initData: IInitData; + private readonly _extHostContext: IMainContext; + private readonly _extHostWorkspace: ExtHostWorkspace; + private readonly _extHostConfiguration: ExtHostConfiguration; + // private readonly _environment: IEnvironment; + private readonly _extHostLogService: ExtHostLogService; + + private readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape; + private readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape; + private readonly _mainThreadExtensionsProxy: MainThreadExtensionServiceShape; + + private readonly _almostReadyToRunExtensions: Barrier; + private readonly _readyToRunExtensions: Barrier; + private readonly _registry: ExtensionDescriptionRegistry; + private readonly _storage: ExtHostStorage; + // private readonly _storagePath: ExtensionStoragePaths; + private readonly _activator: ExtensionsActivator; + private _extensionPathIndex: Promise> | null; + private readonly _extensionApiFactory: IExtensionApiFactory; + + private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver; }; + + private _started: boolean; + + constructor( + hostUtils: IHostUtils, + initData: IInitData, + extHostContext: IMainContext, + extHostWorkspace: ExtHostWorkspace, + extHostConfiguration: ExtHostConfiguration, + environment: IEnvironment, + extHostLogService: ExtHostLogService, + schemeTransformer: ISchemeTransformer | null, + outputChannelName: string + ) { + this._hostUtils = hostUtils; + this._initData = initData; + this._extHostContext = extHostContext; + this._extHostWorkspace = extHostWorkspace; + this._extHostConfiguration = extHostConfiguration; + // this._environment = environment; + this._extHostLogService = extHostLogService; + + this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace); + this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry); + this._mainThreadExtensionsProxy = this._extHostContext.getProxy(MainContext.MainThreadExtensionService); + + this._almostReadyToRunExtensions = new Barrier(); + this._readyToRunExtensions = new Barrier(); + this._registry = new ExtensionDescriptionRegistry(initData.extensions); + this._storage = new ExtHostStorage(this._extHostContext); + // this._storagePath = new ExtensionStoragePaths(withNullAsUndefined(initData.workspace), initData.environment); + + const hostExtensions = new Set(); + initData.hostExtensions.forEach((extensionId) => hostExtensions.add(ExtensionIdentifier.toKey(extensionId))); + + this._activator = new ExtensionsActivator(this._registry, initData.resolvedExtensions, initData.hostExtensions, { + onExtensionActivationError: (extensionId: ExtensionIdentifier, error: ExtensionActivationError): void => { + this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, error); + }, + + actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise => { + if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) { + const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); + await this._mainThreadExtensionsProxy.$activateExtension(extensionId, activationEvent); + return new HostExtension(); + } + const extensionDescription = this._registry.getExtensionDescription(extensionId)!; + return this._activateExtension(extensionDescription, reason); + } + }); + this._extensionPathIndex = null; + + // initialize API first (i.e. do not release barrier until the API is initialized) + this._extensionApiFactory = createApiFactory( + this._initData, + this._extHostContext, + this._extHostWorkspace, + this._extHostConfiguration, + this, + this._extHostLogService, + this._storage, + schemeTransformer, + outputChannelName + ); + + this._resolvers = Object.create(null); + + this._started = false; + + this._initialize(); + + if (this._initData.autoStart) { + this._startExtensionHost(); + } + } + + private async _initialize(): Promise { + try { + // const configProvider = await this._extHostConfiguration.getConfigProvider(); + // const extensionPaths = await this.getExtensionPathIndex(); + // NodeModuleRequireInterceptor.INSTANCE.register(new VSCodeNodeModuleFactory(this._extensionApiFactory, extensionPaths, this._registry, configProvider)); + // NodeModuleRequireInterceptor.INSTANCE.register(new KeytarNodeModuleFactory(this._extHostContext.getProxy(MainContext.MainThreadKeytar), this._environment)); + // if (this._initData.remoteAuthority) { + // NodeModuleRequireInterceptor.INSTANCE.register(new OpenNodeModuleFactory( + // this._extHostContext.getProxy(MainContext.MainThreadWindow), + // this._extHostContext.getProxy(MainContext.MainThreadTelemetry), + // extensionPaths + // )); + // } + + // // Do this when extension service exists, but extensions are not being activated yet. + // await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._extHostLogService, this._mainThreadTelemetryProxy); + this._almostReadyToRunExtensions.open(); + + await this._extHostWorkspace.waitForInitializeCall(); + this._readyToRunExtensions.open(); + } catch (err) { + errors.onUnexpectedError(err); + } + } + + public async deactivateAll(): Promise { + let allPromises: Promise[] = []; + try { + const allExtensions = this._registry.getAllExtensionDescriptions(); + const allExtensionsIds = allExtensions.map(ext => ext.identifier); + const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id)); + + allPromises = activatedExtensions.map((extensionId) => { + return this._deactivate(extensionId); + }); + } catch (err) { + // TODO: write to log once we have one + } + await allPromises; + } + + public isActivated(extensionId: ExtensionIdentifier): boolean { + if (this._readyToRunExtensions.isOpen()) { + return this._activator.isActivated(extensionId); + } + return false; + } + + private _activateByEvent(activationEvent: string, startup: boolean): Promise { + const reason = new ExtensionActivatedByEvent(startup, activationEvent); + return this._activator.activateByEvent(activationEvent, reason); + } + + private _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { + return this._activator.activateById(extensionId, reason); + } + + public activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { + return this._activateById(extensionId, reason).then(() => { + const extension = this._activator.getActivatedExtension(extensionId); + if (extension.activationFailed) { + // activation failed => bubble up the error as the promise result + return Promise.reject(extension.activationFailedError); + } + return undefined; + }); + } + + public getExtensionRegistry(): Promise { + return this._readyToRunExtensions.wait().then(_ => this._registry); + } + + public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined { + if (this._readyToRunExtensions.isOpen()) { + return this._activator.getActivatedExtension(extensionId).exports; + } else { + return null; + } + } + + // create trie to enable fast 'filename -> extension id' look up + public getExtensionPathIndex(): Promise> { + if (!this._extensionPathIndex) { + const tree = TernarySearchTree.forPaths(); + const extensions = this._registry.getAllExtensionDescriptions().map(ext => { + if (!ext.main) { + return undefined; + } + return this._hostUtils.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext)); + }); + this._extensionPathIndex = Promise.all(extensions).then(() => tree); + } + return this._extensionPathIndex; + } + + private _deactivate(extensionId: ExtensionIdentifier): Promise { + let result = Promise.resolve(undefined); + + if (!this._readyToRunExtensions.isOpen()) { + return result; + } + + if (!this._activator.isActivated(extensionId)) { + return result; + } + + const extension = this._activator.getActivatedExtension(extensionId); + if (!extension) { + return result; + } + + // call deactivate if available + try { + if (typeof extension.module.deactivate === 'function') { + result = Promise.resolve(extension.module.deactivate()).then(undefined, (err) => { + // TODO: Do something with err if this is not the shutdown case + return Promise.resolve(undefined); + }); + } + } catch (err) { + // TODO: Do something with err if this is not the shutdown case + } + + // clean up subscriptions + try { + dispose(extension.subscriptions); + } catch (err) { + // TODO: Do something with err if this is not the shutdown case + } + + return result; + } + + // --- impl + + private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { + this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier); + return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => { + const activationTimes = activatedExtension.activationTimes; + const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); + this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent); + this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes); + return activatedExtension; + }, (err) => { + this._logExtensionActivationTimes(extensionDescription, reason, 'failure'); + throw err; + }); + } + + private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) { + const event = getTelemetryActivationEvent(extensionDescription, reason); + /* __GDPR__ + "extensionActivationTimes" : { + "${include}": [ + "${TelemetryActivationEvent}", + "${ExtensionActivationTimes}" + ], + "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this._mainThreadTelemetryProxy.$publicLog('extensionActivationTimes', { + ...event, + ...(activationTimes || {}), + outcome, + }); + } + + private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { + const event = getTelemetryActivationEvent(extensionDescription, reason); + /* __GDPR__ + "activatePlugin" : { + "${include}": [ + "${TelemetryActivationEvent}" + ] + } + */ + this._mainThreadTelemetryProxy.$publicLog('activatePlugin', event); + if (!extensionDescription.main) { + // Treat the extension as being empty => NOT AN ERROR CASE + return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE)); + } + + this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.identifier.value} ${JSON.stringify(reason)}`); + + const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); + return Promise.all([ + loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder), + this._loadExtensionContext(extensionDescription) + ]).then(values => { + return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder); + }); + } + + private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { + + const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage); + const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage); + + this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`); + return Promise.all([ + globalState.whenReady, + workspaceState.whenReady, + // this._storagePath.whenReady + ]).then(() => { + const that = this; + return Object.freeze({ + globalState, + workspaceState, + subscriptions: [], + get extensionPath() { return extensionDescription.extensionLocation.fsPath; }, + get storagePath(): string { throw new Error('not implemented'); }, // this._storagePath.workspaceValue(extensionDescription), + get globalStoragePath(): string { throw new Error('not implemented'); }, // this._storagePath.globalValue(extensionDescription), + asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); }, + logPath: that._extHostLogService.getLogDirectory(extensionDescription.identifier) + }); + }); + } + + private static _callActivate(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + // Make sure the extension's surface is not undefined + extensionModule = extensionModule || { + activate: undefined, + deactivate: undefined + }; + + return this._callActivateOptional(logService, extensionId, extensionModule, context, activationTimesBuilder).then((extensionExports) => { + return new ActivatedExtension(false, null, activationTimesBuilder.build(), extensionModule, extensionExports, context.subscriptions); + }); + } + + private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + if (typeof extensionModule.activate === 'function') { + try { + activationTimesBuilder.activateCallStart(); + logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`); + const activateResult: Promise = extensionModule.activate.apply(global, [context]); + activationTimesBuilder.activateCallStop(); + + activationTimesBuilder.activateResolveStart(); + return Promise.resolve(activateResult).then((value) => { + activationTimesBuilder.activateResolveStop(); + return value; + }); + } catch (err) { + return Promise.reject(err); + } + } else { + // No activate found => the module is the extension's exports + return Promise.resolve(extensionModule); + } + } + + // -- eager activation + + // Handle "eager" activation extensions + private _handleEagerExtensions(): Promise { + this._activateByEvent('*', true).then(undefined, (err) => { + console.error(err); + }); + + return this._handleWorkspaceContainsEagerExtensions(this._extHostWorkspace.workspace); + } + + private _handleWorkspaceContainsEagerExtensions(workspace: IWorkspace | undefined): Promise { + if (!workspace || workspace.folders.length === 0) { + return Promise.resolve(undefined); + } + + return Promise.all( + this._registry.getAllExtensionDescriptions().map((desc) => { + return this._handleWorkspaceContainsEagerExtension(workspace, desc); + }) + ).then(() => { }); + } + + private _handleWorkspaceContainsEagerExtension(workspace: IWorkspace, desc: IExtensionDescription): Promise { + const activationEvents = desc.activationEvents; + if (!activationEvents) { + return Promise.resolve(undefined); + } + + const fileNames: string[] = []; + const globPatterns: string[] = []; + + for (const activationEvent of activationEvents) { + if (/^workspaceContains:/.test(activationEvent)) { + const fileNameOrGlob = activationEvent.substr('workspaceContains:'.length); + if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) { + globPatterns.push(fileNameOrGlob); + } else { + fileNames.push(fileNameOrGlob); + } + } + } + + if (fileNames.length === 0 && globPatterns.length === 0) { + return Promise.resolve(undefined); + } + + const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(workspace, desc.identifier, fileName))).then(() => { }); + const globPatternPromise = this._activateIfGlobPatterns(desc.identifier, globPatterns); + + return Promise.all([fileNamePromise, globPatternPromise]).then(() => { }); + } + + private async _activateIfFileName(workspace: IWorkspace, extensionId: ExtensionIdentifier, fileName: string): Promise { + + // find exact path + for (const { uri } of workspace.folders) { + if (await this._hostUtils.exists(path.join(URI.revive(uri).fsPath, fileName))) { + // the file was found + return ( + this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${fileName}`)) + .then(undefined, err => console.error(err)) + ); + } + } + + return undefined; + } + + private async _activateIfGlobPatterns(extensionId: ExtensionIdentifier, globPatterns: string[]): Promise { + this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`); + + if (globPatterns.length === 0) { + return Promise.resolve(undefined); + } + + const tokenSource = new CancellationTokenSource(); + const searchP = this._mainThreadWorkspaceProxy.$checkExists(globPatterns, tokenSource.token); + + const timer = setTimeout(async () => { + tokenSource.cancel(); + this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContainsTimeout:${globPatterns.join(',')}`)) + .then(undefined, err => console.error(err)); + }, ExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT); + + let exists: boolean = false; + try { + exists = await searchP; + } catch (err) { + if (!errors.isPromiseCanceledError(err)) { + console.error(err); + } + } + + tokenSource.dispose(); + clearTimeout(timer); + + if (exists) { + // a file was found matching one of the glob patterns + return ( + this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${globPatterns.join(',')}`)) + .then(undefined, err => console.error(err)) + ); + } + + return Promise.resolve(undefined); + } + + private _handleExtensionTests(): Promise { + return this._doHandleExtensionTests().then(undefined, error => { + console.error(error); // ensure any error message makes it onto the console + + return Promise.reject(error); + }); + } + + private _doHandleExtensionTests(): Promise { + const { extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment; + if (!(extensionDevelopmentLocationURI && extensionTestsLocationURI && extensionTestsLocationURI.scheme === Schemas.file)) { + return Promise.resolve(undefined); + } + + const extensionTestsPath = originalFSPath(extensionTestsLocationURI); + + // Require the test runner via node require from the provided path + let testRunner: ITestRunner | undefined; + let requireError: Error | undefined; + try { + testRunner = require.__$__nodeRequire(extensionTestsPath); + } catch (error) { + requireError = error; + } + + // Execute the runner if it follows our spec + if (testRunner && typeof testRunner.run === 'function') { + return new Promise((c, e) => { + testRunner!.run(extensionTestsPath, (error, failures) => { + if (error) { + e(error.toString()); + } else { + c(undefined); + } + + // after tests have run, we shutdown the host + this._gracefulExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */); + }); + }); + } + + // Otherwise make sure to shutdown anyway even in case of an error + else { + this._gracefulExit(1 /* ERROR */); + } + + return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath))); + } + + private _gracefulExit(code: number): void { + // to give the PH process a chance to flush any outstanding console + // messages to the main process, we delay the exit() by some time + setTimeout(() => { + // If extension tests are running, give the exit code to the renderer + if (this._initData.remoteAuthority && !!this._initData.environment.extensionTestsLocationURI) { + this._mainThreadExtensionsProxy.$onExtensionHostExit(code); + return; + } + + this._hostUtils.exit(code); + }, 500); + } + + private _startExtensionHost(): Promise { + if (this._started) { + throw new Error(`Extension host is already started!`); + } + this._started = true; + + return this._readyToRunExtensions.wait() + .then(() => this._handleEagerExtensions()) + .then(() => this._handleExtensionTests()) + .then(() => { + this._extHostLogService.info(`eager extensions activated`); + }); + } + + // -- called by extensions + + public registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable { + this._resolvers[authorityPrefix] = resolver; + return toDisposable(() => { + delete this._resolvers[authorityPrefix]; + }); + } + + // -- called by main thread + + public async $resolveAuthority(remoteAuthority: string): Promise { + const authorityPlusIndex = remoteAuthority.indexOf('+'); + if (authorityPlusIndex === -1) { + throw new Error(`Not an authority that can be resolved!`); + } + const authorityPrefix = remoteAuthority.substr(0, authorityPlusIndex); + + await this._almostReadyToRunExtensions.wait(); + await this._activateByEvent(`onResolveRemoteAuthority:${authorityPrefix}`, false); + + const resolver = this._resolvers[authorityPrefix]; + if (!resolver) { + throw new Error(`No resolver available for ${authorityPrefix}`); + } + + const result = await resolver.resolve(remoteAuthority); + return { + authority: remoteAuthority, + host: result.host, + port: result.port, + }; + } + + public $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise { + this._registry.keepOnly(enabledExtensionIds); + return this._startExtensionHost(); + } + + public $activateByEvent(activationEvent: string): Promise { + return ( + this._readyToRunExtensions.wait() + .then(_ => this._activateByEvent(activationEvent, false)) + ); + } + + public async $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise { + await this._readyToRunExtensions.wait(); + if (!this._registry.getExtensionDescription(extensionId)) { + // unknown extension => ignore + return false; + } + await this._activateById(extensionId, new ExtensionActivatedByEvent(false, activationEvent)); + return true; + } + + public async $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { + toAdd.forEach((extension) => (extension).extensionLocation = URI.revive(extension.extensionLocation)); + + const trie = await this.getExtensionPathIndex(); + + await Promise.all(toRemove.map(async (extensionId) => { + const extensionDescription = this._registry.getExtensionDescription(extensionId); + if (!extensionDescription) { + return; + } + const realpathValue = await this._hostUtils.realpath(extensionDescription.extensionLocation.fsPath); + trie.delete(URI.file(realpathValue).fsPath); + })); + + await Promise.all(toAdd.map(async (extensionDescription) => { + const realpathValue = await this._hostUtils.realpath(extensionDescription.extensionLocation.fsPath); + trie.set(URI.file(realpathValue).fsPath, extensionDescription); + })); + + this._registry.deltaExtensions(toAdd, toRemove); + return Promise.resolve(undefined); + } + + public async $test_latency(n: number): Promise { + return n; + } + + public async $test_up(b: VSBuffer): Promise { + return b.byteLength; + } + + public async $test_down(size: number): Promise { + return VSBuffer.wrap(Buffer.alloc(size, Math.random() % 256)); + } + +} + +function loadCommonJSModule(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + // let r: T | null = null; + // activationTimesBuilder.codeLoadingStart(); + // logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`); + // try { + // r = require.__$__nodeRequire(modulePath); + // } catch (e) { + // return Promise.reject(e); + // } finally { + // activationTimesBuilder.codeLoadingStop(); + // } + // return Promise.resolve(r); + return Promise.reject('cannot'); +} + +function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any { + const reasonStr = reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : + reason instanceof ExtensionActivatedByAPI ? 'api' : + ''; + + /* __GDPR__FRAGMENT__ + "TelemetryActivationEvent" : { + "id": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "extensionVersion": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "activationEvents": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + const event = { + id: extensionDescription.identifier.value, + name: extensionDescription.name, + extensionVersion: extensionDescription.version, + publisherDisplayName: extensionDescription.publisher, + activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null, + isBuiltin: extensionDescription.isBuiltin, + reason: reasonStr + }; + + return event; +} diff --git a/src/vs/workbench/services/extensions/worker/extensionHostMain.ts b/src/vs/workbench/services/extensions/worker/extensionHostMain.ts new file mode 100644 index 0000000000000..935d6d458f4e5 --- /dev/null +++ b/src/vs/workbench/services/extensions/worker/extensionHostMain.ts @@ -0,0 +1,163 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { timeout } from 'vs/base/common/async'; +import * as errors from 'vs/base/common/errors'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Counter } from 'vs/base/common/numbers'; +import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri'; +import { IURITransformer } from 'vs/base/common/uriIpc'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { IInitData, MainContext, MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; +import { ExtHostExtensionService, IHostUtils } from './extHostExtensionService'; +import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; +import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; +import { RPCProtocol } from 'vs/workbench/services/extensions/common/rpcProtocol'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { withNullAsUndefined } from 'vs/base/common/types'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures'; + +// we don't (yet) throw when extensions parse +// uris that have no scheme +setUriThrowOnMissingScheme(false); + +export interface IExitFn { + (code?: number): any; +} + +export interface IConsolePatchFn { + (mainThreadConsole: MainThreadConsoleShape): any; +} + +export interface ILogServiceFn { + (initData: IInitData): ILogService; +} + +export class ExtensionHostMain { + + private _isTerminating: boolean; + private readonly _hostUtils: IHostUtils; + private readonly _extensionService: ExtHostExtensionService; + private readonly _extHostLogService: ExtHostLogService; + private disposables: IDisposable[] = []; + + private _searchRequestIdProvider: Counter; + + constructor( + protocol: IMessagePassingProtocol, + initData: IInitData, + hostUtils: IHostUtils, + consolePatchFn: IConsolePatchFn, + logServiceFn: ILogServiceFn, + uriTransformer: IURITransformer | null, + schemeTransformer: ISchemeTransformer | null, + outputChannelName: string, + ) { + this._isTerminating = false; + this._hostUtils = hostUtils; + const rpcProtocol = new RPCProtocol(protocol, null, uriTransformer); + + // ensure URIs are transformed and revived + initData = this.transform(initData, rpcProtocol); + + // allow to patch console + consolePatchFn(rpcProtocol.getProxy(MainContext.MainThreadConsole)); + + // services + this._extHostLogService = new ExtHostLogService(logServiceFn(initData), initData.logsLocation.fsPath); + this.disposables.push(this._extHostLogService); + + this._searchRequestIdProvider = new Counter(); + const extHostWorkspace = new ExtHostWorkspace(rpcProtocol, this._extHostLogService, this._searchRequestIdProvider, withNullAsUndefined(initData.workspace)); + + this._extHostLogService.info('extension host started'); + this._extHostLogService.trace('initData', initData); + + const extHostConfiguraiton = new ExtHostConfiguration(rpcProtocol.getProxy(MainContext.MainThreadConfiguration), extHostWorkspace); + this._extensionService = new ExtHostExtensionService( + hostUtils, + initData, + rpcProtocol, + extHostWorkspace, + extHostConfiguraiton, + initData.environment, + this._extHostLogService, + schemeTransformer, + outputChannelName + ); + + // error forwarding and stack trace scanning + Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) + const extensionErrors = new WeakMap(); + this._extensionService.getExtensionPathIndex().then(map => { + (Error).prepareStackTrace = (error: Error, stackTrace: errors.V8CallSite[]) => { + let stackTraceMessage = ''; + let extension: IExtensionDescription | undefined; + let fileName: string; + for (const call of stackTrace) { + stackTraceMessage += `\n\tat ${call.toString()}`; + fileName = call.getFileName(); + if (!extension && fileName) { + extension = map.findSubstr(fileName); + } + + } + extensionErrors.set(error, extension); + return `${error.name || 'Error'}: ${error.message || ''}${stackTraceMessage}`; + }; + }); + + const mainThreadExtensions = rpcProtocol.getProxy(MainContext.MainThreadExtensionService); + const mainThreadErrors = rpcProtocol.getProxy(MainContext.MainThreadErrors); + errors.setUnexpectedErrorHandler(err => { + const data = errors.transformErrorForSerialization(err); + const extension = extensionErrors.get(err); + if (extension) { + mainThreadExtensions.$onExtensionRuntimeError(extension.identifier, data); + } else { + mainThreadErrors.$onUnexpectedError(data); + } + }); + } + + terminate(): void { + if (this._isTerminating) { + // we are already shutting down... + return; + } + this._isTerminating = true; + + this.disposables = dispose(this.disposables); + + errors.setUnexpectedErrorHandler((err) => { + // TODO: write to log once we have one + }); + + const extensionsDeactivated = this._extensionService.deactivateAll(); + + // Give extensions 1 second to wrap up any async dispose, then exit in at most 4 seconds + setTimeout(() => { + Promise.race([timeout(4000), extensionsDeactivated]).finally(() => this._hostUtils.exit()); + }, 1000); + } + + private transform(initData: IInitData, rpcProtocol: RPCProtocol): IInitData { + initData.extensions.forEach((ext) => (ext).extensionLocation = URI.revive(rpcProtocol.transformIncomingURIs(ext.extensionLocation))); + initData.environment.appRoot = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appRoot)); + initData.environment.appSettingsHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appSettingsHome)); + const extDevLocs = initData.environment.extensionDevelopmentLocationURI; + if (extDevLocs) { + initData.environment.extensionDevelopmentLocationURI = extDevLocs.map(url => URI.revive(rpcProtocol.transformIncomingURIs(url))); + } + initData.environment.extensionTestsLocationURI = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.extensionTestsLocationURI)); + initData.environment.globalStorageHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.globalStorageHome)); + initData.environment.userHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.userHome)); + initData.logsLocation = URI.revive(rpcProtocol.transformIncomingURIs(initData.logsLocation)); + initData.workspace = rpcProtocol.transformIncomingURIs(initData.workspace); + return initData; + } +} diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts index 0e6081d7b2168..60f61307c3f51 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts @@ -7,9 +7,11 @@ import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter } from 'vs/base/common/event'; -import { IExitFn } from 'vs/workbench/services/extensions/node/extensionHostMain'; import { isMessageOfType, MessageType, createMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; +import { IHostUtils } from 'vs/workbench/services/extensions/worker/extHostExtensionService'; +import { ExtensionHostMain } from 'vs/workbench/services/extensions/worker/extensionHostMain'; +import { ConsoleLogService } from 'vs/platform/log/common/log'; // worker-self declare namespace self { @@ -17,9 +19,21 @@ declare namespace self { } // do not allow extensions to call terminate -const nativeTerminate: IExitFn = self.close.bind(self); +const nativeClose = self.close.bind(self); self.close = () => console.trace('An extension called terminate and this was prevented'); -let onTerminate = nativeTerminate; +let onTerminate = nativeClose; + +const hostUtil = new class implements IHostUtils { + exit(code?: number | undefined): void { + nativeClose(); + } + async exists(path: string): Promise { + return true; + } + async realpath(path: string): Promise { + return path; + } +}; //todo@joh do not allow extensions to call postMessage and other globals... @@ -87,11 +101,24 @@ export function create(postMessage: (message: any, transfer?: Transferable[]) => const res = new ExtensionWorker(postMessage); connectToRenderer(res.protocol).then(data => { - console.log('INIT_DATA', data.initData); - - data.protocol.onMessage(msg => { - // console.log('SOME MSG', msg.toString()); - }); + // console.log('INIT_DATA', data.initData); + + // data.protocol.onMessage(msg => { + // // console.log('SOME MSG', msg.toString()); + // }); + + const extHostMain = new ExtensionHostMain( + data.protocol, + data.initData, + hostUtil, + () => { }, + () => new ConsoleLogService(), + null, + null, + 'Extension Host - WebWorker' + ); + + onTerminate = () => extHostMain.terminate(); }); return res; diff --git a/tslint.json b/tslint.json index b18e616921cc5..91d6bfa8f6105 100644 --- a/tslint.json +++ b/tslint.json @@ -431,6 +431,19 @@ "**/vs/workbench/services/**/common/**" ] }, + { + "target": "**/vs/workbench/services/**/worker/**", + "restrictions": [ + "vs/nls", + "**/vs/base/**/common/**", + "**/vs/platform/**/common/**", + "**/vs/editor/common/**", + "**/vs/workbench/**/common/**", + "**/vs/workbench/**/worker/**", + "**/vs/workbench/services/**/common/**", + "vscode" + ] + }, { "target": "**/vs/workbench/services/**/browser/**", "restrictions": [ From a30c25f359fcf3e8f22da22f827fa681008b5028 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 11 Apr 2019 13:04:08 +0200 Subject: [PATCH 009/613] less Buffer --- src/vs/workbench/api/node/extHostExtensionService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 643dc4899f4d9..c9f18be439ba7 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -672,7 +672,12 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } public async $test_down(size: number): Promise { - return VSBuffer.wrap(Buffer.alloc(size, Math.random() % 256)); + let buff = VSBuffer.alloc(size); + let value = Math.random() % 256; + for (let i = 0; i < size; i++) { + buff.writeUint8(value, i); + } + return buff; } } From 0f4b4fd8efa0bb879ba74589e3d450e8b8663dd7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 11 Apr 2019 14:24:22 +0200 Subject: [PATCH 010/613] use VSBuffer, not Buffer --- .../services/extensions/worker/extHostExtensionService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts b/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts index 082edbd5aa2a2..6d6424b416ea4 100644 --- a/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts +++ b/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts @@ -671,7 +671,12 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } public async $test_down(size: number): Promise { - return VSBuffer.wrap(Buffer.alloc(size, Math.random() % 256)); + let buff = VSBuffer.alloc(size); + let value = Math.random() % 256; + for (let i = 0; i < size; i++) { + buff.writeUint8(value, i); + } + return buff; } } From 79624ed7d20459de27aa296f932291fc352c1307 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 11 Apr 2019 16:11:49 +0200 Subject: [PATCH 011/613] hardcode sample to load and run --- .../electron-browser/extensionService.ts | 4 +-- .../webWorkerExtensionHostStarter.ts | 2 +- .../extensions/worker/extHost.api.impl.ts | 2 +- .../worker/extHostExtensionService.ts | 28 +++++++++---------- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 42136f9e2c9f0..181983db13296 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -440,14 +440,14 @@ export class ExtensionService extends Disposable implements IExtensionService { extensions = this.getExtensions(); } { - const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._extensionHostLogsLocation); + const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions.then(value => value.filter(desc => !ExtensionIdentifier.equals(desc.identifier, 'jrieken.helloworld'))), this._extensionHostLogsLocation); const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents); extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); extHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ target: extHostProcessManager, isResponsive: responsiveState === ResponsiveState.Responsive }); }); this._extensionHostProcessManagers.push(extHostProcessManager); } { - const extHostWebWorkerWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, /* autoStart, */ extensions, this._extensionHostLogsLocation); + const extHostWebWorkerWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, /* autoStart, */ extensions.then(value => value.filter(desc => ExtensionIdentifier.equals(desc.identifier, 'jrieken.helloworld'))), this._extensionHostLogsLocation); const extHostWebWorkerManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostWebWorkerWorker, null, initialActivationEvents); extHostWebWorkerManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); extHostWebWorkerManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ target: extHostWebWorkerManager, isResponsive: responsiveState === ResponsiveState.Responsive }); }); diff --git a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts index b8bec5612c824..96c40033e2868 100644 --- a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts @@ -137,7 +137,7 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { }, resolvedExtensions: [], hostExtensions: [], - extensions: [], // < todo@joh extensionDescriptions, + extensions: extensionDescriptions, telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: this._extensionHostLogsLocation, diff --git a/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts b/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts index 50c5ed11a0d9b..2080787d01c21 100644 --- a/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts +++ b/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts @@ -156,7 +156,7 @@ export function createApiFactory( const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments); // Register an output channel for exthost log - extHostOutputService.createOutputChannelFromLogFile(outputChannelName, extHostLogService.logFile); + // extHostOutputService.createOutputChannelFromLogFile(outputChannelName, extHostLogService.logFile); // < todo@joh // Register API-ish commands ExtHostApiCommands.register(extHostCommands); diff --git a/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts b/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts index 6d6424b416ea4..b93711499c492 100644 --- a/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts +++ b/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts @@ -19,7 +19,7 @@ import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionA import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionActivationError, nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; @@ -147,6 +147,11 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } private async _initialize(): Promise { + + // globally define the vscode module and share that + // for all extensions + define('vscode', this._extensionApiFactory(nullExtensionDescription, this._registry, await this._extHostConfiguration.getConfigProvider())); + try { // const configProvider = await this._extHostConfiguration.getConfigProvider(); // const extensionPaths = await this.getExtensionPathIndex(); @@ -381,7 +386,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { try { activationTimesBuilder.activateCallStart(); logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`); - const activateResult: Promise = extensionModule.activate.apply(global, [context]); + const activateResult: Promise = extensionModule.activate.apply(undefined, [context]); activationTimesBuilder.activateCallStop(); activationTimesBuilder.activateResolveStart(); @@ -681,19 +686,12 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } -function loadCommonJSModule(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { - // let r: T | null = null; - // activationTimesBuilder.codeLoadingStart(); - // logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`); - // try { - // r = require.__$__nodeRequire(modulePath); - // } catch (e) { - // return Promise.reject(e); - // } finally { - // activationTimesBuilder.codeLoadingStop(); - // } - // return Promise.resolve(r); - return Promise.reject('cannot'); +async function loadCommonJSModule(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + + const exports = Object.create(null); + self['exports'] = exports; + importScripts(modulePath); + return exports; } function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any { From e11f4fa777621af44333532472e61b752d71d9f8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 11 Apr 2019 16:48:54 +0200 Subject: [PATCH 012/613] more tweaks --- .../electron-browser/extensionService.ts | 8 +++++-- .../worker/extHostExtensionService.ts | 24 +++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 181983db13296..9744ed4ddb999 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -439,15 +439,19 @@ export class ExtensionService extends Disposable implements IExtensionService { autoStart = true; extensions = this.getExtensions(); } + + const workerExtension = new Set(); + workerExtension.add(ExtensionIdentifier.toKey('jrieken.helloworld')); + { - const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions.then(value => value.filter(desc => !ExtensionIdentifier.equals(desc.identifier, 'jrieken.helloworld'))), this._extensionHostLogsLocation); + const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions.then(value => value.filter(desc => !workerExtension.has(desc.identifier.value))), this._extensionHostLogsLocation); const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents); extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); extHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ target: extHostProcessManager, isResponsive: responsiveState === ResponsiveState.Responsive }); }); this._extensionHostProcessManagers.push(extHostProcessManager); } { - const extHostWebWorkerWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, /* autoStart, */ extensions.then(value => value.filter(desc => ExtensionIdentifier.equals(desc.identifier, 'jrieken.helloworld'))), this._extensionHostLogsLocation); + const extHostWebWorkerWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, /* autoStart, */ extensions.then(value => value.filter(desc => workerExtension.has(desc.identifier.value))), this._extensionHostLogsLocation); const extHostWebWorkerManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostWebWorkerWorker, null, initialActivationEvents); extHostWebWorkerManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); extHostWebWorkerManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ target: extHostWebWorkerManager, isResponsive: responsiveState === ResponsiveState.Responsive }); }); diff --git a/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts b/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts index b93711499c492..15ae9d658040d 100644 --- a/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts +++ b/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts @@ -32,6 +32,7 @@ import { Schemas } from 'vs/base/common/network'; import { VSBuffer } from 'vs/base/common/buffer'; import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures'; import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento'; +import { endsWith } from 'vs/base/common/strings'; // import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths'; interface ITestRunner { @@ -688,10 +689,25 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { async function loadCommonJSModule(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { - const exports = Object.create(null); - self['exports'] = exports; - importScripts(modulePath); - return exports; + // fake commonjs world + const module = { exports: {} }; + self['module'] = module; + self['exports'] = module.exports; + + // that's improper but might help extensions that aren't author correctly + // @ts-ignore + self['window'] = self; + + // import the single (!) script, make sure it's a JS-file + const suffix = '.js'; + if (endsWith(modulePath, suffix)) { + importScripts(modulePath); + } else { + importScripts(modulePath + suffix); + } + + // return what it exported + return module.exports as T; } function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any { From 37f1fd63caa4a64c1d63428cc50679cbf5d0c10f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Apr 2019 12:19:31 +0200 Subject: [PATCH 013/613] remove unused file --- .../extensions/common/extensionHostStarter.ts | 14 -------------- .../webWorkerExtensionHostStarter.ts | 2 +- 2 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 src/vs/workbench/services/extensions/common/extensionHostStarter.ts diff --git a/src/vs/workbench/services/extensions/common/extensionHostStarter.ts b/src/vs/workbench/services/extensions/common/extensionHostStarter.ts deleted file mode 100644 index 32551aaba2d69..0000000000000 --- a/src/vs/workbench/services/extensions/common/extensionHostStarter.ts +++ /dev/null @@ -1,14 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Event } from 'vs/base/common/event'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; - -export interface IExtensionHostStarter { - readonly onCrashed: Event<[number, string | null]>; - start(): Promise | null; - getInspectPort(): number | undefined; - dispose(): void; -} diff --git a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts index 96c40033e2868..de6f3caf5902e 100644 --- a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts @@ -11,7 +11,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; -import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensionHostStarter'; import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -24,6 +23,7 @@ import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import product from 'vs/platform/product/node/product'; import pkg from 'vs/platform/product/node/package'; +import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions'; export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { From 7f4de09bf473d12bf3fe094711f359f01b0ce482 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 12 Apr 2019 16:00:18 +0200 Subject: [PATCH 014/613] fix merge issue --- .../services/extensions/electron-browser/extensionService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 319fe22f0fb63..84cc23831bfcb 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -445,7 +445,7 @@ export class ExtensionService extends Disposable implements IExtensionService { workerExtension.add(ExtensionIdentifier.toKey('jrieken.helloworld')); { - const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._extensionHostLogsLocation); + const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions.then(value => value.filter(desc => !workerExtension.has(desc.identifier.value))), this._extensionHostLogsLocation); const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents); extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); extHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ isResponsive: responsiveState === ResponsiveState.Responsive }); }); From 7ef2209d13db572e3d3d0fb082ebe44ea6f37002 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 31 May 2019 15:17:26 +0200 Subject: [PATCH 015/613] adopt product service --- .../webWorkerExtensionHostStarter.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts index 8319c96781329..a7d616c257c82 100644 --- a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts @@ -21,9 +21,8 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import product from 'vs/platform/product/node/product'; -import pkg from 'vs/platform/product/node/package'; import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions'; +import { IProductService } from 'vs/platform/product/common/product'; export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { @@ -43,6 +42,7 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { @ILabelService private readonly _labelService: ILabelService, @ILogService private readonly _logService: ILogService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IProductService private readonly _productService: IProductService, ) { } @@ -115,15 +115,15 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { .then(([telemetryInfo, extensionDescriptions]) => { const workspace = this._contextService.getWorkspace(); const r: IInitData = { - commit: product.commit, - version: pkg.version, + commit: this._productService.commit, + version: this._productService.version, parentPid: process.pid, environment: { isExtensionDevelopmentDebug: false, // < todo@joh appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, appSettingsHome: this._environmentService.appSettingsHome ? URI.file(this._environmentService.appSettingsHome) : undefined, - appName: product.nameLong, - appUriScheme: product.urlProtocol, + appName: this._productService.nameLong, + appUriScheme: this._productService.urlProtocol, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, From c41ad1841419d7b3447b93b1b334ce93d9c0fa9d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 12 Jun 2019 14:37:12 -0700 Subject: [PATCH 016/613] Puppeteer exploration --- package.json | 1 + test/smoke/package.json | 4 +- test/smoke/src/application.ts | 8 +- .../smoke/src/areas/terminal/terminal.test.ts | 3 +- test/smoke/src/main.ts | 16 +- test/smoke/src/vscode/code.ts | 82 +++---- test/smoke/src/vscode/puppeteer-driver.d.ts | 56 +++++ test/smoke/src/vscode/puppeteer-driver.js | 202 ++++++++++++++++++ yarn.lock | 47 +++- 9 files changed, 361 insertions(+), 58 deletions(-) create mode 100644 test/smoke/src/vscode/puppeteer-driver.d.ts create mode 100644 test/smoke/src/vscode/puppeteer-driver.js diff --git a/package.json b/package.json index 299f9cd814d70..268ae97ee1f20 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "native-watchdog": "1.0.0", "node-pty": "0.9.0-beta17", "onigasm-umd": "^2.2.2", + "puppeteer": "^1.17.0", "semver": "^5.5.0", "spdlog": "^0.9.0", "sudo-prompt": "8.2.0", diff --git a/test/smoke/package.json b/test/smoke/package.json index e19de8f9de2b7..cf0ce7cde1feb 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -6,8 +6,8 @@ "postinstall": "npm run compile", "compile": "npm run copy-driver && npm run copy-driver-definition && tsc", "watch": "concurrently \"npm run watch-driver\" \"npm run watch-driver-definition\" \"tsc --watch\"", - "copy-driver": "cpx src/vscode/driver.js out/vscode", - "watch-driver": "cpx src/vscode/driver.js out/vscode -w", + "copy-driver": "cpx src/vscode/puppeteer-driver.js out/vscode", + "watch-driver": "cpx src/vscode/puppeteer-driver.js out/vscode -w", "copy-driver-definition": "node tools/copy-driver-definition.js", "watch-driver-definition": "watch \"node tools/copy-driver-definition.js\" ../../src/vs/platform/driver/node", "mocha": "mocha" diff --git a/test/smoke/src/application.ts b/test/smoke/src/application.ts index 5d76417684668..4f5b7aa2e8595 100644 --- a/test/smoke/src/application.ts +++ b/test/smoke/src/application.ts @@ -137,11 +137,11 @@ export class Application { } await this.code.waitForWindowIds(ids => ids.length > 0); - await this.code.waitForElement('.monaco-workbench'); + // await this.code.waitForElement('.monaco-workbench'); - if (this.remote) { - await this.code.waitForElement('.monaco-workbench .statusbar-item.statusbar-entry a[title="Editing on TestResolver"]'); - } + // if (this.remote) { + // await this.code.waitForElement('.monaco-workbench .statusbar-item.statusbar-entry a[title="Editing on TestResolver"]'); + // } // wait a bit, since focus might be stolen off widgets // as soon as they open (e.g. quick open) diff --git a/test/smoke/src/areas/terminal/terminal.test.ts b/test/smoke/src/areas/terminal/terminal.test.ts index d661b13a2dc56..8dc459faf6337 100644 --- a/test/smoke/src/areas/terminal/terminal.test.ts +++ b/test/smoke/src/areas/terminal/terminal.test.ts @@ -6,8 +6,9 @@ import { Application } from '../../application'; export function setup() { - describe('Terminal', () => { + describe.only('Terminal', () => { it(`opens terminal, runs 'echo' and verifies the output`, async function () { + this.timeout(60 * 5000); const app = this.app as Application; const expected = new Date().getTime().toString(); diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 21a0fe252a15c..81e5caa8ed238 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -132,13 +132,13 @@ if (testCodePath) { process.env.VSCODE_CLI = '1'; } -if (!fs.existsSync(electronPath || '')) { - fail(`Can't find Code at ${electronPath}.`); -} - -if (typeof stablePath === 'string' && !fs.existsSync(stablePath)) { - fail(`Can't find Stable Code at ${stablePath}.`); -} +// if (!fs.existsSync(electronPath || '')) { +// fail(`Can't find Code at ${electronPath}.`); +// } +console.log(stablePath); +// if (typeof stablePath === 'string' && !fs.existsSync(stablePath)) { +// fail(`Can't find Stable Code at ${stablePath}.`); +// } const userDataDir = path.join(testDataPath, 'd'); @@ -234,7 +234,7 @@ setupDataMigrationTests(stableCodePath, testDataPath); describe('Running Code', () => { before(async function () { const app = new Application(this.defaultOptions); - await app!.start(); + await app!.start(false); this.app = app; }); diff --git a/test/smoke/src/vscode/code.ts b/test/smoke/src/vscode/code.ts index 0b9c739cb69e6..804b38a6c7e05 100644 --- a/test/smoke/src/vscode/code.ts +++ b/test/smoke/src/vscode/code.ts @@ -9,44 +9,44 @@ import * as os from 'os'; import * as fs from 'fs'; import * as mkdirp from 'mkdirp'; import { tmpName } from 'tmp'; -import { IDriver, connect as connectDriver, IDisposable, IElement, Thenable } from './driver'; +import { IDriver, connect as connectDriver, IDisposable, IElement, Thenable } from './puppeteer-driver'; import { Logger } from '../logger'; import { ncp } from 'ncp'; const repoPath = path.join(__dirname, '../../../..'); -function getDevElectronPath(): string { - const buildPath = path.join(repoPath, '.build'); - const product = require(path.join(repoPath, 'product.json')); - - switch (process.platform) { - case 'darwin': - return path.join(buildPath, 'electron', `${product.nameLong}.app`, 'Contents', 'MacOS', 'Electron'); - case 'linux': - return path.join(buildPath, 'electron', `${product.applicationName}`); - case 'win32': - return path.join(buildPath, 'electron', `${product.nameShort}.exe`); - default: - throw new Error('Unsupported platform.'); - } -} - -function getBuildElectronPath(root: string): string { - switch (process.platform) { - case 'darwin': - return path.join(root, 'Contents', 'MacOS', 'Electron'); - case 'linux': { - const product = require(path.join(root, 'resources', 'app', 'product.json')); - return path.join(root, product.applicationName); - } - case 'win32': { - const product = require(path.join(root, 'resources', 'app', 'product.json')); - return path.join(root, `${product.nameShort}.exe`); - } - default: - throw new Error('Unsupported platform.'); - } -} +// function getDevElectronPath(): string { +// const buildPath = path.join(repoPath, '.build'); +// const product = require(path.join(repoPath, 'product.json')); + +// switch (process.platform) { +// case 'darwin': +// return path.join(buildPath, 'electron', `${product.nameLong}.app`, 'Contents', 'MacOS', 'Electron'); +// case 'linux': +// return path.join(buildPath, 'electron', `${product.applicationName}`); +// case 'win32': +// return path.join(buildPath, 'electron', `${product.nameShort}.exe`); +// default: +// throw new Error('Unsupported platform.'); +// } +// } + +// function getBuildElectronPath(root: string): string { +// switch (process.platform) { +// case 'darwin': +// return path.join(root, 'Contents', 'MacOS', 'Electron'); +// case 'linux': { +// const product = require(path.join(root, 'resources', 'app', 'product.json')); +// return path.join(root, product.applicationName); +// } +// case 'win32': { +// const product = require(path.join(root, 'resources', 'app', 'product.json')); +// return path.join(root, `${product.nameShort}.exe`); +// } +// default: +// throw new Error('Unsupported platform.'); +// } +// } function getDevOutPath(): string { return path.join(repoPath, 'out'); @@ -61,7 +61,7 @@ function getBuildOutPath(root: string): string { } } -async function connect(child: cp.ChildProcess, outPath: string, handlePath: string, logger: Logger): Promise { +async function connect(child: cp.ChildProcess | undefined, outPath: string, handlePath: string, logger: Logger): Promise { let errCount = 0; while (true) { @@ -69,8 +69,9 @@ async function connect(child: cp.ChildProcess, outPath: string, handlePath: stri const { client, driver } = await connectDriver(outPath, handlePath); return new Code(client, driver, logger); } catch (err) { + console.log('err', err); if (++errCount > 50) { - child.kill(); + // child.kill(); throw err; } @@ -107,7 +108,7 @@ async function createDriverHandle(): Promise { export async function spawn(options: SpawnOptions): Promise { const codePath = options.codePath; - const electronPath = codePath ? getBuildElectronPath(codePath) : getDevElectronPath(); + // const electronPath = codePath ? getBuildElectronPath(codePath) : getDevElectronPath(); const outPath = codePath ? getBuildOutPath(codePath) : getDevOutPath(); const handle = await createDriverHandle(); @@ -163,13 +164,14 @@ export async function spawn(options: SpawnOptions): Promise { args.push(...options.extraArgs); } - const spawnOptions: cp.SpawnOptions = { env }; + // const spawnOptions: cp.SpawnOptions = { env }; - const child = cp.spawn(electronPath, args, spawnOptions); + // const child = cp.spawn(electronPath, args, spawnOptions); - instances.add(child); - child.once('exit', () => instances.delete(child)); + // instances.add(child); + // child.once('exit', () => instances.delete(child)); + const child = undefined; return connect(child, outPath, handle, options.logger); } diff --git a/test/smoke/src/vscode/puppeteer-driver.d.ts b/test/smoke/src/vscode/puppeteer-driver.d.ts new file mode 100644 index 0000000000000..ee385f2b3f3d0 --- /dev/null +++ b/test/smoke/src/vscode/puppeteer-driver.d.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, + * and others. This API makes no assumption about what promise library is being used which + * enables reusing existing code without migrating to a specific promise implementation. Still, + * we recommend the use of native promises which are available in this editor. + */ +interface Thenable { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; +} + +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; +} + +export interface IDisposable { + dispose(): void; +} + +export function connect(outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }>; diff --git a/test/smoke/src/vscode/puppeteer-driver.js b/test/smoke/src/vscode/puppeteer-driver.js new file mode 100644 index 0000000000000..78aebc4597464 --- /dev/null +++ b/test/smoke/src/vscode/puppeteer-driver.js @@ -0,0 +1,202 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const puppeteer = require('puppeteer'); + +// export function connect(outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }> + +const width = 800; +const height = 600; + +function buildDriver(browser, page) { + return { + _serviceBrand: undefined, + getWindowIds: () => { + return Promise.resolve([1]); + }, + capturePage: () => Promise.result(''), + reloadWindow: (windowId) => Promise.resolve(), + exitApplication: () => browser.close(), + dispatchKeybinding: async (windowId, keybinding) => { + console.log('ctrl+p'); + await page.keyboard.down('Control'); + await page.keyboard.press('p'); + await page.keyboard.up('Control'); + await page.waitForSelector('.jkasndknjadsf'); + }, + click: (windowId, selector, xoffset, yoffset) => Promise.resolve(), + doubleClick: (windowId, selector) => Promise.resolve(), + setValue: (windowId, selector, text) => Promise.resolve(), + getTitle: (windowId) => page.title(), + isActiveElement: (windowId, selector) => { + page.evaluate(`document.querySelector('${selector}') === document.activeElement`); + }, + getElements: async (windowId, selector, recursive) => { + return await page.evaluate(` + (function() { + function convertToPixels(element, value) { + return parseFloat(value) || 0; + } + function getDimension(element, cssPropertyName, jsPropertyName) { + let computedStyle = getComputedStyle(element); + let value = '0'; + if (computedStyle) { + if (computedStyle.getPropertyValue) { + value = computedStyle.getPropertyValue(cssPropertyName); + } else { + // IE8 + value = (computedStyle).getAttribute(jsPropertyName); + } + } + return convertToPixels(element, value); + } + function getBorderLeftWidth(element) { + return getDimension(element, 'border-left-width', 'borderLeftWidth'); + } + function getBorderRightWidth(element) { + return getDimension(element, 'border-right-width', 'borderRightWidth'); + } + function getBorderTopWidth(element) { + return getDimension(element, 'border-top-width', 'borderTopWidth'); + } + function getBorderBottomWidth(element) { + return getDimension(element, 'border-bottom-width', 'borderBottomWidth'); + } + function getTopLeftOffset(element) { + // Adapted from WinJS.Utilities.getPosition + // and added borders to the mix + + let offsetParent = element.offsetParent, top = element.offsetTop, left = element.offsetLeft; + + while ((element = element.parentNode) !== null && element !== document.body && element !== document.documentElement) { + top -= element.scrollTop; + let c = getComputedStyle(element); + if (c) { + left -= c.direction !== 'rtl' ? element.scrollLeft : -element.scrollLeft; + } + + if (element === offsetParent) { + left += getBorderLeftWidth(element); + top += getBorderTopWidth(element); + top += element.offsetTop; + left += element.offsetLeft; + offsetParent = element.offsetParent; + } + } + + return { + left: left, + top: top + }; + } + function serializeElement(element, recursive) { + 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 = []; + + 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); + + return { + tagName: element.tagName, + className: element.className, + textContent: element.textContent || '', + attributes, + children, + left, + top + }; + } + + const query = document.querySelectorAll('${selector}'); + const result = []; + + for (let i = 0; i < query.length; i++) { + const element = query.item(i); + result.push(serializeElement(element, ${recursive})); + } + + return result; + })(); + `); + }, + typeInEditor: (windowId, selector, text) => Promise.resolve(), + getTerminalBuffer: async (windowId, selector) => { + return await page.evaluate(` + (function () { + const element = document.querySelector(selector); + + if (!element) { + throw new Error('Terminal not found: ${selector}''); + } + + const xterm: Terminal = element.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; + })(); + `); + }, + writeInTerminal: async (windowId, selector, text) => { + page.evaluate(` + const element = document.querySelector(selector); + + if (!element) { + throw new Error('Element not found: ${selector}'); + } + + const xterm: Terminal = element.xterm; + + if (!xterm) { + throw new Error('Xterm not found: ${selector}'); + } + + xterm._core.handler(text); + `); + } + } +} + +exports.connect = function (outPath, handle) { + return new Promise(async (c) => { + const browser = await puppeteer.launch({ + headless: false, + slowMo: 80, + args: [`--window-size=${width},${height}`] + }); + const page = (await browser.pages())[0]; + await page.setViewport({ width, height }); + await page.goto('http://127.0.0.1:8000'); + const result = { + client: { dispose: () => {} }, + driver: buildDriver(browser, page) + } + c(result); + }); +}; diff --git a/yarn.lock b/yarn.lock index d2629586c0957..0e22653e4dcc8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1729,7 +1729,7 @@ concat-stream@1.6.0, concat-stream@^1.5.2: readable-stream "^2.2.2" typedarray "^0.0.6" -concat-stream@^1.5.0, concat-stream@^1.6.0: +concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -2104,7 +2104,7 @@ debug@^3.2.6: dependencies: ms "^2.1.1" -debug@^4.0.1: +debug@^4.0.1, debug@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -3109,6 +3109,16 @@ extract-zip@^1.6.5: mkdirp "0.5.0" yauzl "2.4.1" +extract-zip@^1.6.6: + version "1.6.7" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9" + integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k= + dependencies: + concat-stream "1.6.2" + debug "2.6.9" + mkdirp "0.5.1" + yauzl "2.4.1" + extsprintf@1.3.0, extsprintf@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -5772,6 +5782,11 @@ mime@^1.4.1, mime@^1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime@^2.0.3: + version "2.4.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" + integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== + mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" @@ -7201,7 +7216,7 @@ progress@^1.1.8: resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= -progress@^2.0.0: +progress@^2.0.0, progress@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -7224,6 +7239,11 @@ proxy-addr@~2.0.2: forwarded "~0.1.2" ipaddr.js "1.5.2" +proxy-from-env@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -7298,6 +7318,20 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +puppeteer@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.17.0.tgz#371957d227a2f450fa74b78e78a2dadb2be7f14f" + integrity sha512-3EXZSximCzxuVKpIHtyec8Wm2dWZn1fc5tQi34qWfiUgubEVYHjUvr0GOJojqf3mifI6oyKnCdrGxaOI+lWReA== + dependencies: + debug "^4.1.0" + extract-zip "^1.6.6" + https-proxy-agent "^2.2.1" + mime "^2.0.3" + progress "^2.0.1" + proxy-from-env "^1.0.0" + rimraf "^2.6.1" + ws "^6.1.0" + q@^1.0.1, q@^1.1.2: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -9842,6 +9876,13 @@ ws@^3.3.3: safe-buffer "~5.1.0" ultron "~1.1.0" +ws@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + xml-name-validator@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-1.0.0.tgz#dcf82ee092322951ef8cc1ba596c9cbfd14a83f1" From 328dd156db44fcf874ead77cd96fd302de9353b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20J=C3=A4hn?= Date: Wed, 3 Jul 2019 00:43:23 +0200 Subject: [PATCH 017/613] Fix middle mouse button opening broken release notes link in browser --- src/vs/workbench/contrib/webview/browser/pre/main.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 1dc234fe681c9..1708578ee9b4d 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -188,6 +188,11 @@ return; } + // Prevent middle clicks opening a broken link in the browser + if (event.button == 1) { + event.preventDefault(); + } + let baseElement = event.view.document.getElementsByTagName('base')[0]; /** @type {any} */ let node = event.target; @@ -443,7 +448,7 @@ }); // Bubble out link clicks - newFrame.contentWindow.addEventListener('click', handleInnerClick); + newFrame.contentWindow.addEventListener('mousedown', handleInnerClick); if (host.onIframeLoaded) { host.onIframeLoaded(newFrame); From 7dd109c2dfdb8cc8b0748d4858da08403c4cc3f3 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 10 Jul 2019 15:49:06 -0700 Subject: [PATCH 018/613] feat(markdown): add render command (fixes #75612) This adds a command which renders the provided document, or the active editor if one is provided. Following the pattern of some of the preview commands, it returned `undefined` if there's no document provided and no active text editor. Otherwise, seems to work... ```ts const html = await vscode.commands.executeCommand('markdown.render'); ``` A way to render arbitrary strings in addition to documents may be useful at some point in the future. However, I didn't implement that here as that'd require some refactoring of the markdown engine. If we're interested though I could certainly give that a shot. --- .../markdown-language-features/package.json | 3 +- .../src/commands/index.ts | 1 + .../src/commands/renderDocument.ts | 30 +++++++++++++++++++ .../src/extension.ts | 1 + 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 extensions/markdown-language-features/src/commands/renderDocument.ts diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 8e841a982aa64..8a53e3fa3313a 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -24,6 +24,7 @@ "onCommand:markdown.showLockedPreviewToSide", "onCommand:markdown.showSource", "onCommand:markdown.showPreviewSecuritySelector", + "onCommand:markdown.render", "onWebviewPanel:markdown.preview" ], "contributes": { @@ -325,4 +326,4 @@ "webpack": "^4.1.0", "webpack-cli": "^2.0.10" } -} \ No newline at end of file +} diff --git a/extensions/markdown-language-features/src/commands/index.ts b/extensions/markdown-language-features/src/commands/index.ts index e8c9651ee0c76..68aff7ffcf5be 100644 --- a/extensions/markdown-language-features/src/commands/index.ts +++ b/extensions/markdown-language-features/src/commands/index.ts @@ -10,3 +10,4 @@ export { RefreshPreviewCommand } from './refreshPreview'; export { ShowPreviewSecuritySelectorCommand } from './showPreviewSecuritySelector'; export { MoveCursorToPositionCommand } from './moveCursorToPosition'; export { ToggleLockCommand } from './toggleLock'; +export { RenderDocument } from './renderDocument'; diff --git a/extensions/markdown-language-features/src/commands/renderDocument.ts b/extensions/markdown-language-features/src/commands/renderDocument.ts new file mode 100644 index 0000000000000..f041c1c4adf79 --- /dev/null +++ b/extensions/markdown-language-features/src/commands/renderDocument.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +import { Command } from '../commandManager'; +import { MarkdownEngine } from '../markdownEngine'; +import { SkinnyTextDocument } from '../tableOfContentsProvider'; + +export class RenderDocument implements Command { + public readonly id = 'markdown.render'; + + public constructor( + private readonly engine: MarkdownEngine + ) { } + + public async execute(document?: SkinnyTextDocument): Promise { + if (!document) { + if (!vscode.window.activeTextEditor) { + return; + } + + document = vscode.window.activeTextEditor.document; + } + + return this.engine.render(document); + } +} diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 3fe2c9d4cd33d..44e8873b16dda 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -83,6 +83,7 @@ function registerMarkdownCommands( commandManager.register(new commands.ShowPreviewSecuritySelectorCommand(previewSecuritySelector, previewManager)); commandManager.register(new commands.OpenDocumentLinkCommand(engine)); commandManager.register(new commands.ToggleLockCommand(previewManager)); + commandManager.register(new commands.RenderDocument(engine)); return commandManager; } From 3c42f56552ade4960c968705f424619ba05f2493 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 11 Jul 2019 11:24:28 -0700 Subject: [PATCH 019/613] fixup! render a string as well --- .../src/commands/renderDocument.ts | 2 +- .../src/markdownEngine.ts | 23 ++++++++----- .../src/test/engine.test.ts | 32 +++++++++++++++++++ 3 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 extensions/markdown-language-features/src/test/engine.test.ts diff --git a/extensions/markdown-language-features/src/commands/renderDocument.ts b/extensions/markdown-language-features/src/commands/renderDocument.ts index f041c1c4adf79..3d7a0bc29a029 100644 --- a/extensions/markdown-language-features/src/commands/renderDocument.ts +++ b/extensions/markdown-language-features/src/commands/renderDocument.ts @@ -16,7 +16,7 @@ export class RenderDocument implements Command { private readonly engine: MarkdownEngine ) { } - public async execute(document?: SkinnyTextDocument): Promise { + public async execute(document?: SkinnyTextDocument | string): Promise { if (!document) { if (!vscode.window.activeTextEditor) { return; diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 0cc75bfe823ea..0a2286e35d6ec 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -118,7 +118,7 @@ export class MarkdownEngine { return md; } - private tokenize( + private tokenizeDocument( document: SkinnyTextDocument, config: MarkdownItConfig, engine: MarkdownIt @@ -131,16 +131,23 @@ export class MarkdownEngine { this.currentDocument = document.uri; this._slugCount = new Map(); - const text = document.getText(); - const tokens = engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); + const tokens = this.tokenizeString(document.getText(), engine); this._tokenCache.update(document, config, tokens); return tokens; } - public async render(document: SkinnyTextDocument): Promise { - const config = this.getConfig(document.uri); + private tokenizeString(text: string, engine: MarkdownIt) { + return engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); + } + + public async render(document: SkinnyTextDocument | string): Promise { + const config = this.getConfig(typeof document === 'string' ? undefined : document.uri); const engine = await this.getEngine(config); - return engine.renderer.render(this.tokenize(document, config, engine), { + const tokens = typeof document === 'string' + ? this.tokenizeString(document, engine) + : this.tokenizeDocument(document, config, engine); + + return engine.renderer.render(tokens, { ...(engine as any).options, ...config }, {}); @@ -149,14 +156,14 @@ export class MarkdownEngine { public async parse(document: SkinnyTextDocument): Promise { const config = this.getConfig(document.uri); const engine = await this.getEngine(config); - return this.tokenize(document, config, engine); + return this.tokenizeDocument(document, config, engine); } public cleanCache(): void { this._tokenCache.clean(); } - private getConfig(resource: vscode.Uri): MarkdownItConfig { + private getConfig(resource?: vscode.Uri): MarkdownItConfig { const config = vscode.workspace.getConfiguration('markdown', resource); return { breaks: config.get('preview.breaks', false), diff --git a/extensions/markdown-language-features/src/test/engine.test.ts b/extensions/markdown-language-features/src/test/engine.test.ts new file mode 100644 index 0000000000000..3cd4f86693e5b --- /dev/null +++ b/extensions/markdown-language-features/src/test/engine.test.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import 'mocha'; + +import { InMemoryDocument } from './inMemoryDocument'; +import { createNewMarkdownEngine } from './engine'; + +const testFileName = vscode.Uri.file('test.md'); + +suite('markdown.engine', () => { + suite('rendering', () => { + const input = '# hello\n\nworld!'; + const output = '

hello

\n' + + '

world!

\n'; + + test('Renders a document', async () => { + const doc = new InMemoryDocument(testFileName, input); + const engine = createNewMarkdownEngine(); + assert.strictEqual(await engine.render(doc), output); + }); + + test('Renders a string', async () => { + const engine = createNewMarkdownEngine(); + assert.strictEqual(await engine.render(input), output); + }); + }); +}); From 76641f06d34e9d5f06ecda5b91ff08140028ab7e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 12 Jul 2019 12:39:53 -0700 Subject: [PATCH 020/613] Get terminal smoke tests working under puppeteer --- src/typings/xterm.d.ts | 4 +- .../driver/electron-browser/driver.ts | 2 +- test/smoke/src/application.ts | 3 +- test/smoke/src/main.ts | 24 +-- test/smoke/src/vscode/puppeteer-driver.js | 196 +++++++++++++++--- 5 files changed, 184 insertions(+), 45 deletions(-) diff --git a/src/typings/xterm.d.ts b/src/typings/xterm.d.ts index b41d12219a934..47c626a2fe52e 100644 --- a/src/typings/xterm.d.ts +++ b/src/typings/xterm.d.ts @@ -931,7 +931,9 @@ declare module 'xterm' { interface TerminalCore { debug: boolean; - handler(text: string): void; + _coreService: { + triggerDataEvent(text: string): void; + }; _onScroll: IEventEmitter; _onKey: IEventEmitter<{ key: string }>; diff --git a/src/vs/platform/driver/electron-browser/driver.ts b/src/vs/platform/driver/electron-browser/driver.ts index d6dc3f659bcf3..a956739bed7f1 100644 --- a/src/vs/platform/driver/electron-browser/driver.ts +++ b/src/vs/platform/driver/electron-browser/driver.ts @@ -206,7 +206,7 @@ class WindowDriver implements IWindowDriver { throw new Error(`Xterm not found: ${selector}`); } - xterm._core.handler(text); + xterm._core._coreService.triggerDataEvent(text); } async openDevTools(): Promise { diff --git a/test/smoke/src/application.ts b/test/smoke/src/application.ts index 40fc3097639f1..904cb7e8232c4 100644 --- a/test/smoke/src/application.ts +++ b/test/smoke/src/application.ts @@ -66,7 +66,8 @@ export class Application { async start(expectWalkthroughPart = true): Promise { await this._start(); - await this.code.waitForElement('.explorer-folders-view'); + // web doesn't show explorer? + // await this.code.waitForElement('.explorer-folders-view'); if (expectWalkthroughPart) { await this.code.waitForActiveElement(`.editor-instance[id="workbench.editor.walkThroughPart"] > div > div[tabIndex="0"]`); diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 81e5caa8ed238..c0cf9f5d15b3d 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -263,19 +263,19 @@ describe('Running Code', () => { }); } - setupDataLossTests(); - setupDataExplorerTests(); - setupDataPreferencesTests(); - setupDataSearchTests(); - setupDataCSSTests(); - setupDataEditorTests(); - setupDataDebugTests(); - setupDataGitTests(); - setupDataStatusbarTests(); - setupDataExtensionTests(); + // setupDataLossTests(); + // setupDataExplorerTests(); + // setupDataPreferencesTests(); + // setupDataSearchTests(); + // setupDataCSSTests(); + // setupDataEditorTests(); + // setupDataDebugTests(); + // setupDataGitTests(); + // setupDataStatusbarTests(); + // setupDataExtensionTests(); setupTerminalTests(); - setupDataMultirootTests(); - setupDataLocalizationTests(); + // setupDataMultirootTests(); + // setupDataLocalizationTests(); }); setupLaunchTests(); \ No newline at end of file diff --git a/test/smoke/src/vscode/puppeteer-driver.js b/test/smoke/src/vscode/puppeteer-driver.js index 78aebc4597464..80127b29b69a3 100644 --- a/test/smoke/src/vscode/puppeteer-driver.js +++ b/test/smoke/src/vscode/puppeteer-driver.js @@ -7,8 +7,14 @@ const puppeteer = require('puppeteer'); // export function connect(outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }> -const width = 800; -const height = 600; +const width = 1200; +const height = 800; + +const vscodeToPuppeteerKey = { + cmd: 'Meta', + ctrl: 'Control', + enter: 'Enter' +}; function buildDriver(browser, page) { return { @@ -20,21 +26,147 @@ function buildDriver(browser, page) { reloadWindow: (windowId) => Promise.resolve(), exitApplication: () => browser.close(), dispatchKeybinding: async (windowId, keybinding) => { - console.log('ctrl+p'); - await page.keyboard.down('Control'); - await page.keyboard.press('p'); - await page.keyboard.up('Control'); - await page.waitForSelector('.jkasndknjadsf'); + const keys = keybinding.split('+'); + const keysDown = []; + for (let i = 0; i < keys.length; i++) { + if (keys[i] in vscodeToPuppeteerKey) { + keys[i] = vscodeToPuppeteerKey[keys[i]]; + } + await page.keyboard.down(keys[i]); + keysDown.push(keys[i]); + } + while (keysDown.length > 0) { + await page.keyboard.up(keysDown.pop()); + } + }, + click: async (windowId, selector, xoffset, yoffset) => { + console.log('click'); + const { x, y } = await page.evaluate(` + (function() { + function convertToPixels(element, value) { + return parseFloat(value) || 0; + } + function getDimension(element, cssPropertyName, jsPropertyName) { + let computedStyle = getComputedStyle(element); + let value = '0'; + if (computedStyle) { + if (computedStyle.getPropertyValue) { + value = computedStyle.getPropertyValue(cssPropertyName); + } else { + // IE8 + value = (computedStyle).getAttribute(jsPropertyName); + } + } + return convertToPixels(element, value); + } + function getBorderLeftWidth(element) { + return getDimension(element, 'border-left-width', 'borderLeftWidth'); + } + function getBorderRightWidth(element) { + return getDimension(element, 'border-right-width', 'borderRightWidth'); + } + function getBorderTopWidth(element) { + return getDimension(element, 'border-top-width', 'borderTopWidth'); + } + function getBorderBottomWidth(element) { + return getDimension(element, 'border-bottom-width', 'borderBottomWidth'); + } + function getClientArea(element) { + // Try with DOM clientWidth / clientHeight + if (element !== document.body) { + return { width: element.clientWidth, height: element.clientHeight }; + } + + // Try innerWidth / innerHeight + if (window.innerWidth && window.innerHeight) { + return { width: window.innerWidth, height: window.innerHeight }; + } + + // Try with document.body.clientWidth / document.body.clientHeight + if (document.body && document.body.clientWidth && document.body.clientHeight) { + return { width: document.body.clientWidth, height: document.body.clientHeight }; + } + + // Try with document.documentElement.clientWidth / document.documentElement.clientHeight + if (document.documentElement && document.documentElement.clientWidth && document.documentElement.clientHeight) { + return { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight }; + } + + throw new Error('Unable to figure out browser width and height'); + } + function getTopLeftOffset(element) { + // Adapted from WinJS.Utilities.getPosition + // and added borders to the mix + + let offsetParent = element.offsetParent, top = element.offsetTop, left = element.offsetLeft; + + while ((element = element.parentNode) !== null && element !== document.body && element !== document.documentElement) { + top -= element.scrollTop; + let c = getComputedStyle(element); + if (c) { + left -= c.direction !== 'rtl' ? element.scrollLeft : -element.scrollLeft; + } + + if (element === offsetParent) { + left += getBorderLeftWidth(element); + top += getBorderTopWidth(element); + top += element.offsetTop; + left += element.offsetLeft; + offsetParent = element.offsetParent; + } + } + + return { + left: left, + top: top + }; + } + const element = document.querySelector('${selector}'); + + if (!element) { + throw new Error('Element not found: ${selector}'); + } + + const { left, top } = getTopLeftOffset(element); + const { width, height } = getClientArea(element); + let x, y; + + x = left + (width / 2); + y = top + (height / 2); + + x = Math.round(x); + y = Math.round(y); + + return { x, y }; + })(); + `); + await page.mouse.click(x + (xoffset ? xoffset : 0), y + (yoffset ? yoffset : 0)); }, - click: (windowId, selector, xoffset, yoffset) => Promise.resolve(), doubleClick: (windowId, selector) => Promise.resolve(), - setValue: (windowId, selector, text) => Promise.resolve(), + setValue: async (windowId, selector, text) => { + return page.evaluate(` + (function() { + const element = document.querySelector('${selector}'); + + if (!element) { + throw new Error('Element not found: ${selector}'); + } + + const inputElement = element; + inputElement.value = '${text}'; + + const event = new Event('input', { bubbles: true, cancelable: true }); + inputElement.dispatchEvent(event); + return true; + })(); + `); + }, getTitle: (windowId) => page.title(), isActiveElement: (windowId, selector) => { - page.evaluate(`document.querySelector('${selector}') === document.activeElement`); + return page.evaluate(`document.querySelector('${selector}') === document.activeElement`); }, - getElements: async (windowId, selector, recursive) => { - return await page.evaluate(` + getElements: (windowId, selector, recursive) => { + return page.evaluate(` (function() { function convertToPixels(element, value) { return parseFloat(value) || 0; @@ -138,22 +270,22 @@ function buildDriver(browser, page) { `); }, typeInEditor: (windowId, selector, text) => Promise.resolve(), - getTerminalBuffer: async (windowId, selector) => { - return await page.evaluate(` + getTerminalBuffer: (windowId, selector) => { + return page.evaluate(` (function () { - const element = document.querySelector(selector); + const element = document.querySelector('${selector}'); if (!element) { - throw new Error('Terminal not found: ${selector}''); + throw new Error('Terminal not found: ${selector}'); } - const xterm: Terminal = element.xterm; + const xterm = element.xterm; if (!xterm) { throw new Error('Xterm not found: ${selector}'); } - const lines: string[] = []; + const lines = []; for (let i = 0; i < xterm.buffer.length; i++) { lines.push(xterm.buffer.getLine(i).translateToString(true)); @@ -163,21 +295,23 @@ function buildDriver(browser, page) { })(); `); }, - writeInTerminal: async (windowId, selector, text) => { - page.evaluate(` - const element = document.querySelector(selector); + writeInTerminal: (windowId, selector, text) => { + return page.evaluate(` + (function () { + const element = document.querySelector('${selector}'); - if (!element) { - throw new Error('Element not found: ${selector}'); - } + if (!element) { + throw new Error('Element not found: ${selector}'); + } - const xterm: Terminal = element.xterm; + const xterm = element.xterm; - if (!xterm) { - throw new Error('Xterm not found: ${selector}'); - } + if (!xterm) { + throw new Error('Xterm not found: ${selector}'); + } - xterm._core.handler(text); + xterm._core._coreService.triggerDataEvent('${text}'); + })(); `); } } @@ -186,13 +320,15 @@ function buildDriver(browser, page) { exports.connect = function (outPath, handle) { return new Promise(async (c) => { const browser = await puppeteer.launch({ + // Run in Edge dev on macOS + // executablePath: '/Applications/Microsoft\ Edge\ Dev.app/Contents/MacOS/Microsoft\ Edge\ Dev', headless: false, slowMo: 80, args: [`--window-size=${width},${height}`] }); const page = (await browser.pages())[0]; await page.setViewport({ width, height }); - await page.goto('http://127.0.0.1:8000'); + await page.goto('http://127.0.0.1:9888'); const result = { client: { dispose: () => {} }, driver: buildDriver(browser, page) From f7b8079f218bf3b08f614be2a073c341256750a1 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 15 Jul 2019 11:50:15 -0700 Subject: [PATCH 021/613] Add puppeteer types and use TS in driver --- package.json | 3 +- test/smoke/src/main.ts | 24 +++---- test/smoke/src/vscode/puppeteer-driver.d.ts | 56 ---------------- ...uppeteer-driver.js => puppeteer-driver.ts} | 64 +++++++++++++++++-- yarn.lock | 7 ++ 5 files changed, 78 insertions(+), 76 deletions(-) delete mode 100644 test/smoke/src/vscode/puppeteer-driver.d.ts rename test/smoke/src/vscode/{puppeteer-driver.js => puppeteer-driver.ts} (80%) diff --git a/package.json b/package.json index 30a031ec455eb..dd5a11c2434bd 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@types/minimist": "^1.2.0", "@types/mocha": "2.2.39", "@types/node": "^10.12.12", + "@types/puppeteer": "^1.12.4", "@types/semver": "^5.5.0", "@types/sinon": "^1.16.36", "@types/webpack": "^4.4.10", @@ -156,4 +157,4 @@ "windows-mutex": "0.2.1", "windows-process-tree": "0.2.4" } -} \ No newline at end of file +} diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 945f109449fe2..c51b6817e9dce 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -14,19 +14,19 @@ import { ncp } from 'ncp'; import { Application, Quality, ApplicationOptions } from './application'; import { setup as setupDataMigrationTests } from './areas/workbench/data-migration.test'; -import { setup as setupDataLossTests } from './areas/workbench/data-loss.test'; -import { setup as setupDataExplorerTests } from './areas/explorer/explorer.test'; -import { setup as setupDataPreferencesTests } from './areas/preferences/preferences.test'; -import { setup as setupDataSearchTests } from './areas/search/search.test'; -import { setup as setupDataCSSTests } from './areas/css/css.test'; -import { setup as setupDataEditorTests } from './areas/editor/editor.test'; -import { setup as setupDataDebugTests } from './areas/debug/debug.test'; -import { setup as setupDataGitTests } from './areas/git/git.test'; -import { setup as setupDataStatusbarTests } from './areas/statusbar/statusbar.test'; -import { setup as setupDataExtensionTests } from './areas/extensions/extensions.test'; +// import { setup as setupDataLossTests } from './areas/workbench/data-loss.test'; +// import { setup as setupDataExplorerTests } from './areas/explorer/explorer.test'; +// import { setup as setupDataPreferencesTests } from './areas/preferences/preferences.test'; +// import { setup as setupDataSearchTests } from './areas/search/search.test'; +// import { setup as setupDataCSSTests } from './areas/css/css.test'; +// import { setup as setupDataEditorTests } from './areas/editor/editor.test'; +// import { setup as setupDataDebugTests } from './areas/debug/debug.test'; +// import { setup as setupDataGitTests } from './areas/git/git.test'; +// import { setup as setupDataStatusbarTests } from './areas/statusbar/statusbar.test'; +// import { setup as setupDataExtensionTests } from './areas/extensions/extensions.test'; import { setup as setupTerminalTests } from './areas/terminal/terminal.test'; -import { setup as setupDataMultirootTests } from './areas/multiroot/multiroot.test'; -import { setup as setupDataLocalizationTests } from './areas/workbench/localization.test'; +// import { setup as setupDataMultirootTests } from './areas/multiroot/multiroot.test'; +// import { setup as setupDataLocalizationTests } from './areas/workbench/localization.test'; import { setup as setupLaunchTests } from './areas/workbench/launch.test'; import { MultiLogger, Logger, ConsoleLogger, FileLogger } from './logger'; diff --git a/test/smoke/src/vscode/puppeteer-driver.d.ts b/test/smoke/src/vscode/puppeteer-driver.d.ts deleted file mode 100644 index ee385f2b3f3d0..0000000000000 --- a/test/smoke/src/vscode/puppeteer-driver.d.ts +++ /dev/null @@ -1,56 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/** - * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, - * and others. This API makes no assumption about what promise library is being used which - * enables reusing existing code without migrating to a specific promise implementation. Still, - * we recommend the use of native promises which are available in this editor. - */ -interface Thenable { - /** - * Attaches callbacks for the resolution and/or rejection of the Promise. - * @param onfulfilled The callback to execute when the Promise is resolved. - * @param onrejected The callback to execute when the Promise is rejected. - * @returns A Promise for the completion of which ever callback is executed. - */ - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; -} - -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; -} - -export interface IDisposable { - dispose(): void; -} - -export function connect(outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }>; diff --git a/test/smoke/src/vscode/puppeteer-driver.js b/test/smoke/src/vscode/puppeteer-driver.ts similarity index 80% rename from test/smoke/src/vscode/puppeteer-driver.js rename to test/smoke/src/vscode/puppeteer-driver.ts index 80127b29b69a3..927af49964b06 100644 --- a/test/smoke/src/vscode/puppeteer-driver.js +++ b/test/smoke/src/vscode/puppeteer-driver.ts @@ -22,12 +22,12 @@ function buildDriver(browser, page) { getWindowIds: () => { return Promise.resolve([1]); }, - capturePage: () => Promise.result(''), + capturePage: () => Promise.resolve(''), reloadWindow: (windowId) => Promise.resolve(), exitApplication: () => browser.close(), dispatchKeybinding: async (windowId, keybinding) => { const keys = keybinding.split('+'); - const keysDown = []; + const keysDown: string[] = []; for (let i = 0; i < keys.length; i++) { if (keys[i] in vscodeToPuppeteerKey) { keys[i] = vscodeToPuppeteerKey[keys[i]]; @@ -314,10 +314,10 @@ function buildDriver(browser, page) { })(); `); } - } + }; } -exports.connect = function (outPath, handle) { +export function connect(outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }> { return new Promise(async (c) => { const browser = await puppeteer.launch({ // Run in Edge dev on macOS @@ -330,9 +330,59 @@ exports.connect = function (outPath, handle) { await page.setViewport({ width, height }); await page.goto('http://127.0.0.1:9888'); const result = { - client: { dispose: () => {} }, + client: { dispose: () => { } }, driver: buildDriver(browser, page) - } + }; c(result); }); -}; +} + +/** + * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, + * and others. This API makes no assumption about what promise library is being used which + * enables reusing existing code without migrating to a specific promise implementation. Still, + * we recommend the use of native promises which are available in this editor. + */ +export interface Thenable { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; + then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; +} + +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; +} + +export interface IDisposable { + dispose(): void; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 4c46dae4c823b..efa24d5eb3a72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -65,6 +65,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.12.tgz#e15a9d034d9210f00320ef718a50c4a799417c47" integrity sha512-Pr+6JRiKkfsFvmU/LK68oBRCQeEg36TyAbPhc2xpez24OOZZCuoIhWGTd39VZy6nGafSbxzGouFPTFD/rR1A0A== +"@types/puppeteer@^1.12.4": + version "1.12.4" + resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-1.12.4.tgz#8388efdb0b30a54a7e7c4831ca0d709191d77ff1" + integrity sha512-aaGbJaJ9TuF9vZfTeoh876sBa+rYJWPwtsmHmYr28pGr42ewJnkDTq2aeSKEmS39SqUdkwLj73y/d7rBSp7mDQ== + dependencies: + "@types/node" "*" + "@types/semver@^5.4.0", "@types/semver@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" From 2eb4b1aebdcc9d1c1c19d9bcc6e8deb0292fdb13 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 16 Jul 2019 14:09:12 -0700 Subject: [PATCH 022/613] Improve types --- test/smoke/src/vscode/puppeteer-driver.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/smoke/src/vscode/puppeteer-driver.ts b/test/smoke/src/vscode/puppeteer-driver.ts index 927af49964b06..f864aede94bed 100644 --- a/test/smoke/src/vscode/puppeteer-driver.ts +++ b/test/smoke/src/vscode/puppeteer-driver.ts @@ -3,9 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -const puppeteer = require('puppeteer'); - -// export function connect(outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }> +import * as puppeteer from 'puppeteer'; const width = 1200; const height = 800; @@ -16,7 +14,7 @@ const vscodeToPuppeteerKey = { enter: 'Enter' }; -function buildDriver(browser, page) { +function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver { return { _serviceBrand: undefined, getWindowIds: () => { @@ -36,11 +34,10 @@ function buildDriver(browser, page) { keysDown.push(keys[i]); } while (keysDown.length > 0) { - await page.keyboard.up(keysDown.pop()); + await page.keyboard.up(keysDown.pop()!); } }, click: async (windowId, selector, xoffset, yoffset) => { - console.log('click'); const { x, y } = await page.evaluate(` (function() { function convertToPixels(element, value) { From 8d20c171fce04be00a26a880643106160e015355 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 19 Jul 2019 17:12:11 -0700 Subject: [PATCH 023/613] Bring back non-web smoke tests --- test/smoke/package.json | 4 +- .../smoke/src/areas/terminal/terminal.test.ts | 2 +- test/smoke/src/main.ts | 72 +++++++------ test/smoke/src/vscode/code.ts | 100 ++++++++++-------- test/smoke/src/vscode/puppeteer-driver.ts | 10 +- 5 files changed, 107 insertions(+), 81 deletions(-) diff --git a/test/smoke/package.json b/test/smoke/package.json index 188a2e082537c..2d92a7a88464f 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -6,8 +6,8 @@ "postinstall": "npm run compile", "compile": "npm run copy-driver && npm run copy-driver-definition && tsc", "watch": "concurrently \"npm run watch-driver\" \"npm run watch-driver-definition\" \"tsc --watch\"", - "copy-driver": "cpx src/vscode/puppeteer-driver.js out/vscode", - "watch-driver": "cpx src/vscode/puppeteer-driver.js out/vscode -w", + "copy-driver": "cpx src/vscode/driver.js out/vscode", + "watch-driver": "cpx src/vscode/driver.js out/vscode -w", "copy-driver-definition": "node tools/copy-driver-definition.js", "watch-driver-definition": "watch \"node tools/copy-driver-definition.js\" ../../src/vs/platform/driver/node", "mocha": "mocha" diff --git a/test/smoke/src/areas/terminal/terminal.test.ts b/test/smoke/src/areas/terminal/terminal.test.ts index 8dc459faf6337..46bd8d997b359 100644 --- a/test/smoke/src/areas/terminal/terminal.test.ts +++ b/test/smoke/src/areas/terminal/terminal.test.ts @@ -6,7 +6,7 @@ import { Application } from '../../application'; export function setup() { - describe.only('Terminal', () => { + describe('Terminal', () => { it(`opens terminal, runs 'echo' and verifies the output`, async function () { this.timeout(60 * 5000); const app = this.app as Application; diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index c51b6817e9dce..85695d96dcc0a 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -14,19 +14,19 @@ import { ncp } from 'ncp'; import { Application, Quality, ApplicationOptions } from './application'; import { setup as setupDataMigrationTests } from './areas/workbench/data-migration.test'; -// import { setup as setupDataLossTests } from './areas/workbench/data-loss.test'; -// import { setup as setupDataExplorerTests } from './areas/explorer/explorer.test'; -// import { setup as setupDataPreferencesTests } from './areas/preferences/preferences.test'; -// import { setup as setupDataSearchTests } from './areas/search/search.test'; -// import { setup as setupDataCSSTests } from './areas/css/css.test'; -// import { setup as setupDataEditorTests } from './areas/editor/editor.test'; -// import { setup as setupDataDebugTests } from './areas/debug/debug.test'; -// import { setup as setupDataGitTests } from './areas/git/git.test'; -// import { setup as setupDataStatusbarTests } from './areas/statusbar/statusbar.test'; -// import { setup as setupDataExtensionTests } from './areas/extensions/extensions.test'; +import { setup as setupDataLossTests } from './areas/workbench/data-loss.test'; +import { setup as setupDataExplorerTests } from './areas/explorer/explorer.test'; +import { setup as setupDataPreferencesTests } from './areas/preferences/preferences.test'; +import { setup as setupDataSearchTests } from './areas/search/search.test'; +import { setup as setupDataCSSTests } from './areas/css/css.test'; +import { setup as setupDataEditorTests } from './areas/editor/editor.test'; +import { setup as setupDataDebugTests } from './areas/debug/debug.test'; +import { setup as setupDataGitTests } from './areas/git/git.test'; +import { setup as setupDataStatusbarTests } from './areas/statusbar/statusbar.test'; +import { setup as setupDataExtensionTests } from './areas/extensions/extensions.test'; import { setup as setupTerminalTests } from './areas/terminal/terminal.test'; -// import { setup as setupDataMultirootTests } from './areas/multiroot/multiroot.test'; -// import { setup as setupDataLocalizationTests } from './areas/workbench/localization.test'; +import { setup as setupDataMultirootTests } from './areas/multiroot/multiroot.test'; +import { setup as setupDataLocalizationTests } from './areas/workbench/localization.test'; import { setup as setupLaunchTests } from './areas/workbench/launch.test'; import { MultiLogger, Logger, ConsoleLogger, FileLogger } from './logger'; @@ -51,7 +51,8 @@ const opts = minimist(args, { ], boolean: [ 'verbose', - 'remote' + 'remote', + 'web' ], default: { verbose: false @@ -132,13 +133,13 @@ if (testCodePath) { process.env.VSCODE_CLI = '1'; } -// if (!fs.existsSync(electronPath || '')) { -// fail(`Can't find Code at ${electronPath}.`); -// } -console.log(stablePath); -// if (typeof stablePath === 'string' && !fs.existsSync(stablePath)) { -// fail(`Can't find Stable Code at ${stablePath}.`); -// } +if (!fs.existsSync(electronPath || '')) { + fail(`Can't find Code at ${electronPath}.`); +} + +if (typeof stablePath === 'string' && !fs.existsSync(stablePath)) { + fail(`Can't find Stable Code at ${stablePath}.`); +} const userDataDir = path.join(testDataPath, 'd'); @@ -239,7 +240,7 @@ setupDataMigrationTests(stableCodePath, testDataPath); describe('Running Code', () => { before(async function () { const app = new Application(this.defaultOptions); - await app!.start(false); + await app!.start(opts.web ? false : undefined); this.app = app; }); @@ -268,19 +269,24 @@ describe('Running Code', () => { }); } - // setupDataLossTests(); - // setupDataExplorerTests(); - // setupDataPreferencesTests(); - // setupDataSearchTests(); - // setupDataCSSTests(); - // setupDataEditorTests(); - // setupDataDebugTests(); - // setupDataGitTests(); - // setupDataStatusbarTests(); - // setupDataExtensionTests(); + if (opts.web) { + setupTerminalTests(); + return; + } + + setupDataLossTests(); + setupDataExplorerTests(); + setupDataPreferencesTests(); + setupDataSearchTests(); + setupDataCSSTests(); + setupDataEditorTests(); + setupDataDebugTests(); + setupDataGitTests(); + setupDataStatusbarTests(); + setupDataExtensionTests(); setupTerminalTests(); - // setupDataMultirootTests(); - // setupDataLocalizationTests(); + setupDataMultirootTests(); + setupDataLocalizationTests(); }); setupLaunchTests(); \ No newline at end of file diff --git a/test/smoke/src/vscode/code.ts b/test/smoke/src/vscode/code.ts index 4ccf2307d32d4..0c493ec65f171 100644 --- a/test/smoke/src/vscode/code.ts +++ b/test/smoke/src/vscode/code.ts @@ -9,45 +9,46 @@ import * as os from 'os'; import * as fs from 'fs'; import * as mkdirp from 'mkdirp'; import { tmpName } from 'tmp'; -import { IDriver, connect as connectDriver, IDisposable, IElement, Thenable } from './puppeteer-driver'; +import { IDriver, connect as connectElectronDriver, IDisposable, IElement, Thenable } from './driver'; +import { connect as connectPuppeteerDriver, launch } from './puppeteer-driver'; import { Logger } from '../logger'; import { ncp } from 'ncp'; import { URI } from 'vscode-uri'; const repoPath = path.join(__dirname, '../../../..'); -// function getDevElectronPath(): string { -// const buildPath = path.join(repoPath, '.build'); -// const product = require(path.join(repoPath, 'product.json')); - -// switch (process.platform) { -// case 'darwin': -// return path.join(buildPath, 'electron', `${product.nameLong}.app`, 'Contents', 'MacOS', 'Electron'); -// case 'linux': -// return path.join(buildPath, 'electron', `${product.applicationName}`); -// case 'win32': -// return path.join(buildPath, 'electron', `${product.nameShort}.exe`); -// default: -// throw new Error('Unsupported platform.'); -// } -// } - -// function getBuildElectronPath(root: string): string { -// switch (process.platform) { -// case 'darwin': -// return path.join(root, 'Contents', 'MacOS', 'Electron'); -// case 'linux': { -// const product = require(path.join(root, 'resources', 'app', 'product.json')); -// return path.join(root, product.applicationName); -// } -// case 'win32': { -// const product = require(path.join(root, 'resources', 'app', 'product.json')); -// return path.join(root, `${product.nameShort}.exe`); -// } -// default: -// throw new Error('Unsupported platform.'); -// } -// } +function getDevElectronPath(): string { + const buildPath = path.join(repoPath, '.build'); + const product = require(path.join(repoPath, 'product.json')); + + switch (process.platform) { + case 'darwin': + return path.join(buildPath, 'electron', `${product.nameLong}.app`, 'Contents', 'MacOS', 'Electron'); + case 'linux': + return path.join(buildPath, 'electron', `${product.applicationName}`); + case 'win32': + return path.join(buildPath, 'electron', `${product.nameShort}.exe`); + default: + throw new Error('Unsupported platform.'); + } +} + +function getBuildElectronPath(root: string): string { + switch (process.platform) { + case 'darwin': + return path.join(root, 'Contents', 'MacOS', 'Electron'); + case 'linux': { + const product = require(path.join(root, 'resources', 'app', 'product.json')); + return path.join(root, product.applicationName); + } + case 'win32': { + const product = require(path.join(root, 'resources', 'app', 'product.json')); + return path.join(root, `${product.nameShort}.exe`); + } + default: + throw new Error('Unsupported platform.'); + } +} function getDevOutPath(): string { return path.join(repoPath, 'out'); @@ -62,7 +63,7 @@ function getBuildOutPath(root: string): string { } } -async function connect(child: cp.ChildProcess | undefined, outPath: string, handlePath: string, logger: Logger): Promise { +async function connect(connectDriver: typeof connectElectronDriver, child: cp.ChildProcess | undefined, outPath: string, handlePath: string, logger: Logger): Promise { let errCount = 0; while (true) { @@ -95,7 +96,10 @@ export interface SpawnOptions { verbose?: boolean; extraArgs?: string[]; log?: string; + /** Run in the test resolver */ remote?: boolean; + /** Run in the web */ + web?: boolean; } async function createDriverHandle(): Promise { @@ -109,7 +113,7 @@ async function createDriverHandle(): Promise { export async function spawn(options: SpawnOptions): Promise { const codePath = options.codePath; - // const electronPath = codePath ? getBuildElectronPath(codePath) : getDevElectronPath(); + const electronPath = codePath ? getBuildElectronPath(codePath) : getDevElectronPath(); const outPath = codePath ? getBuildOutPath(codePath) : getDevOutPath(); const handle = await createDriverHandle(); @@ -162,15 +166,21 @@ export async function spawn(options: SpawnOptions): Promise { args.push(...options.extraArgs); } - // const spawnOptions: cp.SpawnOptions = { env }; + let child: cp.ChildProcess | undefined; + let connectDriver: typeof connectElectronDriver; - // const child = cp.spawn(electronPath, args, spawnOptions); - - // instances.add(child); - // child.once('exit', () => instances.delete(child)); + if (options.web) { + launch(args); + connectDriver = connectPuppeteerDriver; + } else { + const spawnOptions: cp.SpawnOptions = { env }; + child = cp.spawn(electronPath, args, spawnOptions); + instances.add(child); + child.once('exit', () => instances.delete(child!)); + connectDriver = connectElectronDriver; + } - const child = undefined; - return connect(child, outPath, handle, options.logger); + return connect(connectDriver, child, outPath, handle, options.logger); } async function poll( @@ -215,7 +225,7 @@ export class Code { private driver: IDriver; constructor( - private client: IDisposable, + private client: IDisposable | undefined, driver: IDriver, readonly logger: Logger ) { @@ -331,7 +341,9 @@ export class Code { } dispose(): void { - this.client.dispose(); + if (this.client) { + this.client.dispose(); + } } } diff --git a/test/smoke/src/vscode/puppeteer-driver.ts b/test/smoke/src/vscode/puppeteer-driver.ts index f864aede94bed..7e1ca48ab30ad 100644 --- a/test/smoke/src/vscode/puppeteer-driver.ts +++ b/test/smoke/src/vscode/puppeteer-driver.ts @@ -314,6 +314,14 @@ function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver }; } +let args; + +export function launch(_args): void { + args = _args; + // TODO: Move puppeteer launch here + console.log(args); +} + export function connect(outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }> { return new Promise(async (c) => { const browser = await puppeteer.launch({ @@ -325,7 +333,7 @@ export function connect(outPath: string, handle: string): Promise<{ client: IDis }); const page = (await browser.pages())[0]; await page.setViewport({ width, height }); - await page.goto('http://127.0.0.1:9888'); + await page.goto(`http://127.0.0.1:9888?folder=${args[1]}`); const result = { client: { dispose: () => { } }, driver: buildDriver(browser, page) From 88c8745851393fbd99e0956c9e9aed7108c7c887 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 19 Jul 2019 17:16:04 -0700 Subject: [PATCH 024/613] Bring more non-web smoke test code back --- test/smoke/src/application.ts | 13 +++++++------ test/smoke/src/areas/terminal/terminal.test.ts | 1 - test/smoke/src/vscode/code.ts | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/test/smoke/src/application.ts b/test/smoke/src/application.ts index 904cb7e8232c4..7a5b364b16cff 100644 --- a/test/smoke/src/application.ts +++ b/test/smoke/src/application.ts @@ -66,8 +66,8 @@ export class Application { async start(expectWalkthroughPart = true): Promise { await this._start(); - // web doesn't show explorer? - // await this.code.waitForElement('.explorer-folders-view'); + // TODO: web doesn't show explorer? + await this.code.waitForElement('.explorer-folders-view'); if (expectWalkthroughPart) { await this.code.waitForActiveElement(`.editor-instance[id="workbench.editor.walkThroughPart"] > div > div[tabIndex="0"]`); @@ -138,11 +138,12 @@ export class Application { } await this.code.waitForWindowIds(ids => ids.length > 0); - // await this.code.waitForElement('.monaco-workbench'); + // TODO: Remove on web? + await this.code.waitForElement('.monaco-workbench'); - // if (this.remote) { - // await this.code.waitForElement('.monaco-workbench .statusbar-item[title="Editing on TestResolver"]'); - // } + if (this.remote) { + await this.code.waitForElement('.monaco-workbench .statusbar-item[title="Editing on TestResolver"]'); + } // wait a bit, since focus might be stolen off widgets // as soon as they open (e.g. quick open) diff --git a/test/smoke/src/areas/terminal/terminal.test.ts b/test/smoke/src/areas/terminal/terminal.test.ts index 46bd8d997b359..d661b13a2dc56 100644 --- a/test/smoke/src/areas/terminal/terminal.test.ts +++ b/test/smoke/src/areas/terminal/terminal.test.ts @@ -8,7 +8,6 @@ import { Application } from '../../application'; export function setup() { describe('Terminal', () => { it(`opens terminal, runs 'echo' and verifies the output`, async function () { - this.timeout(60 * 5000); const app = this.app as Application; const expected = new Date().getTime().toString(); diff --git a/test/smoke/src/vscode/code.ts b/test/smoke/src/vscode/code.ts index 0c493ec65f171..da5326e929c59 100644 --- a/test/smoke/src/vscode/code.ts +++ b/test/smoke/src/vscode/code.ts @@ -71,9 +71,10 @@ async function connect(connectDriver: typeof connectElectronDriver, child: cp.Ch const { client, driver } = await connectDriver(outPath, handlePath); return new Code(client, driver, logger); } catch (err) { - console.log('err', err); if (++errCount > 50) { - // child.kill(); + if (child) { + child.kill(); + } throw err; } From e0d2e537f0555bb6e27eba6bfcb95d836088b728 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 19 Jul 2019 17:21:11 -0700 Subject: [PATCH 025/613] Pass web opt through --- test/smoke/src/application.ts | 3 ++- test/smoke/src/main.ts | 3 ++- test/smoke/src/vscode/puppeteer-driver.ts | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/smoke/src/application.ts b/test/smoke/src/application.ts index 7a5b364b16cff..52fe19dd83458 100644 --- a/test/smoke/src/application.ts +++ b/test/smoke/src/application.ts @@ -125,7 +125,8 @@ export class Application { verbose: this.options.verbose, log: this.options.log, extraArgs, - remote: this.options.remote + remote: this.options.remote, + web: this.options.web }); this._workbench = new Workbench(this._code, this.userDataPath); diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 85695d96dcc0a..3060b27d93c52 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -212,7 +212,8 @@ function createOptions(): ApplicationOptions { verbose: opts.verbose, log, screenshotsPath, - remote: opts.remote + remote: opts.remote, + web: opts.web }; } diff --git a/test/smoke/src/vscode/puppeteer-driver.ts b/test/smoke/src/vscode/puppeteer-driver.ts index 7e1ca48ab30ad..42f0c65248791 100644 --- a/test/smoke/src/vscode/puppeteer-driver.ts +++ b/test/smoke/src/vscode/puppeteer-driver.ts @@ -319,7 +319,6 @@ let args; export function launch(_args): void { args = _args; // TODO: Move puppeteer launch here - console.log(args); } export function connect(outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }> { From a23f859aa9f4e4b51dbbbd7fd34d80613c2a4e98 Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Mon, 22 Jul 2019 03:53:24 -0500 Subject: [PATCH 026/613] Make centerEditorLayout do not call layout. Fixes #76494 --- src/vs/workbench/browser/workbench.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 634655ffa84c5..489d07550efb9 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -414,7 +414,7 @@ export class Workbench extends Layout { // Restore Editor Center Mode if (this.state.editor.restoreCentered) { - this.centerEditorLayout(true); + this.centerEditorLayout(true, true); } // Emit a warning after 10s if restore does not complete From 96d1767464491e63300aabf886b9e65f6739118f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 24 Jul 2019 18:15:07 -0700 Subject: [PATCH 027/613] Start of browser driver and driver lib sharing --- src/vs/platform/driver/browser/driver.ts | 233 +++++++++++++++++++++++ src/vs/platform/driver/common/driver.ts | 51 +++++ src/vs/workbench/workbench.web.main.ts | 3 + 3 files changed, 287 insertions(+) create mode 100644 src/vs/platform/driver/browser/driver.ts create mode 100644 src/vs/platform/driver/common/driver.ts diff --git a/src/vs/platform/driver/browser/driver.ts b/src/vs/platform/driver/browser/driver.ts new file mode 100644 index 0000000000000..83b37dc1e212b --- /dev/null +++ b/src/vs/platform/driver/browser/driver.ts @@ -0,0 +1,233 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { getTopLeftOffset } from 'vs/base/browser/dom'; +// TODO: Allow this +// tslint:disable-next-line: import-patterns +import { Terminal } from 'xterm'; +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 + }; +} + +class BrowserWindowDriver implements IWindowDriver { + + constructor() { } + + click(selector: string, xoffset?: number, yoffset?: number): Promise { + const offset = typeof xoffset === 'number' && typeof yoffset === 'number' ? { x: xoffset, y: yoffset } : undefined; + return this._click(selector, 1, offset); + } + + doubleClick(selector: string): Promise { + 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 { + console.log('NYI'); + // const { x, y } = await this._getElementXY(selector, offset); + + // const webContents: electron.WebContents = (electron as any).remote.getCurrentWebContents(); + // webContents.sendInputEvent({ type: 'mouseDown', x, y, button: 'left', clickCount } as any); + // await timeout(10); + + // webContents.sendInputEvent({ type: 'mouseUp', x, y, button: 'left', clickCount } as any); + // 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' }); + } +} + +export async function registerWindowDriver(): Promise { + console.log('registerWindowDriver'); + (window).driver = new BrowserWindowDriver(); + // const windowDriverChannel = new WindowDriverChannel(windowDriver); + // mainProcessService.registerChannel('windowDriver', windowDriverChannel); + + // const windowDriverRegistryChannel = mainProcessService.getChannel('windowDriverRegistry'); + // const windowDriverRegistry = new WindowDriverRegistryChannelClient(windowDriverRegistryChannel); + + // await windowDriverRegistry.registerWindowDriver(windowService.windowId); + // const options = await windowDriverRegistry.registerWindowDriver(windowId); + + // if (options.verbose) { + // windowDriver.openDevTools(); + // } + + // return toDisposable(() => windowDriverRegistry.reloadWindowDriver(windowService.windowId)); + 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 0000000000000..299628846a450 --- /dev/null +++ b/src/vs/platform/driver/common/driver.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// TODO: Change smoketest build to read off common instead + +// !! 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 + +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; +} diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 4d5cf7bfbf93a..4e00191bd99d7 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -349,6 +349,7 @@ import 'vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution'; // Outline import 'vs/workbench/contrib/outline/browser/outline.contribution'; +import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; // Experiments // import 'vs/workbench/contrib/experiments/electron-browser/experiments.contribution'; @@ -357,3 +358,5 @@ import 'vs/workbench/contrib/outline/browser/outline.contribution'; // import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; //#endregion + +registerWindowDriver(); From df3983ffc8a5a4b978129e37ddff88e0f9672e71 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Wed, 24 Jul 2019 13:40:44 -0500 Subject: [PATCH 028/613] Add CompletionItem.deprecated property from LSP --- src/vs/editor/common/modes.ts | 4 ++++ src/vs/monaco.d.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index fa05b9b968cf2..dbeb3fb19566f 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -396,6 +396,10 @@ export interface CompletionItem { * an icon is chosen by the editor. */ kind: CompletionItemKind; + /** + * Indicates if this item is deprecated. + */ + deprecated?: boolean; /** * A human-readable string with additional information * about this item, like type or symbol information. diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index e08ad36831251..b64d6701d7219 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4799,6 +4799,10 @@ declare namespace monaco.languages { * an icon is chosen by the editor. */ kind: CompletionItemKind; + /** + * Indicates if this item is deprecated. + */ + deprecated?: boolean; /** * A human-readable string with additional information * about this item, like type or symbol information. From dbbf3316d3214fcb77b3bc10e9cae6d285069239 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Wed, 24 Jul 2019 14:46:50 -0500 Subject: [PATCH 029/613] Add deprecated support to SuggestDataDto --- src/vs/vscode.proposed.d.ts | 21 +++++++++++++++++++ .../api/browser/mainThreadLanguageFeatures.ts | 1 + .../workbench/api/common/extHost.protocol.ts | 1 + .../api/common/extHostLanguageFeatures.ts | 1 + 4 files changed, 24 insertions(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 430967c999eab..610c340fd55bd 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1290,4 +1290,25 @@ declare module 'vscode' { } //#endregion + + //#region Deprecated support + + export interface CompletionItem { + /** + * Indicates if this item is deprecated. + */ + deprecated?: boolean; + } + + export enum DiagnosticTag { + /** + * Deprecated or obsolete code + * + * Can be used to style with strikeout or other "obsolete" styling. See: + * https://github.com/microsoft/vscode/issues/50972 + */ + Deprecated = 2, + } + + //#endregion } diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 75af9eebf0146..3541ba2eef043 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -341,6 +341,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha commitCharacters: data.k, additionalTextEdits: data.l, command: data.m, + deprecated: data.n, // not-standard _id: data.x, }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index f8d565df56c2d..94db70c62ff60 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -941,6 +941,7 @@ export interface SuggestDataDto { k/* commitCharacters */?: string[]; l/* additionalTextEdits */?: ISingleEditOperation[]; m/* command */?: modes.Command; + n/* deprecated */?: boolean; // not-standard x?: ChainedCacheId; } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 884618cc457c4..6c7065f29f2ca 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -736,6 +736,7 @@ class SuggestAdapter { k: item.commitCharacters, l: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from), m: this._commands.toInternal(item.command, disposables), + n: item.deprecated }; // 'insertText'-logic From 4a4804b7e138e7dab27474633e99d9a0842d9fb3 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Wed, 24 Jul 2019 15:10:18 -0500 Subject: [PATCH 030/613] Add inline deprecated styling to suggest widget --- src/vs/editor/common/model/intervalTree.ts | 3 ++- src/vs/editor/contrib/suggest/media/suggest.css | 7 +++++++ src/vs/editor/contrib/suggest/suggestWidget.ts | 5 +++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts index 496e77b9ef4ba..1815f7ee86f40 100644 --- a/src/vs/editor/common/model/intervalTree.ts +++ b/src/vs/editor/common/model/intervalTree.ts @@ -17,7 +17,8 @@ export const enum ClassName { EditorWarningDecoration = 'squiggly-warning', EditorErrorDecoration = 'squiggly-error', EditorUnnecessaryDecoration = 'squiggly-unnecessary', - EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary' + EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary', + EditorDeprecatedInlineDecoration = 'inline-deprecated' } export const enum NodeColor { diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index 09594abb4937d..2f740dfad6407 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -97,6 +97,13 @@ font-weight: bold; } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .inline-deprecated { + text-decoration: none; /* override normal inline behavior due to HTML structure */ +} +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .inline-deprecated span { + text-decoration: line-through; +} + /** Icon styles **/ .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close, diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index e6ac05dab25a9..7add51f66842b 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -9,6 +9,7 @@ import { createMatches } from 'vs/base/common/filters'; import * as strings from 'vs/base/common/strings'; import { Event, Emitter } from 'vs/base/common/event'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { ClassName } from 'vs/editor/common/model/intervalTree'; import { IDisposable, dispose, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass, addDisposableListener } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IListEvent, IListRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list'; @@ -195,6 +196,10 @@ class Renderer implements IListRenderer ]; } + if (suggestion.label && suggestion.deprecated) { + labelOptions.extraClasses = (labelOptions.extraClasses || []).concat([ClassName.EditorDeprecatedInlineDecoration]); + } + data.iconLabel.setLabel(suggestion.label, undefined, labelOptions); data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); From 65cd41503b525757d347fc1fd1bcce45e14feb10 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 27 Jul 2019 13:39:35 -0700 Subject: [PATCH 031/613] Share code between electron and browser drivers --- package.json | 1 - src/vs/platform/driver/browser/baseDriver.ts | 166 ++++++++++++++ src/vs/platform/driver/browser/driver.ts | 206 +----------------- .../driver/electron-browser/driver.ts | 155 +------------ test/smoke/package.json | 1 + test/smoke/src/main.ts | 9 +- test/smoke/src/vscode/puppeteer-driver.ts | 191 ++-------------- test/smoke/yarn.lock | 7 + yarn.lock | 7 - 9 files changed, 213 insertions(+), 530 deletions(-) create mode 100644 src/vs/platform/driver/browser/baseDriver.ts diff --git a/package.json b/package.json index ca74e4fb96114..0da0569d843c0 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,6 @@ "@types/minimist": "^1.2.0", "@types/mocha": "2.2.39", "@types/node": "^10.12.12", - "@types/puppeteer": "^1.12.4", "@types/semver": "^5.5.0", "@types/sinon": "^1.16.36", "@types/webpack": "^4.4.10", diff --git a/src/vs/platform/driver/browser/baseDriver.ts b/src/vs/platform/driver/browser/baseDriver.ts new file mode 100644 index 0000000000000..7b7d3057db648 --- /dev/null +++ b/src/vs/platform/driver/browser/baseDriver.ts @@ -0,0 +1,166 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getTopLeftOffset } from 'vs/base/browser/dom'; +// TODO: Allow this +// tslint:disable-next-line: import-patterns +import { Terminal } from 'xterm'; +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() { } + + // TODO: This doesn't work in browser driver + 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 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); + } + + abstract async openDevTools(): Promise; +} diff --git a/src/vs/platform/driver/browser/driver.ts b/src/vs/platform/driver/browser/driver.ts index 83b37dc1e212b..e22460f228fbe 100644 --- a/src/vs/platform/driver/browser/driver.ts +++ b/src/vs/platform/driver/browser/driver.ts @@ -4,209 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { getTopLeftOffset } from 'vs/base/browser/dom'; -// TODO: Allow this -// tslint:disable-next-line: import-patterns -import { Terminal } from 'xterm'; -import { coalesce } from 'vs/base/common/arrays'; -import { IElement, IWindowDriver } from 'vs/platform/driver/common/driver'; +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 BrowserWindowDriver implements IWindowDriver { - - constructor() { } - - click(selector: string, xoffset?: number, yoffset?: number): Promise { - const offset = typeof xoffset === 'number' && typeof yoffset === 'number' ? { x: xoffset, y: yoffset } : undefined; - return this._click(selector, 1, offset); +class BrowserWindowDriver extends BaseWindowDriver { + click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise { + throw new Error('Method not implemented.'); } - doubleClick(selector: string): Promise { - 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 { - console.log('NYI'); - // const { x, y } = await this._getElementXY(selector, offset); - - // const webContents: electron.WebContents = (electron as any).remote.getCurrentWebContents(); - // webContents.sendInputEvent({ type: 'mouseDown', x, y, button: 'left', clickCount } as any); - // await timeout(10); - - // webContents.sendInputEvent({ type: 'mouseUp', x, y, button: 'left', clickCount } as any); - // 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); + throw new Error('Method not implemented.'); } - - 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' }); + openDevTools(): Promise { + throw new Error('Method not implemented.'); } } diff --git a/src/vs/platform/driver/electron-browser/driver.ts b/src/vs/platform/driver/electron-browser/driver.ts index a956739bed7f1..931b898d55f87 100644 --- a/src/vs/platform/driver/electron-browser/driver.ts +++ b/src/vs/platform/driver/electron-browser/driver.ts @@ -4,55 +4,22 @@ *--------------------------------------------------------------------------------------------*/ 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; @@ -99,116 +66,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/test/smoke/package.json b/test/smoke/package.json index 11b5662b207d9..24d4cc743ae30 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -18,6 +18,7 @@ "@types/mocha": "2.2.41", "@types/ncp": "2.0.1", "@types/node": "^10.14.8", + "@types/puppeteer": "^1.19.0", "@types/rimraf": "2.0.2", "@types/webdriverio": "4.6.1", "concurrently": "^3.5.1", diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 3060b27d93c52..935b16fb46c21 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -236,7 +236,9 @@ after(async function () { await new Promise((c, e) => rimraf(testDataPath, { maxBusyTries: 10 }, err => err ? e(err) : c())); }); -setupDataMigrationTests(stableCodePath, testDataPath); +if (!opts.web) { + setupDataMigrationTests(stableCodePath, testDataPath); +} describe('Running Code', () => { before(async function () { @@ -271,6 +273,7 @@ describe('Running Code', () => { } if (opts.web) { + console.log('setup term tests only'); setupTerminalTests(); return; } @@ -290,4 +293,6 @@ describe('Running Code', () => { setupDataLocalizationTests(); }); -setupLaunchTests(); \ No newline at end of file +if (!opts.web) { + setupLaunchTests(); +} diff --git a/test/smoke/src/vscode/puppeteer-driver.ts b/test/smoke/src/vscode/puppeteer-driver.ts index 42f0c65248791..12b56ebd202a9 100644 --- a/test/smoke/src/vscode/puppeteer-driver.ts +++ b/test/smoke/src/vscode/puppeteer-driver.ts @@ -139,181 +139,28 @@ function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver `); await page.mouse.click(x + (xoffset ? xoffset : 0), y + (yoffset ? yoffset : 0)); }, - doubleClick: (windowId, selector) => Promise.resolve(), - setValue: async (windowId, selector, text) => { - return page.evaluate(` - (function() { - const element = document.querySelector('${selector}'); - - if (!element) { - throw new Error('Element not found: ${selector}'); - } - - const inputElement = element; - inputElement.value = '${text}'; - - const event = new Event('input', { bubbles: true, cancelable: true }); - inputElement.dispatchEvent(event); - return true; - })(); - `); + doubleClick: async (windowId, selector) => { + await this.click(windowId, selector, 0, 0); + await timeout(60); + await this.click(windowId, selector, 0, 0); + await timeout(100); }, - getTitle: (windowId) => page.title(), - isActiveElement: (windowId, selector) => { - return page.evaluate(`document.querySelector('${selector}') === document.activeElement`); - }, - getElements: (windowId, selector, recursive) => { - return page.evaluate(` - (function() { - function convertToPixels(element, value) { - return parseFloat(value) || 0; - } - function getDimension(element, cssPropertyName, jsPropertyName) { - let computedStyle = getComputedStyle(element); - let value = '0'; - if (computedStyle) { - if (computedStyle.getPropertyValue) { - value = computedStyle.getPropertyValue(cssPropertyName); - } else { - // IE8 - value = (computedStyle).getAttribute(jsPropertyName); - } - } - return convertToPixels(element, value); - } - function getBorderLeftWidth(element) { - return getDimension(element, 'border-left-width', 'borderLeftWidth'); - } - function getBorderRightWidth(element) { - return getDimension(element, 'border-right-width', 'borderRightWidth'); - } - function getBorderTopWidth(element) { - return getDimension(element, 'border-top-width', 'borderTopWidth'); - } - function getBorderBottomWidth(element) { - return getDimension(element, 'border-bottom-width', 'borderBottomWidth'); - } - function getTopLeftOffset(element) { - // Adapted from WinJS.Utilities.getPosition - // and added borders to the mix - - let offsetParent = element.offsetParent, top = element.offsetTop, left = element.offsetLeft; - - while ((element = element.parentNode) !== null && element !== document.body && element !== document.documentElement) { - top -= element.scrollTop; - let c = getComputedStyle(element); - if (c) { - left -= c.direction !== 'rtl' ? element.scrollLeft : -element.scrollLeft; - } - - if (element === offsetParent) { - left += getBorderLeftWidth(element); - top += getBorderTopWidth(element); - top += element.offsetTop; - left += element.offsetLeft; - offsetParent = element.offsetParent; - } - } - - return { - left: left, - top: top - }; - } - function serializeElement(element, recursive) { - 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 = []; - - 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); - - return { - tagName: element.tagName, - className: element.className, - textContent: element.textContent || '', - attributes, - children, - left, - top - }; - } - - const query = document.querySelectorAll('${selector}'); - const result = []; - - for (let i = 0; i < query.length; i++) { - const element = query.item(i); - result.push(serializeElement(element, ${recursive})); - } - - return result; - })(); - `); - }, - typeInEditor: (windowId, selector, text) => Promise.resolve(), - getTerminalBuffer: (windowId, selector) => { - return page.evaluate(` - (function () { - const element = document.querySelector('${selector}'); - - if (!element) { - throw new Error('Terminal not found: ${selector}'); - } - - const xterm = element.xterm; - - if (!xterm) { - throw new Error('Xterm not found: ${selector}'); - } - - const lines = []; - - for (let i = 0; i < xterm.buffer.length; i++) { - lines.push(xterm.buffer.getLine(i).translateToString(true)); - } - - return lines; - })(); - `); - }, - writeInTerminal: (windowId, selector, text) => { - return page.evaluate(` - (function () { - const element = document.querySelector('${selector}'); - - if (!element) { - throw new Error('Element not found: ${selector}'); - } - - const xterm = element.xterm; - - if (!xterm) { - throw new Error('Xterm not found: ${selector}'); - } - - xterm._core._coreService.triggerDataEvent('${text}'); - })(); - `); - } + setValue: async (windowId, selector, text) => page.evaluate(`window.driver.setValue('${selector}', '${text}')`), + getTitle: (windowId) => page.evaluate(`window.driver.getTitle()`), + isActiveElement: (windowId, selector) => page.evaluate(`window.driver.isActiveElement('${selector}')`), + getElements: (windowId, selector, recursive) => page.evaluate(`window.driver.getElements('${selector}', ${recursive})`), + typeInEditor: (windowId, selector, text) => page.evaluate(`window.driver.typeInEditor('${selector}', '${text}')`), + getTerminalBuffer: (windowId, selector) => page.evaluate(`window.driver.getTerminalBuffer('${selector}')`), + writeInTerminal: (windowId, selector, text) => page.evaluate(`window.driver.writeInTerminal('${selector}', '${text}')`) }; } +function timeout(ms: number): Promise { + return new Promise(r => setTimeout(r, ms)); +} + +// function runInDriver(call: string, args: (string | boolean)[]): Promise {} + let args; export function launch(_args): void { @@ -389,4 +236,4 @@ export interface IDriver { export interface IDisposable { dispose(): void; -} \ No newline at end of file +} diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock index b86356d3ac379..7ae63926a2031 100644 --- a/test/smoke/yarn.lock +++ b/test/smoke/yarn.lock @@ -54,6 +54,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== +"@types/puppeteer@^1.19.0": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-1.19.0.tgz#59f0050bae019cee7c3af2bb840a25892a3078b6" + integrity sha512-Db9LWOuTm2bR/qgPE7PQCmnsCQ6flHdULuIDWTks8YdQ/SGHKg5WGWG54gl0734NDKCTF5MbqAp2qWuvBiyQ3Q== + dependencies: + "@types/node" "*" + "@types/rimraf@2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.2.tgz#7f0fc3cf0ff0ad2a99bb723ae1764f30acaf8b6e" diff --git a/yarn.lock b/yarn.lock index 8c907deb523a9..46df292dba6a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -137,13 +137,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.12.tgz#e15a9d034d9210f00320ef718a50c4a799417c47" integrity sha512-Pr+6JRiKkfsFvmU/LK68oBRCQeEg36TyAbPhc2xpez24OOZZCuoIhWGTd39VZy6nGafSbxzGouFPTFD/rR1A0A== -"@types/puppeteer@^1.12.4": - version "1.12.4" - resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-1.12.4.tgz#8388efdb0b30a54a7e7c4831ca0d709191d77ff1" - integrity sha512-aaGbJaJ9TuF9vZfTeoh876sBa+rYJWPwtsmHmYr28pGr42ewJnkDTq2aeSKEmS39SqUdkwLj73y/d7rBSp7mDQ== - dependencies: - "@types/node" "*" - "@types/semver@^5.4.0", "@types/semver@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" From 8ab8f3c758ff36c9f032415478d714e5e1ffbfa7 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Mon, 29 Jul 2019 09:12:00 -0500 Subject: [PATCH 032/613] Switch to private style const --- src/vs/editor/contrib/suggest/media/suggest.css | 5 +---- src/vs/editor/contrib/suggest/suggestWidget.ts | 7 +++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index 2f740dfad6407..5e35818fac705 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -97,10 +97,7 @@ font-weight: bold; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .inline-deprecated { - text-decoration: none; /* override normal inline behavior due to HTML structure */ -} -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .inline-deprecated span { +.monaco-editor .suggest-widget-deprecated span { text-decoration: line-through; } diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 7add51f66842b..fd0c559bb2b7f 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -9,7 +9,6 @@ import { createMatches } from 'vs/base/common/filters'; import * as strings from 'vs/base/common/strings'; import { Event, Emitter } from 'vs/base/common/event'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { ClassName } from 'vs/editor/common/model/intervalTree'; import { IDisposable, dispose, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass, addDisposableListener } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IListEvent, IListRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list'; @@ -63,6 +62,10 @@ export const editorSuggestWidgetForeground = registerColor('editorSuggestWidget. export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: listFocusBackground, light: listFocusBackground, hc: listFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.')); export const editorSuggestWidgetHighlightForeground = registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.')); +/** + * Suggest widget styles + */ +const editorSuggestWidgetDeprecatedClassName = "suggest-widget-deprecated"; const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i; function extractColor(item: CompletionItem, out: string[]): boolean { @@ -197,7 +200,7 @@ class Renderer implements IListRenderer } if (suggestion.label && suggestion.deprecated) { - labelOptions.extraClasses = (labelOptions.extraClasses || []).concat([ClassName.EditorDeprecatedInlineDecoration]); + labelOptions.extraClasses = (labelOptions.extraClasses || []).concat([editorSuggestWidgetDeprecatedClassName]); } data.iconLabel.setLabel(suggestion.label, undefined, labelOptions); From 239d57eabfc4e27b4bb931a5820a1b873c58ffd3 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Mon, 29 Jul 2019 09:13:00 -0500 Subject: [PATCH 033/613] Fix bad merge --- src/vs/editor/common/model/intervalTree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts index 1815f7ee86f40..d400024e7c5b5 100644 --- a/src/vs/editor/common/model/intervalTree.ts +++ b/src/vs/editor/common/model/intervalTree.ts @@ -18,7 +18,7 @@ export const enum ClassName { EditorErrorDecoration = 'squiggly-error', EditorUnnecessaryDecoration = 'squiggly-unnecessary', EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary', - EditorDeprecatedInlineDecoration = 'inline-deprecated' + EditorDeprecatedInlineDecoration = 'squiggly-inline-deprecated' } export const enum NodeColor { From c8b2019d58fd251883d2467ea4b0b5d8de151cbc Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Mon, 29 Jul 2019 09:14:59 -0500 Subject: [PATCH 034/613] Fix bad merge comment --- src/vs/vscode.proposed.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 68e0b1e6e5d26..563ea4475eb77 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1318,7 +1318,7 @@ declare module 'vscode' { /** * Deprecated or obsolete code * - * Can be used to style with strikeout or other "obsolete" styling. + * Diagnostics with this tag are rendered with a strike through. */ Deprecated = 2, } From ffe48525cb9c94aa505031112189361c9211cae0 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Mon, 29 Jul 2019 09:15:31 -0500 Subject: [PATCH 035/613] Missing period --- src/vs/vscode.proposed.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 563ea4475eb77..2415f0609b617 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1316,7 +1316,7 @@ declare module 'vscode' { export enum DiagnosticTag { /** - * Deprecated or obsolete code + * Deprecated or obsolete code. * * Diagnostics with this tag are rendered with a strike through. */ From 27b0a0a0a087b73e6616c9f890d0d54d9ec693fc Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Mon, 29 Jul 2019 10:22:21 -0500 Subject: [PATCH 036/613] Use single quotes for classname --- src/vs/editor/contrib/suggest/suggestWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index fd0c559bb2b7f..8cbb72a677f54 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -65,7 +65,7 @@ export const editorSuggestWidgetHighlightForeground = registerColor('editorSugge /** * Suggest widget styles */ -const editorSuggestWidgetDeprecatedClassName = "suggest-widget-deprecated"; +const editorSuggestWidgetDeprecatedClassName = 'suggest-widget-deprecated'; const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i; function extractColor(item: CompletionItem, out: string[]): boolean { From e723f810ecba9738b9c98eb2b8f18c2b14ec0058 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 31 Jul 2019 10:24:42 -0700 Subject: [PATCH 037/613] Add more tests --- .../src/areas/preferences/preferences.test.ts | 2 +- test/smoke/src/areas/preferences/settings.ts | 4 +- test/smoke/src/main.ts | 11 +++++- test/smoke/src/vscode/puppeteer-driver.ts | 38 +++++++++++++------ 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/test/smoke/src/areas/preferences/preferences.test.ts b/test/smoke/src/areas/preferences/preferences.test.ts index f1eb589b11885..c7a1ce5147583 100644 --- a/test/smoke/src/areas/preferences/preferences.test.ts +++ b/test/smoke/src/areas/preferences/preferences.test.ts @@ -34,4 +34,4 @@ export function setup() { await app.workbench.settingsEditor.clearUserSettings(); }); }); -} \ No newline at end of file +} diff --git a/test/smoke/src/areas/preferences/settings.ts b/test/smoke/src/areas/preferences/settings.ts index cebd2f5149787..de7bd1ffb43af 100644 --- a/test/smoke/src/areas/preferences/settings.ts +++ b/test/smoke/src/areas/preferences/settings.ts @@ -30,7 +30,7 @@ export class SettingsEditor { async clearUserSettings(): Promise { const settingsPath = path.join(this.userDataPath, 'User', 'settings.json'); - await new Promise((c, e) => fs.writeFile(settingsPath, '{}', 'utf8', err => err ? e(err) : c())); + await new Promise((c, e) => fs.writeFile(settingsPath, '{\n}', 'utf8', err => err ? e(err) : c())); await this.openSettings(); await this.editor.waitForEditorContents('settings.json', c => c === '{}'); @@ -39,4 +39,4 @@ export class SettingsEditor { private async openSettings(): Promise { await this.quickopen.runCommand('Preferences: Open Settings (JSON)'); } -} \ No newline at end of file +} diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 935b16fb46c21..4972be890f1bf 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -141,7 +141,8 @@ if (typeof stablePath === 'string' && !fs.existsSync(stablePath)) { fail(`Can't find Stable Code at ${stablePath}.`); } -const userDataDir = path.join(testDataPath, 'd'); +// TODO: Server should be launched from smoke tests +const userDataDir = opts.web ? path.join(process.env.HOME!, '.vscode-remote/data') : path.join(testDataPath, 'd'); let quality: Quality; if (process.env.VSCODE_DEV === '1') { @@ -245,6 +246,11 @@ describe('Running Code', () => { const app = new Application(this.defaultOptions); await app!.start(opts.web ? false : undefined); this.app = app; + + // TODO: User data dir is not cleared for web yet + if (opts.web) { + await app.workbench.settingsEditor.clearUserSettings(); + } }); after(async function () { @@ -273,7 +279,8 @@ describe('Running Code', () => { } if (opts.web) { - console.log('setup term tests only'); + setupDataExplorerTests(); + setupDataPreferencesTests(); setupTerminalTests(); return; } diff --git a/test/smoke/src/vscode/puppeteer-driver.ts b/test/smoke/src/vscode/puppeteer-driver.ts index 12b56ebd202a9..fbe7b26db47db 100644 --- a/test/smoke/src/vscode/puppeteer-driver.ts +++ b/test/smoke/src/vscode/puppeteer-driver.ts @@ -11,7 +11,13 @@ const height = 800; const vscodeToPuppeteerKey = { cmd: 'Meta', ctrl: 'Control', - enter: 'Enter' + enter: 'Enter', + escape: 'Escape', + right: 'ArrowRight', + up: 'ArrowUp', + down: 'ArrowDown', + left: 'ArrowLeft', + home: 'Home' }; function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver { @@ -24,18 +30,26 @@ function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver reloadWindow: (windowId) => Promise.resolve(), exitApplication: () => browser.close(), dispatchKeybinding: async (windowId, keybinding) => { - const keys = keybinding.split('+'); - const keysDown: string[] = []; - for (let i = 0; i < keys.length; i++) { - if (keys[i] in vscodeToPuppeteerKey) { - keys[i] = vscodeToPuppeteerKey[keys[i]]; + const chords = keybinding.split(' '); + chords.forEach(async (chord, index) => { + if (index > 0) { + await timeout(100); } - await page.keyboard.down(keys[i]); - keysDown.push(keys[i]); - } - while (keysDown.length > 0) { - await page.keyboard.up(keysDown.pop()!); - } + const keys = chord.split('+'); + const keysDown: string[] = []; + for (let i = 0; i < keys.length; i++) { + if (keys[i] in vscodeToPuppeteerKey) { + keys[i] = vscodeToPuppeteerKey[keys[i]]; + } + await page.keyboard.down(keys[i]); + keysDown.push(keys[i]); + } + while (keysDown.length > 0) { + await page.keyboard.up(keysDown.pop()!); + } + }); + + await timeout(100); }, click: async (windowId, selector, xoffset, yoffset) => { const { x, y } = await page.evaluate(` From 1d658ded860195bdcac132e28044aa2b2ea019b5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 6 Aug 2019 17:27:06 +0200 Subject: [PATCH 038/613] remove unsed ext loader --- .../extensions/worker/extensionLoader.ts | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 src/vs/workbench/services/extensions/worker/extensionLoader.ts diff --git a/src/vs/workbench/services/extensions/worker/extensionLoader.ts b/src/vs/workbench/services/extensions/worker/extensionLoader.ts deleted file mode 100644 index 088fa872920bb..0000000000000 --- a/src/vs/workbench/services/extensions/worker/extensionLoader.ts +++ /dev/null @@ -1,24 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - - -self['extensions'] = {}; -function wrap(name: string, script: string) { - //https://github.com/nodejs/node/blob/master/lib/internal/modules/cjs/loader.js#L125 - return `self['extensions']['${name}']= (function (exports, require) { ${script}\n});`; -} - -export function importWrappedScript(scriptSrc: string, scriptPath: string) { - - importScripts(`data:text/javascript;charset=utf-8,${encodeURIComponent(wrap(scriptPath, scriptSrc))}`); - - // const fn = new Function('exports', 'require', 'module', scriptSrc); - // const exports = Object.create(null); - // const thisRequire = function (path: string) { - // console.log(path); - // }; - // fn(exports, thisRequire, undefined); - // console.log(exports); -} From 69b1bfb5a19ddf2336a5f28792e30f6682e4a79c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 Aug 2019 10:07:05 +0200 Subject: [PATCH 039/613] update with changes on master --- .../electron-browser/extensionService.ts | 2 +- .../webWorkerExtensionHostStarter.ts | 16 +- .../extensions/worker/extHost.api.impl.ts | 148 +++++++++----- .../worker/extHostExtensionService.ts | 180 ++++++++++++------ .../extensions/worker/extensionHostMain.ts | 27 ++- 5 files changed, 249 insertions(+), 124 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index c8a224a107088..8d8b85b0f01a7 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -350,7 +350,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten } const result: ExtensionHostProcessManager[] = []; - const workerExtensions = { ['jrieken.helloworld']: true }; + const workerExtensions: Record = { ['jrieken.helloworld']: true }; const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions.then(e => e.filter(e => !workerExtensions[e.identifier.value])), this._extensionHostLogsLocation); const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, true, extHostProcessWorker, null, initialActivationEvents); diff --git a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts index a7d616c257c82..4c8492f14096a 100644 --- a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts @@ -17,12 +17,12 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILogService } from 'vs/platform/log/common/log'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions'; import { IProductService } from 'vs/platform/product/common/product'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { @@ -41,7 +41,7 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @ILabelService private readonly _labelService: ILabelService, @ILogService private readonly _logService: ILogService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, @IProductService private readonly _productService: IProductService, ) { @@ -121,14 +121,16 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { environment: { isExtensionDevelopmentDebug: false, // < todo@joh appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, - appSettingsHome: this._environmentService.appSettingsHome ? URI.file(this._environmentService.appSettingsHome) : undefined, + appSettingsHome: this._environmentService.appSettingsHome ? this._environmentService.appSettingsHome : undefined, appName: this._productService.nameLong, appUriScheme: this._productService.urlProtocol, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, globalStorageHome: URI.file(this._environmentService.globalStorageHome), - userHome: URI.file(this._environmentService.userHome) + userHome: URI.file(this._environmentService.userHome), + webviewResourceRoot: this._environmentService.webviewResourceRoot, + webviewCspSource: this._environmentService.webviewCspSource, }, workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { configuration: workspace.configuration || undefined, @@ -141,7 +143,11 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: this._extensionHostLogsLocation, - autoStart: true// < todo@joh this._autoStart + autoStart: true,// < todo@joh this._autoStart, + remote: { + authority: this._environmentService.configuration.remoteAuthority, + isRemote: false + }, }; return r; }); diff --git a/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts b/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts index 3b4950702e63a..e0c1f3ad4d1da 100644 --- a/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts +++ b/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; @@ -32,7 +33,6 @@ import { ExtensionActivatedByAPI } from 'vs/workbench/api/common/extHostExtensio import { ExtHostExtensionService } from './extHostExtensionService'; import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem'; import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService'; -import { ExtHostHeapService } from 'vs/workbench/api/common/extHostHeapService'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures'; import { ExtHostLanguages } from 'vs/workbench/api/common/extHostLanguages'; import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; @@ -49,6 +49,7 @@ import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; // import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService'; import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors'; import { ExtHostTreeViews } from 'vs/workbench/api/common/extHostTreeViews'; +// import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadService'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { ExtHostUrls } from 'vs/workbench/api/common/extHostUrls'; @@ -62,10 +63,15 @@ import * as vscode from 'vscode'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { originalFSPath } from 'vs/base/common/resources'; // import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { values } from 'vs/base/common/collections'; -import { IURITransformer } from 'vs/base/common/uriIpc'; // import { Schemas } from 'vs/base/common/network'; +import { IURITransformer } from 'vs/base/common/uriIpc'; +import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets'; +import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService'; +// import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +// import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +// import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions'; +import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -90,44 +96,51 @@ export function createApiFactory( extensionService: ExtHostExtensionService, extHostLogService: ExtHostLogService, extHostStorage: ExtHostStorage, - schemeTransformer: IURITransformer | null + uriTransformer: IURITransformer | null ): IExtensionApiFactory { + // bootstrap services + // const services = new ServiceCollection(...getSingletonServiceDescriptors()); + // const instaService = new InstantiationService(services); + // Addressable instances rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService); - const extHostHeapService = rpcProtocol.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService()); const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, new ExtHostDecorations(rpcProtocol)); - const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol)); + const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment)); const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol)); const extHostDocumentsAndEditors = rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(rpcProtocol)); const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors)); const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService)); const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadTextEditors))); const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors)); - const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, extHostLogService)); + const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostLogService)); const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService)); + // rpcProtocol.set(ExtHostContext.ExtHostDownloadService, new ExtHostDownloadService(rpcProtocol.getProxy(MainContext.MainThreadDownloadService), extHostCommands)); rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace); rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration); + const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment)); const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol)); - const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, schemeTransformer, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics, extHostLogService)); + const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService)); const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures)); const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors)); const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands)); - // const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService)); - // const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands)); + // const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostWorkspace, extHostDocumentsAndEditors, extHostLogService)); + // const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, instaService.createInstance(ExtHostDebugService, rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands)); const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService)); const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments)); - // const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer, extHostLogService)); + // const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, uriTransformer, extHostLogService)); // const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService)); const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol)); rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService); const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress))); const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, new ExtHostOutputService(PushOutputChannelFactory, initData.logsLocation, rpcProtocol)); rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage); - // if (initData.remoteAuthority) { + const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol)); + + // if (initData.remote.isRemote && initData.remote.authority) { // extHostTask.registerTaskSystem(Schemas.vscodeRemote, { // scheme: Schemas.vscodeRemote, - // authority: initData.remoteAuthority, + // authority: initData.remote.authority, // platform: process.platform // }); @@ -143,6 +156,7 @@ export function createApiFactory( rpcProtocol.set(ExtHostContext.ExtHostDebugService, proxy); rpcProtocol.set(ExtHostContext.ExtHostSearch, proxy); rpcProtocol.set(ExtHostContext.ExtHostTask, proxy); + rpcProtocol.set(ExtHostContext.ExtHostDownloadService, proxy); // Check that no named customers are missing const expected: ProxyIdentifier[] = values(ExtHostContext); @@ -156,7 +170,8 @@ export function createApiFactory( const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments); // Register an output channel for exthost log - // extHostOutputService.createOutputChannelFromLogFile(outputChannelName, extHostLogService.logFile); // < todo@joh + const outputChannelName = initData.remote.isRemote ? nls.localize('remote extension host Log', "Remote Extension Host") : nls.localize('extension host Log', "Extension Host"); + extHostOutputService.createOutputChannelFromLogFile(outputChannelName, extHostLogService.logFile); // Register API-ish commands ExtHostApiCommands.register(extHostCommands); @@ -165,7 +180,7 @@ export function createApiFactory( // Check document selectors for being overly generic. Technically this isn't a problem but // in practice many extensions say they support `fooLang` but need fs-access to do so. Those - // extension should specify then the `file`-scheme, e.g `{ scheme: 'fooLang', language: 'fooLang' }` + // extension should specify then the `file`-scheme, e.g. `{ scheme: 'fooLang', language: 'fooLang' }` // We only inform once, it is not a warning because we just want to raise awareness and because // we cannot say if the extension is doing it right or wrong... const checkSelector = (function () { @@ -237,11 +252,15 @@ export function createApiFactory( }, 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 - const env: typeof vscode.env = Object.freeze({ + const env: typeof vscode.env = { get machineId() { return initData.telemetryInfo.machineId; }, get sessionId() { return initData.telemetryInfo.sessionId; }, get language() { return initData.environment.appLanguage; }, @@ -259,22 +278,37 @@ export function createApiFactory( get clipboard(): vscode.Clipboard { return extHostClipboard; }, + get shell(): string { + throw new Error('not implemented'); + // return extHostTerminalService.getDefaultShell(configProvider); + }, openExternal(uri: URI) { - return extHostWindow.openUri(uri, { allowTunneling: Boolean(initData.remoteAuthority) }); + return extHostWindow.openUri(uri, { allowTunneling: !!initData.remote.isRemote }); + }, + get remoteName() { + return getRemoteName(initData.remote.authority); } - }); + }; + if (!initData.environment.extensionTestsLocationURI) { + // allow to patch env-function when running tests + Object.freeze(env); + } + + const extensionKind = initData.remote.isRemote + ? extHostTypes.ExtensionKind.Workspace + : extHostTypes.ExtensionKind.UI; // namespace: extensions const extensions: typeof vscode.extensions = { getExtension(extensionId: string): Extension | undefined { const desc = extensionRegistry.getExtensionDescription(extensionId); if (desc) { - return new Extension(extensionService, desc); + return new Extension(extensionService, desc, extensionKind); } return undefined; }, get all(): Extension[] { - return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc)); + return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc, extensionKind)); }, get onDidChange() { return extensionRegistry.onDidChange; @@ -307,10 +341,6 @@ export function createApiFactory( registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider); }, - registerCodeInsetProvider(selector: vscode.DocumentSelector, provider: vscode.CodeInsetProvider): vscode.Disposable { - checkProposedApiEnabled(extension); - return extHostLanguageFeatures.registerCodeInsetProvider(extension, checkSelector(selector), provider); - }, registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider); }, @@ -474,8 +504,24 @@ export function createApiFactory( showSaveDialog(options) { return extHostDialogs.showSaveDialog(options); }, - createStatusBarItem(position?: vscode.StatusBarAlignment, priority?: number): vscode.StatusBarItem { - return extHostStatusBar.createStatusBarEntry(extension.identifier, position, priority); + createStatusBarItem(alignmentOrOptions?: vscode.StatusBarAlignment | vscode.window.StatusBarItemOptions, priority?: number): vscode.StatusBarItem { + let id: string; + let name: string; + let alignment: number | undefined; + + if (alignmentOrOptions && typeof alignmentOrOptions !== 'number') { + id = alignmentOrOptions.id; + name = alignmentOrOptions.name; + alignment = alignmentOrOptions.alignment; + priority = alignmentOrOptions.priority; + } else { + id = extension.identifier.value; + name = nls.localize('extensionLabel', "{0} (Extension)", extension.displayName || extension.name); + alignment = alignmentOrOptions; + priority = priority; + } + + return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority); }, setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable): vscode.Disposable { return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable); @@ -493,10 +539,17 @@ export function createApiFactory( createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel { return extHostWebviews.createWebviewPanel(extension, viewType, title, showOptions, options); }, - createTerminal(nameOrOptions?: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal { + createWebviewTextEditorInset(editor: vscode.TextEditor, line: number, height: number, options: vscode.WebviewOptions): vscode.WebviewEditorInset { + checkProposedApiEnabled(extension); + return extHostEditorInsets.createWebviewEditorInset(editor, line, height, options, extension); + }, + createTerminal(nameOrOptions?: vscode.TerminalOptions | vscode.ExtensionTerminalOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal { throw new Error('not implemented'); // if (typeof nameOrOptions === 'object') { - // return extHostTerminalService.createTerminalFromOptions(nameOrOptions); + // if ('pty' in nameOrOptions) { + // return extHostTerminalService.createExtensionTerminal(nameOrOptions); + // } + // return extHostTerminalService.createTerminalFromOptions(nameOrOptions); // } // return extHostTerminalService.createTerminal(nameOrOptions, shellPath, shellArgs); }, @@ -550,6 +603,9 @@ export function createApiFactory( get workspaceFile() { return extHostWorkspace.workspaceFile; }, + set workspaceFile(value) { + throw errors.readonly(); + }, updateWorkspaceFolders: (index, deleteCount, ...workspaceFoldersToAdd) => { return extHostWorkspace.updateWorkspaceFolders(extension, index, deleteCount || 0, ...workspaceFoldersToAdd); }, @@ -560,7 +616,8 @@ export function createApiFactory( return extHostWorkspace.getRelativePath(pathOrUri, includeWorkspace); }, findFiles: (include, exclude, maxResults?, token?) => { - return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(withNullAsUndefined(exclude)), maxResults, extension.identifier, token); + // Note, undefined/null have different meanings on "exclude" + return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(exclude), maxResults, extension.identifier, token); }, findTextInFiles: (query: vscode.TextSearchQuery, optionsOrCallback: vscode.FindTextInFilesOptions | ((result: vscode.TextSearchResult) => void), callbackOrToken?: vscode.CancellationToken | ((result: vscode.TextSearchResult) => void), token?: vscode.CancellationToken) => { let options: vscode.FindTextInFilesOptions; @@ -639,11 +696,14 @@ export function createApiFactory( }, registerTaskProvider: (type: string, provider: vscode.TaskProvider) => { throw new Error('not implemented'); - // return extHostTask.registerTaskProvider(extension, provider); + // return extHostTask.registerTaskProvider(extension, type, provider); }, registerFileSystemProvider(scheme, provider, options) { return extHostFileSystem.registerFileSystemProvider(scheme, provider, options); }, + get fs() { + return extHostFileSystem.fileSystem; + }, registerFileSearchProvider: proposedApiFunction(extension, (scheme: string, provider: vscode.FileSearchProvider) => { throw new Error('not implemented'); // return extHostSearch.registerFileSearchProvider(scheme, provider); @@ -652,17 +712,11 @@ export function createApiFactory( throw new Error('not implemented'); // return extHostSearch.registerTextSearchProvider(scheme, provider); }), - registerDocumentCommentProvider: proposedApiFunction(extension, (provider: vscode.DocumentCommentProvider) => { - return extHostComment.registerDocumentCommentProvider(extension.identifier, provider); - }), - registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => { - return extHostComment.registerWorkspaceCommentProvider(extension.identifier, provider); - }), registerRemoteAuthorityResolver: proposedApiFunction(extension, (authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver) => { return extensionService.registerRemoteAuthorityResolver(authorityPrefix, resolver); }), registerResourceLabelFormatter: proposedApiFunction(extension, (formatter: vscode.ResourceLabelFormatter) => { - return extHostFileSystem.registerResourceLabelFormatter(formatter); + return extHostLabelService.$registerResourceLabelFormatter(formatter); }), onDidRenameFile: proposedApiFunction(extension, (listener: (e: vscode.FileRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => { return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables); @@ -682,7 +736,7 @@ export function createApiFactory( } }; - const comment: typeof vscode.comment = { + const comment: typeof vscode.comments = { createCommentController(id: string, label: string) { return extHostComment.createCommentController(extension, id, label); } @@ -690,7 +744,7 @@ export function createApiFactory( const comments = comment; - // // namespace: debug + // namespace: debug // const debug: typeof vscode.debug = { // get activeDebugSession() { // return extHostDebugService.activeDebugSession; @@ -738,7 +792,7 @@ export function createApiFactory( // const tasks: typeof vscode.tasks = { // registerTaskProvider: (type: string, provider: vscode.TaskProvider) => { - // return extHostTask.registerTaskProvider(extension, provider); + // return extHostTask.registerTaskProvider(extension, type, provider); // }, // fetchTasks: (filter?: vscode.TaskFilter): Thenable => { // return extHostTask.fetchTasks(filter); @@ -810,7 +864,9 @@ export function createApiFactory( EndOfLine: extHostTypes.EndOfLine, EventEmitter: Emitter, ExtensionExecutionContext: extHostTypes.ExtensionExecutionContext, + ExtensionKind: extHostTypes.ExtensionKind, CustomExecution: extHostTypes.CustomExecution, + CustomExecution2: extHostTypes.CustomExecution2, FileChangeType: extHostTypes.FileChangeType, FileSystemError: extHostTypes.FileSystemError, FileType: files.FileType, @@ -877,16 +933,18 @@ class Extension implements vscode.Extension { private _extensionService: ExtHostExtensionService; private _identifier: ExtensionIdentifier; - public id: string; - public extensionPath: string; - public packageJSON: IExtensionDescription; + readonly id: string; + readonly extensionPath: string; + readonly packageJSON: IExtensionDescription; + readonly extensionKind: vscode.ExtensionKind; - constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription) { + constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription, kind: extHostTypes.ExtensionKind) { this._extensionService = extensionService; this._identifier = description.identifier; this.id = description.identifier.value; this.extensionPath = path.normalize(originalFSPath(description.extensionLocation)); this.packageJSON = description; + this.extensionKind = kind; } get isActive(): boolean { diff --git a/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts b/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts index 9e8e3eb7cd4db..6e4cc7adb4264 100644 --- a/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts +++ b/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; import { originalFSPath } from 'vs/base/common/resources'; import { Barrier } from 'vs/base/common/async'; -import { dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; @@ -15,7 +15,7 @@ import { createApiFactory, IExtensionApiFactory } from './extHost.api.impl'; // import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor'; import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IResolveAuthorityResult } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; -import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension } from 'vs/workbench/api/common/extHostExtensionActivator'; +import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator'; import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; @@ -26,7 +26,6 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import * as vscode from 'vscode'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { IWorkspace } from 'vs/platform/workspace/common/workspace'; import { Schemas } from 'vs/base/common/network'; // import { withNullAsUndefined } from 'vs/base/common/types'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -34,18 +33,35 @@ import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento'; // import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths'; import { RemoteAuthorityResolverError, ExtensionExecutionContext } from 'vs/workbench/api/common/extHostTypes'; import { IURITransformer } from 'vs/base/common/uriIpc'; +import { ResolvedAuthority, ResolvedOptions } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { endsWith } from 'vs/base/common/strings'; interface ITestRunner { + /** Old test runner API, as exported from `vscode/lib/testrunner` */ run(testsRoot: string, clb: (error: Error, failures?: number) => void): void; } +interface INewTestRunner { + /** New test runner API, as explained in the extension test doc */ + run(): Promise; +} + export interface IHostUtils { exit(code?: number): void; exists(path: string): Promise; realpath(path: string): Promise; } +type TelemetryActivationEventFragment = { + id: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + name: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + extensionVersion: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + publisherDisplayName: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + activationEvents: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + isBuiltin: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + reason: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; +}; + export class ExtHostExtensionService implements ExtHostExtensionServiceShape { private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000; @@ -76,6 +92,8 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { private _started: boolean; + private readonly _disposables: DisposableStore; + constructor( hostUtils: IHostUtils, initData: IInitData, @@ -93,6 +111,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { this._extHostConfiguration = extHostConfiguration; // this._environment = environment; this._extHostLogService = extHostLogService; + this._disposables = new DisposableStore(); this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace); this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry); @@ -137,7 +156,6 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { uriTransformer ); - this._resolvers = Object.create(null); this._started = false; @@ -304,32 +322,32 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) { const event = getTelemetryActivationEvent(extensionDescription, reason); - /* __GDPR__ - "extensionActivationTimes" : { - "${include}": [ - "${TelemetryActivationEvent}", - "${ExtensionActivationTimes}" - ], - "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this._mainThreadTelemetryProxy.$publicLog('extensionActivationTimes', { + type ExtensionActivationTimesClassification = { + outcome: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + } & TelemetryActivationEventFragment & ExtensionActivationTimesFragment; + + type ExtensionActivationTimesEvent = { + outcome: string + } & ActivationTimesEvent & TelemetryActivationEvent; + + type ActivationTimesEvent = { + startup?: boolean; + codeLoadingTime?: number; + activateCallTime?: number; + activateResolvedTime?: number; + }; + + this._mainThreadTelemetryProxy.$publicLog2('extensionActivationTimes', { ...event, ...(activationTimes || {}), - outcome, + outcome }); } private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { const event = getTelemetryActivationEvent(extensionDescription, reason); - /* __GDPR__ - "activatePlugin" : { - "${include}": [ - "${TelemetryActivationEvent}" - ] - } - */ - this._mainThreadTelemetryProxy.$publicLog('activatePlugin', event); + type ActivatePluginClassification = {} & TelemetryActivationEventFragment; + this._mainThreadTelemetryProxy.$publicLog2('activatePlugin', event); if (!extensionDescription.main) { // Treat the extension as being empty => NOT AN ERROR CASE return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE)); @@ -346,7 +364,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { }); } - private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { + private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage); const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage); @@ -363,11 +381,13 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { workspaceState, subscriptions: [], get extensionPath() { return extensionDescription.extensionLocation.fsPath; }, + // storagePath: this._storagePath.workspaceValue(extensionDescription), + // globalStoragePath: this._storagePath.globalValue(extensionDescription), get storagePath(): string { throw new Error('not impl'); },// this._storagePath.workspaceValue(extensionDescription), get globalStoragePath(): string { throw new Error('not impl'); },// this._storagePath.globalValue(extensionDescription), asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); }, logPath: that._extHostLogService.getLogDirectory(extensionDescription.identifier), - executionContext: this._initData.remoteAuthority ? ExtensionExecutionContext.Remote : ExtensionExecutionContext.Local + executionContext: this._initData.remote.isRemote ? ExtensionExecutionContext.Remote : ExtensionExecutionContext.Local, }); }); } @@ -389,6 +409,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { try { activationTimesBuilder.activateCallStart(); logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`); + // const activateResult: Promise = extensionModule.activate.apply(global, [context]); const activateResult: Promise = extensionModule.activate.apply(undefined, [context]); activationTimesBuilder.activateCallStop(); @@ -414,27 +435,33 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { console.error(err); }); - return this._handleWorkspaceContainsEagerExtensions(this._extHostWorkspace.workspace); + this._disposables.add(this._extHostWorkspace.onDidChangeWorkspace((e) => this._handleWorkspaceContainsEagerExtensions(e.added))); + const folders = this._extHostWorkspace.workspace ? this._extHostWorkspace.workspace.folders : []; + return this._handleWorkspaceContainsEagerExtensions(folders); } - private _handleWorkspaceContainsEagerExtensions(workspace: IWorkspace | undefined): Promise { - if (!workspace || workspace.folders.length === 0) { + private _handleWorkspaceContainsEagerExtensions(folders: ReadonlyArray): Promise { + if (folders.length === 0) { return Promise.resolve(undefined); } return Promise.all( this._registry.getAllExtensionDescriptions().map((desc) => { - return this._handleWorkspaceContainsEagerExtension(workspace, desc); + return this._handleWorkspaceContainsEagerExtension(folders, desc); }) ).then(() => { }); } - private _handleWorkspaceContainsEagerExtension(workspace: IWorkspace, desc: IExtensionDescription): Promise { + private _handleWorkspaceContainsEagerExtension(folders: ReadonlyArray, desc: IExtensionDescription): Promise { const activationEvents = desc.activationEvents; if (!activationEvents) { return Promise.resolve(undefined); } + if (this.isActivated(desc.identifier)) { + return Promise.resolve(undefined); + } + const fileNames: string[] = []; const globPatterns: string[] = []; @@ -453,16 +480,16 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return Promise.resolve(undefined); } - const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(workspace, desc.identifier, fileName))).then(() => { }); - const globPatternPromise = this._activateIfGlobPatterns(desc.identifier, globPatterns); + const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(folders, desc.identifier, fileName))).then(() => { }); + const globPatternPromise = this._activateIfGlobPatterns(folders, desc.identifier, globPatterns); return Promise.all([fileNamePromise, globPatternPromise]).then(() => { }); } - private async _activateIfFileName(workspace: IWorkspace, extensionId: ExtensionIdentifier, fileName: string): Promise { + private async _activateIfFileName(folders: ReadonlyArray, extensionId: ExtensionIdentifier, fileName: string): Promise { // find exact path - for (const { uri } of workspace.folders) { + for (const { uri } of folders) { if (await this._hostUtils.exists(path.join(URI.revive(uri).fsPath, fileName))) { // the file was found return ( @@ -475,7 +502,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return undefined; } - private async _activateIfGlobPatterns(extensionId: ExtensionIdentifier, globPatterns: string[]): Promise { + private async _activateIfGlobPatterns(folders: ReadonlyArray, extensionId: ExtensionIdentifier, globPatterns: string[]): Promise { this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`); if (globPatterns.length === 0) { @@ -483,7 +510,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } const tokenSource = new CancellationTokenSource(); - const searchP = this._mainThreadWorkspaceProxy.$checkExists(globPatterns, tokenSource.token); + const searchP = this._mainThreadWorkspaceProxy.$checkExists(folders.map(folder => folder.uri), globPatterns, tokenSource.token); const timer = setTimeout(async () => { tokenSource.cancel(); @@ -531,7 +558,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { const extensionTestsPath = originalFSPath(extensionTestsLocationURI); // Require the test runner via node require from the provided path - let testRunner: ITestRunner | undefined; + let testRunner: ITestRunner | INewTestRunner | undefined; let requireError: Error | undefined; try { testRunner = require.__$__nodeRequire(extensionTestsPath); @@ -539,10 +566,10 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { requireError = error; } - // Execute the runner if it follows our spec + // Execute the runner if it follows the old `run` spec if (testRunner && typeof testRunner.run === 'function') { return new Promise((c, e) => { - testRunner!.run(extensionTestsPath, (error, failures) => { + const oldTestRunnerCallback = (error: Error, failures: number | undefined) => { if (error) { e(error.toString()); } else { @@ -551,7 +578,22 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { // after tests have run, we shutdown the host this._gracefulExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */); - }); + }; + + const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback); + + // Using the new API `run(): Promise` + if (runResult && runResult.then) { + runResult + .then(() => { + c(); + this._gracefulExit(0); + }) + .catch((err: Error) => { + e(err.toString()); + this._gracefulExit(1); + }); + } }); } @@ -568,7 +610,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { // messages to the main process, we delay the exit() by some time setTimeout(() => { // If extension tests are running, give the exit code to the renderer - if (this._initData.remoteAuthority && !!this._initData.environment.extensionTestsLocationURI) { + if (this._initData.remote.isRemote && !!this._initData.environment.extensionTestsLocationURI) { this._mainThreadExtensionsProxy.$onExtensionHostExit(code); return; } @@ -620,12 +662,22 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { try { const result = await resolver.resolve(remoteAuthority, { resolveAttempt }); + + // Split merged API result into separate authority/options + const authority: ResolvedAuthority = { + authority: remoteAuthority, + host: result.host, + port: result.port + }; + const options: ResolvedOptions = { + extensionHostEnv: result.extensionHostEnv + }; + return { type: 'ok', value: { - authority: remoteAuthority, - host: result.host, - port: result.port, + authority, + options } }; } catch (err) { @@ -705,13 +757,29 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return buff; } + public async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise { + if (!this._initData.remote.isRemote) { + return; + } + + for (const key in env) { + const value = env[key]; + if (value === null) { + delete process.env[key]; + } else { + process.env[key] = value; + } + } + } } async function loadCommonJSModule(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { // fake commonjs world const module = { exports: {} }; + //@ts-ignore self['module'] = module; + //@ts-ignore self['exports'] = module.exports; // that's improper but might help extensions that aren't author correctly @@ -730,22 +798,20 @@ async function loadCommonJSModule(logService: ILogService, modulePath: string return module.exports as T; } -function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any { +type TelemetryActivationEvent = { + id: string; + name: string; + extensionVersion: string; + publisherDisplayName: string; + activationEvents: string | null; + isBuiltin: boolean; + reason: string; +}; + +function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TelemetryActivationEvent { const reasonStr = reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : reason instanceof ExtensionActivatedByAPI ? 'api' : ''; - - /* __GDPR__FRAGMENT__ - "TelemetryActivationEvent" : { - "id": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "extensionVersion": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "activationEvents": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ const event = { id: extensionDescription.identifier.value, name: extensionDescription.name, diff --git a/src/vs/workbench/services/extensions/worker/extensionHostMain.ts b/src/vs/workbench/services/extensions/worker/extensionHostMain.ts index b8992b876d0e6..54094e1254721 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostMain.ts @@ -5,8 +5,7 @@ import { timeout } from 'vs/base/common/async'; import * as errors from 'vs/base/common/errors'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Counter } from 'vs/base/common/numbers'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri'; import { IURITransformer } from 'vs/base/common/uriIpc'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; @@ -41,10 +40,7 @@ export class ExtensionHostMain { private _isTerminating: boolean; private readonly _hostUtils: IHostUtils; private readonly _extensionService: ExtHostExtensionService; - private readonly _extHostLogService: ExtHostLogService; - private disposables: IDisposable[] = []; - - private _searchRequestIdProvider: Counter; + private readonly _disposables = new DisposableStore(); constructor( protocol: IMessagePassingProtocol, @@ -59,20 +55,19 @@ export class ExtensionHostMain { const rpcProtocol = new RPCProtocol(protocol, null, uriTransformer); // ensure URIs are transformed and revived - initData = this.transform(initData, rpcProtocol); + initData = ExtensionHostMain._transform(initData, rpcProtocol); // allow to patch console consolePatchFn(rpcProtocol.getProxy(MainContext.MainThreadConsole)); // services - this._extHostLogService = new ExtHostLogService(logServiceFn(initData), initData.logsLocation.fsPath); - this.disposables.push(this._extHostLogService); + const extHostLogService = new ExtHostLogService(logServiceFn(initData), initData.logsLocation.fsPath); + this._disposables.add(extHostLogService); - this._searchRequestIdProvider = new Counter(); - const extHostWorkspace = new ExtHostWorkspace(rpcProtocol, this._extHostLogService, this._searchRequestIdProvider, withNullAsUndefined(initData.workspace)); + const extHostWorkspace = new ExtHostWorkspace(rpcProtocol, extHostLogService, withNullAsUndefined(initData.workspace)); - this._extHostLogService.info('extension host started'); - this._extHostLogService.trace('initData', initData); + extHostLogService.info('extension host started'); + extHostLogService.trace('initData', initData); const extHostConfiguraiton = new ExtHostConfiguration(rpcProtocol.getProxy(MainContext.MainThreadConfiguration), extHostWorkspace); this._extensionService = new ExtHostExtensionService( @@ -82,7 +77,7 @@ export class ExtensionHostMain { extHostWorkspace, extHostConfiguraiton, initData.environment, - this._extHostLogService, + extHostLogService, uriTransformer ); @@ -127,7 +122,7 @@ export class ExtensionHostMain { } this._isTerminating = true; - this.disposables = dispose(this.disposables); + this._disposables.dispose(); errors.setUnexpectedErrorHandler((err) => { // TODO: write to log once we have one @@ -141,7 +136,7 @@ export class ExtensionHostMain { }, 1000); } - private transform(initData: IInitData, rpcProtocol: RPCProtocol): IInitData { + private static _transform(initData: IInitData, rpcProtocol: RPCProtocol): IInitData { initData.extensions.forEach((ext) => (ext).extensionLocation = URI.revive(rpcProtocol.transformIncomingURIs(ext.extensionLocation))); initData.environment.appRoot = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appRoot)); initData.environment.appSettingsHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appSettingsHome)); From 0db668ac8a9d8accf2b4807d7715dce7a30cc044 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 Aug 2019 10:16:31 +0200 Subject: [PATCH 040/613] tiny tweaks --- src/vs/base/common/worker/simpleWorker.ts | 5 ++--- src/vs/code/electron-browser/workbench/workbench.html | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index 6331cfb910e36..6c9815d3aaa67 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -12,8 +12,7 @@ const INITIALIZE = '$initialize'; export interface IWorker extends IDisposable { getId(): number; - postMessage(message: any, transfer?: Transferable[]): void; - dispose(): void; + postMessage(message: any, transfer: Transferable[]): void; } export interface IWorkerCallback { @@ -214,7 +213,7 @@ export class SimpleWorkerClient extends Disp )); this._protocol = new SimpleWorkerProtocol({ - sendMessage: (msg: any, transfer?: Transferable[]): void => { + sendMessage: (msg: any, transfer: ArrayBuffer[]): void => { this._worker.postMessage(msg, transfer); }, handleMessage: (method: string, args: any[]): Promise => { diff --git a/src/vs/code/electron-browser/workbench/workbench.html b/src/vs/code/electron-browser/workbench/workbench.html index 42a9682a00c3a..483d01493f57a 100644 --- a/src/vs/code/electron-browser/workbench/workbench.html +++ b/src/vs/code/electron-browser/workbench/workbench.html @@ -8,6 +8,6 @@ - - + + From bb27a11386906b272a98e3c730405fc30b98391b Mon Sep 17 00:00:00 2001 From: Yuya Tanaka Date: Sat, 27 Jul 2019 00:59:19 +0900 Subject: [PATCH 041/613] Fixes #77997: Let executeCodeActionProvider pass-through Selection obj. --- .../editor/contrib/codeAction/codeAction.ts | 16 +++++++++-- .../api/common/extHostApiCommands.ts | 8 ++++-- .../api/extHostApiCommands.test.ts | 28 +++++++++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 93873f17ffbcd..f706070a89bb7 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -117,8 +117,8 @@ function getCodeActionProviders( } registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise> { - const { resource, range, kind } = args; - if (!(resource instanceof URI) || !Range.isIRange(range)) { + const { resource, rangeOrSelection, kind } = args; + if (!(resource instanceof URI)) { throw illegalArgument(); } @@ -127,9 +127,19 @@ registerLanguageCommand('_executeCodeActionProvider', async function (accessor, throw illegalArgument(); } + const validatedRangeOrSelection = Selection.isISelection(rangeOrSelection) + ? Selection.liftSelection(rangeOrSelection) + : Range.isIRange(rangeOrSelection) + ? model.validateRange(rangeOrSelection) + : undefined; + + if (!validatedRangeOrSelection) { + throw illegalArgument(); + } + const codeActionSet = await getCodeActions( model, - model.validateRange(range), + validatedRangeOrSelection, { type: 'manual', filter: { includeSourceActions: true, kind: kind && kind.value ? new CodeActionKind(kind.value) : undefined } }, CancellationToken.None); diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index ff2a106f427f2..2a95e81c5e1fe 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -135,7 +135,7 @@ export class ExtHostApiCommands { description: 'Execute code action provider.', args: [ { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'range', description: 'Range in a text document', constraint: types.Range }, + { name: 'rangeOrSelection', description: 'Range in a text document. Some refactoring provider requires Selection object.', constraint: types.Range }, { name: 'kind', description: '(optional) Code action kind to return code actions for', constraint: (value: any) => !value || typeof value.value === 'string' }, ], returns: 'A promise that resolves to an array of Command-instances.' @@ -480,10 +480,12 @@ export class ExtHostApiCommands { }); } - private _executeCodeActionProvider(resource: URI, range: types.Range, kind?: string): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> { + private _executeCodeActionProvider(resource: URI, rangeOrSelection: types.Range | types.Selection, kind?: string): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> { const args = { resource, - range: typeConverters.Range.from(range), + rangeOrSelection: types.Selection.isSelection(rangeOrSelection) + ? typeConverters.Selection.from(rangeOrSelection) + : typeConverters.Range.from(rangeOrSelection), kind }; return this._commands.executeCommand('_executeCodeActionProvider', args) diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index 779c40e96562f..8e1bd39fdef27 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -631,6 +631,34 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); + test('vscode.executeCodeActionProvider passes Range to provider although Selection is passed in #77997', function () { + disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, { + provideCodeActions(document, rangeOrSelection): vscode.CodeAction[] { + return [{ + command: { + arguments: [document, rangeOrSelection], + command: 'command', + title: 'command_title', + }, + kind: types.CodeActionKind.Empty.append('foo'), + title: 'title', + }]; + } + })); + + const selection = new types.Selection(0, 0, 1, 1); + + return rpcProtocol.sync().then(() => { + return commands.executeCommand('vscode.executeCodeActionProvider', model.uri, selection).then(value => { + assert.equal(value.length, 1); + const [first] = value; + assert.ok(first.command); + assert.ok(first.command!.arguments![1] instanceof types.Selection); + assert.ok(first.command!.arguments![1].isEqual(selection)); + }); + }); + }); + // --- code lens test('CodeLens, back and forth', function () { From 46c702b118f59eefbf83104943d76266a8c0c809 Mon Sep 17 00:00:00 2001 From: Yuya Tanaka Date: Mon, 29 Jul 2019 23:20:40 +0900 Subject: [PATCH 042/613] Fixes #78098: Return isPreferred from vscode.executeCodeActionProvider --- .../api/common/extHostApiCommands.ts | 1 + src/vs/workbench/api/common/extHostTypes.ts | 2 ++ .../api/extHostApiCommands.test.ts | 27 +++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 2a95e81c5e1fe..d84da000c3fdd 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -506,6 +506,7 @@ export class ExtHostApiCommands { if (codeAction.command) { ret.command = this._commands.converter.fromInternal(codeAction.command); } + ret.isPreferred = codeAction.isPreferred; return ret; } })); diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 9ad1d38f66e9f..0f1da741909c5 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1075,6 +1075,8 @@ export class CodeAction { kind?: CodeActionKind; + isPreferred?: boolean; + constructor(title: string, kind?: CodeActionKind) { this.title = title; this.kind = kind; diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index 8e1bd39fdef27..048070561f2db 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -659,6 +659,33 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); + test('vscode.executeCodeActionProvider results seem to be missing their `isPreferred` property #78098', function () { + disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, { + provideCodeActions(document, rangeOrSelection): vscode.CodeAction[] { + return [{ + command: { + arguments: [document, rangeOrSelection], + command: 'command', + title: 'command_title', + }, + kind: types.CodeActionKind.Empty.append('foo'), + title: 'title', + isPreferred: true + }]; + } + })); + + const selection = new types.Selection(0, 0, 1, 1); + + return rpcProtocol.sync().then(() => { + return commands.executeCommand('vscode.executeCodeActionProvider', model.uri, selection).then(value => { + assert.equal(value.length, 1); + const [first] = value; + assert.equal(first.isPreferred, true); + }); + }); + }); + // --- code lens test('CodeLens, back and forth', function () { From 1556800102ab0d44f2dad97d59da0246fb4fb9c6 Mon Sep 17 00:00:00 2001 From: skprabhanjan Date: Thu, 8 Aug 2019 16:14:23 +0530 Subject: [PATCH 043/613] Fix fix-64077 With a setting --- extensions/git/package.json | 9 +++++++++ extensions/git/package.nls.json | 1 + extensions/git/src/git.ts | 11 +++++++++-- extensions/git/src/repository.ts | 7 +++++-- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index aba696781b6bb..3e0e7d2e4d473 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1334,6 +1334,15 @@ "scope": "resource", "default": false, "description": "%config.supportCancellation%" + }, + "git.sortOrderForBranch": { + "type": "string", + "enum": [ + "committerdate", + "alphabetically" + ], + "default": "committerdate", + "description": "%config.sortOrderForBranch%" } } }, diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index cddbdad605e50..7086508a13768 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -123,6 +123,7 @@ "config.confirmForcePush": "Controls whether to ask for confirmation before force-pushing.", "config.openDiffOnClick": "Controls whether the diff editor should be opened when clicking a change. Otherwise the regular editor will be opened.", "config.supportCancellation": "Controls whether a notification comes up when running the Sync action, which allows the user to cancel the operation.", + "config.sortOrderForBranch": "Controls the sort order for branches", "colors.added": "Color for added resources.", "colors.modified": "Color for modified resources.", "colors.deleted": "Color for deleted resources.", diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index b306cab125568..9b5ba8a9eca89 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1610,8 +1610,15 @@ export class Repository { .map(([ref]) => ({ name: ref, type: RefType.Head } as Branch)); } - async getRefs(): Promise { - const result = await this.run(['for-each-ref', '--format', '%(refname) %(objectname)', '--sort', '-committerdate']); + async getRefs(sortBranchListByCommitterDate?: Boolean): Promise { + const args = ['for-each-ref', '--format', '%(refname) %(objectname)']; + + if (sortBranchListByCommitterDate) { + args.push('--sort'); + args.push('-committerdate'); + } + + const result = await this.run(args); const fn = (line: string): Ref | null => { let match: RegExpExecArray | null; diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index a4b37bebd5eb2..ec81987241c21 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -698,6 +698,9 @@ export class Repository implements Disposable { onConfigListener(updateIndexGroupVisibility, this, this.disposables); updateIndexGroupVisibility(); + const onConfigListenerForBranchSortOrder = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.sortOrderForBranch', root)); + onConfigListenerForBranchSortOrder(this.updateModelState, this, this.disposables); + this.mergeGroup.hideWhenEmpty = true; this.disposables.push(this.mergeGroup); @@ -1399,7 +1402,7 @@ export class Repository implements Disposable { const config = workspace.getConfiguration('git'); const shouldIgnore = config.get('ignoreLimitWarning') === true; const useIcons = !config.get('decorations.enabled', true); - + const sortBranchListByCommitterDate = config.get('sortOrderForBranch') === 'committerdate'; this.isRepositoryHuge = didHitLimit; if (didHitLimit && !shouldIgnore && !this.didWarnAboutLimit) { @@ -1449,7 +1452,7 @@ export class Repository implements Disposable { // noop } - const [refs, remotes, submodules, rebaseCommit] = await Promise.all([this.repository.getRefs(), this.repository.getRemotes(), this.repository.getSubmodules(), this.getRebaseCommit()]); + const [refs, remotes, submodules, rebaseCommit] = await Promise.all([this.repository.getRefs(sortBranchListByCommitterDate), this.repository.getRemotes(), this.repository.getSubmodules(), this.getRebaseCommit()]); this._HEAD = HEAD; this._refs = refs; From f1aa0929db61ae84f8677210d44b0dbc09d6a280 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 9 Aug 2019 16:10:50 +0200 Subject: [PATCH 044/613] adopt ext host service world, add dummy implementations for some services --- .../api/common/extHostExtensionService.ts | 3 +- .../api/worker/extHostExtensionService.ts | 55 + .../api/worker/extHostStoragePaths.ts | 23 + .../extensions/worker/extHost.api.impl.ts | 964 ------------------ .../extensions/worker/extHost.services.ts | 51 + .../worker/extHostExtensionService.ts | 826 --------------- .../extensions/worker/extensionHostMain.ts | 154 --- .../extensions/worker/extensionHostWorker.ts | 10 +- 8 files changed, 137 insertions(+), 1949 deletions(-) create mode 100644 src/vs/workbench/api/worker/extHostExtensionService.ts create mode 100644 src/vs/workbench/api/worker/extHostStoragePaths.ts delete mode 100644 src/vs/workbench/services/extensions/worker/extHost.api.impl.ts create mode 100644 src/vs/workbench/services/extensions/worker/extHost.services.ts delete mode 100644 src/vs/workbench/services/extensions/worker/extHostExtensionService.ts delete mode 100644 src/vs/workbench/services/extensions/worker/extensionHostMain.ts diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 58098d94099c7..cd25ceb4bbadb 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -385,7 +385,8 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio try { activationTimesBuilder.activateCallStart(); logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`); - const activateResult: Promise = extensionModule.activate.apply(global, [context]); + const scope = typeof global === 'object' ? global : self; //todo@joh not so nice + const activateResult: Promise = extensionModule.activate.apply(scope, [context]); activationTimesBuilder.activateCallStop(); activationTimesBuilder.activateResolveStart(); diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts new file mode 100644 index 0000000000000..30d15e39399b0 --- /dev/null +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl'; +import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator'; +import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; +import { endsWith } from 'vs/base/common/strings'; +import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; + +export class ExtHostExtensionService extends AbstractExtHostExtensionService { + + protected async _beforeAlmostReadyToRunExtensions(): Promise { + // initialize API and register actors + const factory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors); + + // globally define the vscode module and share that for all extensions + // todo@joh have an instance per extension, not a shared one.... + const sharedApiInstance = factory(nullExtensionDescription, this._registry, await this._extHostConfiguration.getConfigProvider()); + define('vscode', sharedApiInstance); + } + + protected _loadCommonJSModule(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + // fake commonjs world + const module = { exports: {} }; + //@ts-ignore + self['module'] = module; + //@ts-ignore + self['exports'] = module.exports; + // that's improper but might help extensions that aren't author correctly + // @ts-ignore + self['window'] = self; + + try { + activationTimesBuilder.codeLoadingStart(); + // import the single (!) script, make sure it's a JS-file + const suffix = '.js'; + if (endsWith(modulePath, suffix)) { + importScripts(modulePath); + } else { + importScripts(modulePath + suffix); + } + } finally { + activationTimesBuilder.codeLoadingStop(); + } + + // return what it exported + return Promise.resolve(module.exports as T); + } + + public async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise { + throw new Error('Not supported'); + } +} diff --git a/src/vs/workbench/api/worker/extHostStoragePaths.ts b/src/vs/workbench/api/worker/extHostStoragePaths.ts new file mode 100644 index 0000000000000..3d07dff7de05f --- /dev/null +++ b/src/vs/workbench/api/worker/extHostStoragePaths.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; + +export class ExtensionStoragePaths implements IExtensionStoragePaths { + + readonly _serviceBrand: undefined; + + readonly whenReady: Promise = Promise.resolve(); + + //todo@joh -> this isn't proper but also hard to get right... + workspaceValue(_extension: IExtensionDescription): string | undefined { + return ''; + } + + globalValue(_extension: IExtensionDescription): string { + return ''; + } +} diff --git a/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts b/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts deleted file mode 100644 index e0c1f3ad4d1da..0000000000000 --- a/src/vs/workbench/services/extensions/worker/extHost.api.impl.ts +++ /dev/null @@ -1,964 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import * as errors from 'vs/base/common/errors'; -import { Emitter, Event } from 'vs/base/common/event'; -import * as path from 'vs/base/common/path'; -import Severity from 'vs/base/common/severity'; -import { URI } from 'vs/base/common/uri'; -import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; -import { OverviewRulerLane } from 'vs/editor/common/model'; -import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; -import { score } from 'vs/editor/common/modes/languageSelector'; -import * as files from 'vs/platform/files/common/files'; -import { ExtHostContext, IInitData, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands'; -import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard'; -import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { ExtHostComments } from 'vs/workbench/api/common/extHostComments'; -import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; -// import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService'; -import { ExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations'; -import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics'; -import { ExtHostDialogs } from 'vs/workbench/api/common/extHostDialogs'; -import { ExtHostDocumentContentProvider } from 'vs/workbench/api/common/extHostDocumentContentProviders'; -import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostDocumentSaveParticipant'; -import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; -import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; -import { ExtensionActivatedByAPI } from 'vs/workbench/api/common/extHostExtensionActivator'; -import { ExtHostExtensionService } from './extHostExtensionService'; -import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem'; -import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService'; -import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures'; -import { ExtHostLanguages } from 'vs/workbench/api/common/extHostLanguages'; -import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; -import { ExtHostMessageService } from 'vs/workbench/api/common/extHostMessageService'; -import { ExtHostOutputService, PushOutputChannelFactory } from 'vs/workbench/api/common/extHostOutput'; -// import { LogOutputChannelFactory } from 'vs/workbench/api/node/extHostOutputService'; -import { ExtHostProgress } from 'vs/workbench/api/common/extHostProgress'; -import { ExtHostQuickOpen } from 'vs/workbench/api/common/extHostQuickOpen'; -import { ExtHostSCM } from 'vs/workbench/api/common/extHostSCM'; -// import { ExtHostSearch, registerEHSearchProviders } from 'vs/workbench/api/node/extHostSearch'; -import { ExtHostStatusBar } from 'vs/workbench/api/common/extHostStatusBar'; -import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; -// import { ExtHostTask } from 'vs/workbench/api/node/extHostTask'; -// import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService'; -import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors'; -import { ExtHostTreeViews } from 'vs/workbench/api/common/extHostTreeViews'; -// import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadService'; -import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; -import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; -import { ExtHostUrls } from 'vs/workbench/api/common/extHostUrls'; -import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; -import { ExtHostWindow } from 'vs/workbench/api/common/extHostWindow'; -import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; -import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; -import * as vscode from 'vscode'; -import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { originalFSPath } from 'vs/base/common/resources'; -// import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer'; -import { values } from 'vs/base/common/collections'; -// import { Schemas } from 'vs/base/common/network'; -import { IURITransformer } from 'vs/base/common/uriIpc'; -import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets'; -import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService'; -// import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -// import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -// import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions'; -import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; - -export interface IExtensionApiFactory { - (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; -} - -function proposedApiFunction(extension: IExtensionDescription, fn: T): T { - if (extension.enableProposedApi) { - return fn; - } else { - return throwProposedApiError.bind(null, extension) as any as T; - } -} - -/** - * This method instantiates and returns the extension API surface - */ -export function createApiFactory( - initData: IInitData, - rpcProtocol: IMainContext, - extHostWorkspace: ExtHostWorkspace, - extHostConfiguration: ExtHostConfiguration, - extensionService: ExtHostExtensionService, - extHostLogService: ExtHostLogService, - extHostStorage: ExtHostStorage, - uriTransformer: IURITransformer | null -): IExtensionApiFactory { - - // bootstrap services - // const services = new ServiceCollection(...getSingletonServiceDescriptors()); - // const instaService = new InstantiationService(services); - - // Addressable instances - rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService); - const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, new ExtHostDecorations(rpcProtocol)); - const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment)); - const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol)); - const extHostDocumentsAndEditors = rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(rpcProtocol)); - const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors)); - const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService)); - const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadTextEditors))); - const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors)); - const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostLogService)); - const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService)); - // rpcProtocol.set(ExtHostContext.ExtHostDownloadService, new ExtHostDownloadService(rpcProtocol.getProxy(MainContext.MainThreadDownloadService), extHostCommands)); - rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace); - rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration); - const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment)); - const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol)); - const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService)); - const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures)); - const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors)); - const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands)); - // const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostWorkspace, extHostDocumentsAndEditors, extHostLogService)); - // const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, instaService.createInstance(ExtHostDebugService, rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands)); - const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService)); - const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments)); - // const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, uriTransformer, extHostLogService)); - // const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService)); - const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol)); - rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService); - const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress))); - const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, new ExtHostOutputService(PushOutputChannelFactory, initData.logsLocation, rpcProtocol)); - rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage); - const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol)); - - // if (initData.remote.isRemote && initData.remote.authority) { - // extHostTask.registerTaskSystem(Schemas.vscodeRemote, { - // scheme: Schemas.vscodeRemote, - // authority: initData.remote.authority, - // platform: process.platform - // }); - - // registerEHSearchProviders(extHostSearch, extHostLogService); - - // const cliServer = new CLIServer(extHostCommands); - // process.env['VSCODE_IPC_HOOK_CLI'] = cliServer.ipcHandlePath; - // } - - // todo@joh - const proxy: any = new Proxy({}, {}); - rpcProtocol.set(ExtHostContext.ExtHostTerminalService, proxy); - rpcProtocol.set(ExtHostContext.ExtHostDebugService, proxy); - rpcProtocol.set(ExtHostContext.ExtHostSearch, proxy); - rpcProtocol.set(ExtHostContext.ExtHostTask, proxy); - rpcProtocol.set(ExtHostContext.ExtHostDownloadService, proxy); - - // Check that no named customers are missing - const expected: ProxyIdentifier[] = values(ExtHostContext); - rpcProtocol.assertRegistered(expected); - - // Other instances - const extHostClipboard = new ExtHostClipboard(rpcProtocol); - const extHostMessageService = new ExtHostMessageService(rpcProtocol); - const extHostDialogs = new ExtHostDialogs(rpcProtocol); - const extHostStatusBar = new ExtHostStatusBar(rpcProtocol); - const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments); - - // Register an output channel for exthost log - const outputChannelName = initData.remote.isRemote ? nls.localize('remote extension host Log', "Remote Extension Host") : nls.localize('extension host Log', "Extension Host"); - extHostOutputService.createOutputChannelFromLogFile(outputChannelName, extHostLogService.logFile); - - // Register API-ish commands - ExtHostApiCommands.register(extHostCommands); - - return function (extension: IExtensionDescription, extensionRegistry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode { - - // Check document selectors for being overly generic. Technically this isn't a problem but - // in practice many extensions say they support `fooLang` but need fs-access to do so. Those - // extension should specify then the `file`-scheme, e.g. `{ scheme: 'fooLang', language: 'fooLang' }` - // We only inform once, it is not a warning because we just want to raise awareness and because - // we cannot say if the extension is doing it right or wrong... - const checkSelector = (function () { - let done = (!extension.isUnderDevelopment); - function informOnce(selector: vscode.DocumentSelector) { - if (!done) { - console.info(`Extension '${extension.identifier.value}' uses a document selector without scheme. Learn more about this: https://go.microsoft.com/fwlink/?linkid=872305`); - done = true; - } - } - return function perform(selector: vscode.DocumentSelector): vscode.DocumentSelector { - if (Array.isArray(selector)) { - selector.forEach(perform); - } else if (typeof selector === 'string') { - informOnce(selector); - } else { - if (typeof selector.scheme === 'undefined') { - informOnce(selector); - } - if (!extension.enableProposedApi && typeof selector.exclusive === 'boolean') { - throwProposedApiError(extension); - } - } - return selector; - }; - })(); - - - // namespace: commands - const commands: typeof vscode.commands = { - registerCommand(id: string, command: (...args: any[]) => T | Thenable, thisArgs?: any): vscode.Disposable { - return extHostCommands.registerCommand(true, id, command, thisArgs); - }, - registerTextEditorCommand(id: string, callback: (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit, ...args: any[]) => void, thisArg?: any): vscode.Disposable { - return extHostCommands.registerCommand(true, id, (...args: any[]): any => { - const activeTextEditor = extHostEditors.getActiveTextEditor(); - if (!activeTextEditor) { - console.warn('Cannot execute ' + id + ' because there is no active text editor.'); - return undefined; - } - - return activeTextEditor.edit((edit: vscode.TextEditorEdit) => { - args.unshift(activeTextEditor, edit); - callback.apply(thisArg, args); - - }).then((result) => { - if (!result) { - console.warn('Edits from command ' + id + ' were not applied.'); - } - }, (err) => { - console.warn('An error occurred while running command ' + id, err); - }); - }); - }, - registerDiffInformationCommand: proposedApiFunction(extension, (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => { - return extHostCommands.registerCommand(true, id, async (...args: any[]): Promise => { - const activeTextEditor = extHostEditors.getActiveTextEditor(); - if (!activeTextEditor) { - console.warn('Cannot execute ' + id + ' because there is no active text editor.'); - return undefined; - } - - const diff = await extHostEditors.getDiffInformation(activeTextEditor.id); - callback.apply(thisArg, [diff, ...args]); - }); - }), - executeCommand(id: string, ...args: any[]): Thenable { - return extHostCommands.executeCommand(id, ...args); - }, - 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 - const env: typeof vscode.env = { - get machineId() { return initData.telemetryInfo.machineId; }, - get sessionId() { return initData.telemetryInfo.sessionId; }, - get language() { return initData.environment.appLanguage; }, - get appName() { return initData.environment.appName; }, - get appRoot() { return initData.environment.appRoot!.fsPath; }, - get uriScheme() { return initData.environment.appUriScheme; }, - get logLevel() { - checkProposedApiEnabled(extension); - return typeConverters.LogLevel.to(extHostLogService.getLevel()); - }, - get onDidChangeLogLevel() { - checkProposedApiEnabled(extension); - return Event.map(extHostLogService.onDidChangeLogLevel, l => typeConverters.LogLevel.to(l)); - }, - get clipboard(): vscode.Clipboard { - return extHostClipboard; - }, - get shell(): string { - throw new Error('not implemented'); - // return extHostTerminalService.getDefaultShell(configProvider); - }, - openExternal(uri: URI) { - return extHostWindow.openUri(uri, { allowTunneling: !!initData.remote.isRemote }); - }, - get remoteName() { - return getRemoteName(initData.remote.authority); - } - }; - if (!initData.environment.extensionTestsLocationURI) { - // allow to patch env-function when running tests - Object.freeze(env); - } - - const extensionKind = initData.remote.isRemote - ? extHostTypes.ExtensionKind.Workspace - : extHostTypes.ExtensionKind.UI; - - // namespace: extensions - const extensions: typeof vscode.extensions = { - getExtension(extensionId: string): Extension | undefined { - const desc = extensionRegistry.getExtensionDescription(extensionId); - if (desc) { - return new Extension(extensionService, desc, extensionKind); - } - return undefined; - }, - get all(): Extension[] { - return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc, extensionKind)); - }, - get onDidChange() { - return extensionRegistry.onDidChange; - } - }; - - // namespace: languages - const languages: typeof vscode.languages = { - createDiagnosticCollection(name?: string): vscode.DiagnosticCollection { - return extHostDiagnostics.createDiagnosticCollection(name); - }, - get onDidChangeDiagnostics() { - return extHostDiagnostics.onDidChangeDiagnostics; - }, - getDiagnostics: (resource?: vscode.Uri) => { - return extHostDiagnostics.getDiagnostics(resource); - }, - getLanguages(): Thenable { - return extHostLanguages.getLanguages(); - }, - setTextDocumentLanguage(document: vscode.TextDocument, languageId: string): Thenable { - return extHostLanguages.changeLanguage(document.uri, languageId); - }, - match(selector: vscode.DocumentSelector, document: vscode.TextDocument): number { - return score(typeConverters.LanguageSelector.from(selector), document.uri, document.languageId, true); - }, - registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { - return extHostLanguageFeatures.registerCodeActionProvider(extension, checkSelector(selector), provider, metadata); - }, - registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { - return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider); - }, - registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider); - }, - registerDeclarationProvider(selector: vscode.DocumentSelector, provider: vscode.DeclarationProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDeclarationProvider(extension, checkSelector(selector), provider); - }, - registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable { - return extHostLanguageFeatures.registerImplementationProvider(extension, checkSelector(selector), provider); - }, - registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable { - return extHostLanguageFeatures.registerTypeDefinitionProvider(extension, checkSelector(selector), provider); - }, - registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable { - return extHostLanguageFeatures.registerHoverProvider(extension, checkSelector(selector), provider, extension.identifier); - }, - registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider); - }, - registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable { - return extHostLanguageFeatures.registerReferenceProvider(extension, checkSelector(selector), provider); - }, - registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable { - return extHostLanguageFeatures.registerRenameProvider(extension, checkSelector(selector), provider); - }, - registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, metadata?: vscode.DocumentSymbolProviderMetadata): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentSymbolProvider(extension, checkSelector(selector), provider, metadata); - }, - registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable { - return extHostLanguageFeatures.registerWorkspaceSymbolProvider(extension, provider); - }, - registerDocumentFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentFormattingEditProvider(extension, checkSelector(selector), provider); - }, - registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentRangeFormattingEditProvider(extension, checkSelector(selector), provider); - }, - registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacters: string[]): vscode.Disposable { - return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(extension, checkSelector(selector), provider, [firstTriggerCharacter].concat(moreTriggerCharacters)); - }, - registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, firstItem?: string | vscode.SignatureHelpProviderMetadata, ...remaining: string[]): vscode.Disposable { - if (typeof firstItem === 'object') { - return extHostLanguageFeatures.registerSignatureHelpProvider(extension, checkSelector(selector), provider, firstItem); - } - return extHostLanguageFeatures.registerSignatureHelpProvider(extension, checkSelector(selector), provider, typeof firstItem === 'undefined' ? [] : [firstItem, ...remaining]); - }, - registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable { - return extHostLanguageFeatures.registerCompletionItemProvider(extension, checkSelector(selector), provider, triggerCharacters); - }, - registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentLinkProvider(extension, checkSelector(selector), provider); - }, - registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { - return extHostLanguageFeatures.registerColorProvider(extension, checkSelector(selector), provider); - }, - registerFoldingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { - return extHostLanguageFeatures.registerFoldingRangeProvider(extension, checkSelector(selector), provider); - }, - registerSelectionRangeProvider(selector: vscode.DocumentSelector, provider: vscode.SelectionRangeProvider): vscode.Disposable { - return extHostLanguageFeatures.registerSelectionRangeProvider(extension, selector, provider); - }, - registerCallHierarchyProvider(selector: vscode.DocumentSelector, provider: vscode.CallHierarchyItemProvider): vscode.Disposable { - checkProposedApiEnabled(extension); - return extHostLanguageFeatures.registerCallHierarchyProvider(extension, selector, provider); - }, - setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => { - return extHostLanguageFeatures.setLanguageConfiguration(language, configuration); - } - }; - - // namespace: window - const window: typeof vscode.window = { - get activeTextEditor() { - return extHostEditors.getActiveTextEditor(); - }, - get visibleTextEditors() { - return extHostEditors.getVisibleTextEditors(); - }, - get activeTerminal(): vscode.Terminal { - throw new Error('not implemented'); - // return extHostTerminalService.activeTerminal; - }, - get terminals(): vscode.Terminal[] { - throw new Error('not implemented'); - // return extHostTerminalService.terminals; - }, - showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Thenable { - let documentPromise: Promise; - if (URI.isUri(documentOrUri)) { - documentPromise = Promise.resolve(workspace.openTextDocument(documentOrUri)); - } else { - documentPromise = Promise.resolve(documentOrUri); - } - return documentPromise.then(document => { - return extHostEditors.showTextDocument(document, columnOrOptions, preserveFocus); - }); - }, - createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType { - return extHostEditors.createTextEditorDecorationType(options); - }, - onDidChangeActiveTextEditor(listener, thisArg?, disposables?) { - return extHostEditors.onDidChangeActiveTextEditor(listener, thisArg, disposables); - }, - onDidChangeVisibleTextEditors(listener, thisArg, disposables) { - return extHostEditors.onDidChangeVisibleTextEditors(listener, thisArg, disposables); - }, - onDidChangeTextEditorSelection(listener: (e: vscode.TextEditorSelectionChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) { - return extHostEditors.onDidChangeTextEditorSelection(listener, thisArgs, disposables); - }, - onDidChangeTextEditorOptions(listener: (e: vscode.TextEditorOptionsChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) { - return extHostEditors.onDidChangeTextEditorOptions(listener, thisArgs, disposables); - }, - onDidChangeTextEditorVisibleRanges(listener: (e: vscode.TextEditorVisibleRangesChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) { - return extHostEditors.onDidChangeTextEditorVisibleRanges(listener, thisArgs, disposables); - }, - onDidChangeTextEditorViewColumn(listener, thisArg?, disposables?) { - return extHostEditors.onDidChangeTextEditorViewColumn(listener, thisArg, disposables); - }, - onDidCloseTerminal(listener, thisArg?, disposables?) { - throw new Error('not implemented'); - // return extHostTerminalService.onDidCloseTerminal(listener, thisArg, disposables); - }, - onDidOpenTerminal(listener, thisArg?, disposables?) { - throw new Error('not implemented'); - // return extHostTerminalService.onDidOpenTerminal(listener, thisArg, disposables); - }, - onDidChangeActiveTerminal(listener, thisArg?, disposables?) { - throw new Error('not implemented'); - // return extHostTerminalService.onDidChangeActiveTerminal(listener, thisArg, disposables); - }, - onDidChangeTerminalDimensions(listener, thisArg?, disposables?) { - throw new Error('not implemented'); - // return extHostTerminalService.onDidChangeTerminalDimensions(listener, thisArg, disposables); - }, - get state() { - return extHostWindow.state; - }, - onDidChangeWindowState(listener, thisArg?, disposables?) { - return extHostWindow.onDidChangeWindowState(listener, thisArg, disposables); - }, - showInformationMessage(message: string, first: vscode.MessageOptions | string | vscode.MessageItem, ...rest: Array) { - return extHostMessageService.showMessage(extension, Severity.Info, message, first, rest); - }, - showWarningMessage(message: string, first: vscode.MessageOptions | string | vscode.MessageItem, ...rest: Array) { - return extHostMessageService.showMessage(extension, Severity.Warning, message, first, rest); - }, - showErrorMessage(message: string, first: vscode.MessageOptions | string | vscode.MessageItem, ...rest: Array) { - return extHostMessageService.showMessage(extension, Severity.Error, message, first, rest); - }, - showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken): any { - return extHostQuickOpen.showQuickPick(items, !!extension.enableProposedApi, options, token); - }, - showWorkspaceFolderPick(options: vscode.WorkspaceFolderPickOptions) { - return extHostQuickOpen.showWorkspaceFolderPick(options); - }, - showInputBox(options?: vscode.InputBoxOptions, token?: vscode.CancellationToken) { - return extHostQuickOpen.showInput(options, token); - }, - showOpenDialog(options) { - return extHostDialogs.showOpenDialog(options); - }, - showSaveDialog(options) { - return extHostDialogs.showSaveDialog(options); - }, - createStatusBarItem(alignmentOrOptions?: vscode.StatusBarAlignment | vscode.window.StatusBarItemOptions, priority?: number): vscode.StatusBarItem { - let id: string; - let name: string; - let alignment: number | undefined; - - if (alignmentOrOptions && typeof alignmentOrOptions !== 'number') { - id = alignmentOrOptions.id; - name = alignmentOrOptions.name; - alignment = alignmentOrOptions.alignment; - priority = alignmentOrOptions.priority; - } else { - id = extension.identifier.value; - name = nls.localize('extensionLabel', "{0} (Extension)", extension.displayName || extension.name); - alignment = alignmentOrOptions; - priority = priority; - } - - return extHostStatusBar.createStatusBarEntry(id, name, alignment, priority); - }, - setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable): vscode.Disposable { - return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable); - }, - withScmProgress(task: (progress: vscode.Progress) => Thenable) { - console.warn(`[Deprecation Warning] function 'withScmProgress' is deprecated and should no longer be used. Use 'withProgress' instead.`); - return extHostProgress.withProgress(extension, { location: extHostTypes.ProgressLocation.SourceControl }, (progress, token) => task({ report(n: number) { /*noop*/ } })); - }, - withProgress(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; worked?: number }>, token: vscode.CancellationToken) => Thenable) { - return extHostProgress.withProgress(extension, options, task); - }, - createOutputChannel(name: string): vscode.OutputChannel { - return extHostOutputService.createOutputChannel(name); - }, - createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel { - return extHostWebviews.createWebviewPanel(extension, viewType, title, showOptions, options); - }, - createWebviewTextEditorInset(editor: vscode.TextEditor, line: number, height: number, options: vscode.WebviewOptions): vscode.WebviewEditorInset { - checkProposedApiEnabled(extension); - return extHostEditorInsets.createWebviewEditorInset(editor, line, height, options, extension); - }, - createTerminal(nameOrOptions?: vscode.TerminalOptions | vscode.ExtensionTerminalOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal { - throw new Error('not implemented'); - // if (typeof nameOrOptions === 'object') { - // if ('pty' in nameOrOptions) { - // return extHostTerminalService.createExtensionTerminal(nameOrOptions); - // } - // return extHostTerminalService.createTerminalFromOptions(nameOrOptions); - // } - // return extHostTerminalService.createTerminal(nameOrOptions, shellPath, shellArgs); - }, - createTerminalRenderer(name: string): vscode.TerminalRenderer { - throw new Error('not implemented'); - // return extHostTerminalService.createTerminalRenderer(name); - }, - registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider): vscode.Disposable { - return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension); - }, - createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider }): vscode.TreeView { - return extHostTreeViews.createTreeView(viewId, options, extension); - }, - registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => { - return extHostWebviews.registerWebviewPanelSerializer(viewType, serializer); - }, - registerDecorationProvider: proposedApiFunction(extension, (provider: vscode.DecorationProvider) => { - return extHostDecorations.registerDecorationProvider(provider, extension.identifier); - }), - registerUriHandler(handler: vscode.UriHandler) { - return extHostUrls.registerUriHandler(extension.identifier, handler); - }, - createQuickPick(): vscode.QuickPick { - return extHostQuickOpen.createQuickPick(extension.identifier, !!extension.enableProposedApi); - }, - createInputBox(): vscode.InputBox { - return extHostQuickOpen.createInputBox(extension.identifier); - } - }; - - // namespace: workspace - const workspace: typeof vscode.workspace = { - get rootPath() { - return extHostWorkspace.getPath(); - }, - set rootPath(value) { - throw errors.readonly(); - }, - getWorkspaceFolder(resource) { - return extHostWorkspace.getWorkspaceFolder(resource); - }, - get workspaceFolders() { - return extHostWorkspace.getWorkspaceFolders(); - }, - get name() { - return extHostWorkspace.name; - }, - set name(value) { - throw errors.readonly(); - }, - get workspaceFile() { - return extHostWorkspace.workspaceFile; - }, - set workspaceFile(value) { - throw errors.readonly(); - }, - updateWorkspaceFolders: (index, deleteCount, ...workspaceFoldersToAdd) => { - return extHostWorkspace.updateWorkspaceFolders(extension, index, deleteCount || 0, ...workspaceFoldersToAdd); - }, - onDidChangeWorkspaceFolders: function (listener, thisArgs?, disposables?) { - return extHostWorkspace.onDidChangeWorkspace(listener, thisArgs, disposables); - }, - asRelativePath: (pathOrUri, includeWorkspace?) => { - return extHostWorkspace.getRelativePath(pathOrUri, includeWorkspace); - }, - findFiles: (include, exclude, maxResults?, token?) => { - // Note, undefined/null have different meanings on "exclude" - return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(exclude), maxResults, extension.identifier, token); - }, - findTextInFiles: (query: vscode.TextSearchQuery, optionsOrCallback: vscode.FindTextInFilesOptions | ((result: vscode.TextSearchResult) => void), callbackOrToken?: vscode.CancellationToken | ((result: vscode.TextSearchResult) => void), token?: vscode.CancellationToken) => { - let options: vscode.FindTextInFilesOptions; - let callback: (result: vscode.TextSearchResult) => void; - - if (typeof optionsOrCallback === 'object') { - options = optionsOrCallback; - callback = callbackOrToken as (result: vscode.TextSearchResult) => void; - } else { - options = {}; - callback = optionsOrCallback; - token = callbackOrToken as vscode.CancellationToken; - } - - return extHostWorkspace.findTextInFiles(query, options || {}, callback, extension.identifier, token); - }, - saveAll: (includeUntitled?) => { - return extHostWorkspace.saveAll(includeUntitled); - }, - applyEdit(edit: vscode.WorkspaceEdit): Thenable { - return extHostEditors.applyWorkspaceEdit(edit); - }, - createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): vscode.FileSystemWatcher => { - return extHostFileSystemEvent.createFileSystemWatcher(typeConverters.GlobPattern.from(pattern), ignoreCreate, ignoreChange, ignoreDelete); - }, - get textDocuments() { - return extHostDocuments.getAllDocumentData().map(data => data.document); - }, - set textDocuments(value) { - throw errors.readonly(); - }, - openTextDocument(uriOrFileNameOrOptions?: vscode.Uri | string | { language?: string; content?: string; }) { - let uriPromise: Thenable; - - const options = uriOrFileNameOrOptions as { language?: string; content?: string; }; - if (typeof uriOrFileNameOrOptions === 'string') { - uriPromise = Promise.resolve(URI.file(uriOrFileNameOrOptions)); - } else if (uriOrFileNameOrOptions instanceof URI) { - uriPromise = Promise.resolve(uriOrFileNameOrOptions); - } else if (!options || typeof options === 'object') { - uriPromise = extHostDocuments.createDocumentData(options); - } else { - throw new Error('illegal argument - uriOrFileNameOrOptions'); - } - - return uriPromise.then(uri => { - return extHostDocuments.ensureDocumentData(uri).then(() => { - return extHostDocuments.getDocument(uri); - }); - }); - }, - onDidOpenTextDocument: (listener, thisArgs?, disposables?) => { - return extHostDocuments.onDidAddDocument(listener, thisArgs, disposables); - }, - onDidCloseTextDocument: (listener, thisArgs?, disposables?) => { - return extHostDocuments.onDidRemoveDocument(listener, thisArgs, disposables); - }, - onDidChangeTextDocument: (listener, thisArgs?, disposables?) => { - return extHostDocuments.onDidChangeDocument(listener, thisArgs, disposables); - }, - onDidSaveTextDocument: (listener, thisArgs?, disposables?) => { - return extHostDocuments.onDidSaveDocument(listener, thisArgs, disposables); - }, - onWillSaveTextDocument: (listener, thisArgs?, disposables?) => { - return extHostDocumentSaveParticipant.getOnWillSaveTextDocumentEvent(extension)(listener, thisArgs, disposables); - }, - onDidChangeConfiguration: (listener: (_: any) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => { - return configProvider.onDidChangeConfiguration(listener, thisArgs, disposables); - }, - getConfiguration(section?: string, resource?: vscode.Uri): vscode.WorkspaceConfiguration { - resource = arguments.length === 1 ? undefined : resource; - return configProvider.getConfiguration(section, resource, extension.identifier); - }, - registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider) { - return extHostDocumentContentProviders.registerTextDocumentContentProvider(scheme, provider); - }, - registerTaskProvider: (type: string, provider: vscode.TaskProvider) => { - throw new Error('not implemented'); - // return extHostTask.registerTaskProvider(extension, type, provider); - }, - registerFileSystemProvider(scheme, provider, options) { - return extHostFileSystem.registerFileSystemProvider(scheme, provider, options); - }, - get fs() { - return extHostFileSystem.fileSystem; - }, - registerFileSearchProvider: proposedApiFunction(extension, (scheme: string, provider: vscode.FileSearchProvider) => { - throw new Error('not implemented'); - // return extHostSearch.registerFileSearchProvider(scheme, provider); - }), - registerTextSearchProvider: proposedApiFunction(extension, (scheme: string, provider: vscode.TextSearchProvider) => { - throw new Error('not implemented'); - // return extHostSearch.registerTextSearchProvider(scheme, provider); - }), - registerRemoteAuthorityResolver: proposedApiFunction(extension, (authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver) => { - return extensionService.registerRemoteAuthorityResolver(authorityPrefix, resolver); - }), - registerResourceLabelFormatter: proposedApiFunction(extension, (formatter: vscode.ResourceLabelFormatter) => { - return extHostLabelService.$registerResourceLabelFormatter(formatter); - }), - onDidRenameFile: proposedApiFunction(extension, (listener: (e: vscode.FileRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => { - return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables); - }), - onWillRenameFile: proposedApiFunction(extension, (listener: (e: vscode.FileWillRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => { - return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables); - }) - }; - - // namespace: scm - const scm: typeof vscode.scm = { - get inputBox() { - return extHostSCM.getLastInputBox(extension)!; // Strict null override - Deprecated api - }, - createSourceControl(id: string, label: string, rootUri?: vscode.Uri) { - return extHostSCM.createSourceControl(extension, id, label, rootUri); - } - }; - - const comment: typeof vscode.comments = { - createCommentController(id: string, label: string) { - return extHostComment.createCommentController(extension, id, label); - } - }; - - const comments = comment; - - // namespace: debug - // const debug: typeof vscode.debug = { - // get activeDebugSession() { - // return extHostDebugService.activeDebugSession; - // }, - // get activeDebugConsole() { - // return extHostDebugService.activeDebugConsole; - // }, - // get breakpoints() { - // return extHostDebugService.breakpoints; - // }, - // onDidStartDebugSession(listener, thisArg?, disposables?) { - // return extHostDebugService.onDidStartDebugSession(listener, thisArg, disposables); - // }, - // onDidTerminateDebugSession(listener, thisArg?, disposables?) { - // return extHostDebugService.onDidTerminateDebugSession(listener, thisArg, disposables); - // }, - // onDidChangeActiveDebugSession(listener, thisArg?, disposables?) { - // return extHostDebugService.onDidChangeActiveDebugSession(listener, thisArg, disposables); - // }, - // onDidReceiveDebugSessionCustomEvent(listener, thisArg?, disposables?) { - // return extHostDebugService.onDidReceiveDebugSessionCustomEvent(listener, thisArg, disposables); - // }, - // onDidChangeBreakpoints(listener, thisArgs?, disposables?) { - // return extHostDebugService.onDidChangeBreakpoints(listener, thisArgs, disposables); - // }, - // registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider) { - // return extHostDebugService.registerDebugConfigurationProvider(debugType, provider); - // }, - // registerDebugAdapterDescriptorFactory(debugType: string, factory: vscode.DebugAdapterDescriptorFactory) { - // return extHostDebugService.registerDebugAdapterDescriptorFactory(extension, debugType, factory); - // }, - // registerDebugAdapterTrackerFactory(debugType: string, factory: vscode.DebugAdapterTrackerFactory) { - // return extHostDebugService.registerDebugAdapterTrackerFactory(debugType, factory); - // }, - // startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession) { - // return extHostDebugService.startDebugging(folder, nameOrConfig, parentSession); - // }, - // addBreakpoints(breakpoints: vscode.Breakpoint[]) { - // return extHostDebugService.addBreakpoints(breakpoints); - // }, - // removeBreakpoints(breakpoints: vscode.Breakpoint[]) { - // return extHostDebugService.removeBreakpoints(breakpoints); - // } - // }; - - // const tasks: typeof vscode.tasks = { - // registerTaskProvider: (type: string, provider: vscode.TaskProvider) => { - // return extHostTask.registerTaskProvider(extension, type, provider); - // }, - // fetchTasks: (filter?: vscode.TaskFilter): Thenable => { - // return extHostTask.fetchTasks(filter); - // }, - // executeTask: (task: vscode.Task): Thenable => { - // return extHostTask.executeTask(extension, task); - // }, - // get taskExecutions(): vscode.TaskExecution[] { - // return extHostTask.taskExecutions; - // }, - // onDidStartTask: (listeners, thisArgs?, disposables?) => { - // return extHostTask.onDidStartTask(listeners, thisArgs, disposables); - // }, - // onDidEndTask: (listeners, thisArgs?, disposables?) => { - // return extHostTask.onDidEndTask(listeners, thisArgs, disposables); - // }, - // onDidStartTaskProcess: (listeners, thisArgs?, disposables?) => { - // return extHostTask.onDidStartTaskProcess(listeners, thisArgs, disposables); - // }, - // onDidEndTaskProcess: (listeners, thisArgs?, disposables?) => { - // return extHostTask.onDidEndTaskProcess(listeners, thisArgs, disposables); - // } - // }; - - return { - version: initData.version, - // namespaces - commands, - get debug(): typeof vscode.debug { throw new Error('not implemented'); }, - env, - extensions, - languages, - scm, - comment, - comments, - get tasks(): typeof vscode.tasks { throw new Error('not implemented'); }, - window, - workspace, - // types - Breakpoint: extHostTypes.Breakpoint, - CancellationTokenSource: CancellationTokenSource, - CodeAction: extHostTypes.CodeAction, - CodeActionKind: extHostTypes.CodeActionKind, - CodeActionTrigger: extHostTypes.CodeActionTrigger, - CodeLens: extHostTypes.CodeLens, - CodeInset: extHostTypes.CodeInset, - Color: extHostTypes.Color, - ColorInformation: extHostTypes.ColorInformation, - ColorPresentation: extHostTypes.ColorPresentation, - CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState, - CommentMode: extHostTypes.CommentMode, - CompletionItem: extHostTypes.CompletionItem, - CompletionItemKind: extHostTypes.CompletionItemKind, - CompletionList: extHostTypes.CompletionList, - CompletionTriggerKind: extHostTypes.CompletionTriggerKind, - ConfigurationTarget: extHostTypes.ConfigurationTarget, - DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable, - DebugAdapterServer: extHostTypes.DebugAdapterServer, - DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior, - Diagnostic: extHostTypes.Diagnostic, - DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation, - DiagnosticSeverity: extHostTypes.DiagnosticSeverity, - DiagnosticTag: extHostTypes.DiagnosticTag, - Disposable: extHostTypes.Disposable, - DocumentHighlight: extHostTypes.DocumentHighlight, - DocumentHighlightKind: extHostTypes.DocumentHighlightKind, - DocumentLink: extHostTypes.DocumentLink, - DocumentSymbol: extHostTypes.DocumentSymbol, - EndOfLine: extHostTypes.EndOfLine, - EventEmitter: Emitter, - ExtensionExecutionContext: extHostTypes.ExtensionExecutionContext, - ExtensionKind: extHostTypes.ExtensionKind, - CustomExecution: extHostTypes.CustomExecution, - CustomExecution2: extHostTypes.CustomExecution2, - FileChangeType: extHostTypes.FileChangeType, - FileSystemError: extHostTypes.FileSystemError, - FileType: files.FileType, - FoldingRange: extHostTypes.FoldingRange, - FoldingRangeKind: extHostTypes.FoldingRangeKind, - FunctionBreakpoint: extHostTypes.FunctionBreakpoint, - Hover: extHostTypes.Hover, - IndentAction: languageConfiguration.IndentAction, - Location: extHostTypes.Location, - LogLevel: extHostTypes.LogLevel, - MarkdownString: extHostTypes.MarkdownString, - OverviewRulerLane: OverviewRulerLane, - ParameterInformation: extHostTypes.ParameterInformation, - Position: extHostTypes.Position, - ProcessExecution: extHostTypes.ProcessExecution, - ProgressLocation: extHostTypes.ProgressLocation, - QuickInputButtons: extHostTypes.QuickInputButtons, - Range: extHostTypes.Range, - RelativePattern: extHostTypes.RelativePattern, - ResolvedAuthority: extHostTypes.ResolvedAuthority, - RemoteAuthorityResolverError: extHostTypes.RemoteAuthorityResolverError, - Selection: extHostTypes.Selection, - SelectionRange: extHostTypes.SelectionRange, - ShellExecution: extHostTypes.ShellExecution, - ShellQuoting: extHostTypes.ShellQuoting, - SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind, - SignatureHelp: extHostTypes.SignatureHelp, - SignatureInformation: extHostTypes.SignatureInformation, - SnippetString: extHostTypes.SnippetString, - SourceBreakpoint: extHostTypes.SourceBreakpoint, - SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType, - StatusBarAlignment: extHostTypes.StatusBarAlignment, - SymbolInformation: extHostTypes.SymbolInformation, - SymbolKind: extHostTypes.SymbolKind, - Task: extHostTypes.Task, - Task2: extHostTypes.Task, - TaskGroup: extHostTypes.TaskGroup, - TaskPanelKind: extHostTypes.TaskPanelKind, - TaskRevealKind: extHostTypes.TaskRevealKind, - TaskScope: extHostTypes.TaskScope, - TextDocumentSaveReason: extHostTypes.TextDocumentSaveReason, - TextEdit: extHostTypes.TextEdit, - TextEditorCursorStyle: TextEditorCursorStyle, - TextEditorLineNumbersStyle: extHostTypes.TextEditorLineNumbersStyle, - TextEditorRevealType: extHostTypes.TextEditorRevealType, - TextEditorSelectionChangeKind: extHostTypes.TextEditorSelectionChangeKind, - ThemeColor: extHostTypes.ThemeColor, - ThemeIcon: extHostTypes.ThemeIcon, - TreeItem: extHostTypes.TreeItem, - TreeItem2: extHostTypes.TreeItem, - TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState, - Uri: URI, - ViewColumn: extHostTypes.ViewColumn, - WorkspaceEdit: extHostTypes.WorkspaceEdit, - // proposed - CallHierarchyDirection: extHostTypes.CallHierarchyDirection, - CallHierarchyItem: extHostTypes.CallHierarchyItem - }; - }; -} - -class Extension implements vscode.Extension { - - private _extensionService: ExtHostExtensionService; - private _identifier: ExtensionIdentifier; - - readonly id: string; - readonly extensionPath: string; - readonly packageJSON: IExtensionDescription; - readonly extensionKind: vscode.ExtensionKind; - - constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription, kind: extHostTypes.ExtensionKind) { - this._extensionService = extensionService; - this._identifier = description.identifier; - this.id = description.identifier.value; - this.extensionPath = path.normalize(originalFSPath(description.extensionLocation)); - this.packageJSON = description; - this.extensionKind = kind; - } - - get isActive(): boolean { - return this._extensionService.isActivated(this._identifier); - } - - get exports(): T { - if (this.packageJSON.api === 'none') { - return undefined!; // Strict nulloverride - Public api - } - return this._extensionService.getExtensionExports(this._identifier); - } - - activate(): Thenable { - return this._extensionService.activateByIdWithErrors(this._identifier, new ExtensionActivatedByAPI(false)).then(() => this.exports); - } -} diff --git a/src/vs/workbench/services/extensions/worker/extHost.services.ts b/src/vs/workbench/services/extensions/worker/extHost.services.ts new file mode 100644 index 0000000000000..69ad6dbfe4bab --- /dev/null +++ b/src/vs/workbench/services/extensions/worker/extHost.services.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IExtHostOutputService, ExtHostOutputService } from 'vs/workbench/api/common/extHostOutput'; +import { IExtHostWorkspace, ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; +import { IExtHostDecorations, ExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations'; +import { IExtHostConfiguration, ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; +import { IExtHostCommands, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; +import { IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; +import { IExtHostTask } from 'vs/workbench/api/common/extHostTask'; +import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService'; +import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch'; +import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; +import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; +import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; +import { ExtHostExtensionService } from 'vs/workbench/api/worker/extHostExtensionService'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { ExtensionStoragePaths } from 'vs/workbench/api/worker/extHostStoragePaths'; + + +// register singleton services +registerSingleton(IExtHostOutputService, ExtHostOutputService); +registerSingleton(IExtHostWorkspace, ExtHostWorkspace); +registerSingleton(IExtHostDecorations, ExtHostDecorations); +registerSingleton(IExtHostConfiguration, ExtHostConfiguration); +registerSingleton(IExtHostCommands, ExtHostCommands); +registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors); +registerSingleton(IExtHostStorage, ExtHostStorage); +registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths); +registerSingleton(IExtHostExtensionService, ExtHostExtensionService); + +// register services that only throw errors +function NotImplementedProxy(name: ServiceIdentifier): { new(): T } { + return class { + constructor() { + return new Proxy({}, { + get(_target: any, prop: any) { + throw new Error(`Not Implemented: ${name}->${String(prop)}`); + } + }); + } + }; +} +registerSingleton(IExtHostTerminalService, class extends NotImplementedProxy(IExtHostTerminalService) { }); +registerSingleton(IExtHostTask, class extends NotImplementedProxy(IExtHostTask) { }); +registerSingleton(IExtHostDebugService, class extends NotImplementedProxy(IExtHostDebugService) { }); +registerSingleton(IExtHostSearch, class extends NotImplementedProxy(IExtHostSearch) { }); diff --git a/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts b/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts deleted file mode 100644 index 6e4cc7adb4264..0000000000000 --- a/src/vs/workbench/services/extensions/worker/extHostExtensionService.ts +++ /dev/null @@ -1,826 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import * as path from 'vs/base/common/path'; -import { originalFSPath } from 'vs/base/common/resources'; -import { Barrier } from 'vs/base/common/async'; -import { dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { TernarySearchTree } from 'vs/base/common/map'; -import { URI } from 'vs/base/common/uri'; -import { ILogService } from 'vs/platform/log/common/log'; -import { createApiFactory, IExtensionApiFactory } from './extHost.api.impl'; -// import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor'; -import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IResolveAuthorityResult } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; -import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator'; -import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; -import { ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; -import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { ExtensionActivationError, nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; -// import { connectProxyResolver } from 'vs/workbench/services/extensions/node/proxyResolver'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import * as errors from 'vs/base/common/errors'; -import * as vscode from 'vscode'; -import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { Schemas } from 'vs/base/common/network'; -// import { withNullAsUndefined } from 'vs/base/common/types'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento'; -// import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths'; -import { RemoteAuthorityResolverError, ExtensionExecutionContext } from 'vs/workbench/api/common/extHostTypes'; -import { IURITransformer } from 'vs/base/common/uriIpc'; -import { ResolvedAuthority, ResolvedOptions } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { endsWith } from 'vs/base/common/strings'; - -interface ITestRunner { - /** Old test runner API, as exported from `vscode/lib/testrunner` */ - run(testsRoot: string, clb: (error: Error, failures?: number) => void): void; -} - -interface INewTestRunner { - /** New test runner API, as explained in the extension test doc */ - run(): Promise; -} - -export interface IHostUtils { - exit(code?: number): void; - exists(path: string): Promise; - realpath(path: string): Promise; -} - -type TelemetryActivationEventFragment = { - id: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; - name: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; - extensionVersion: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; - publisherDisplayName: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - activationEvents: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - isBuiltin: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; - reason: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; -}; - -export class ExtHostExtensionService implements ExtHostExtensionServiceShape { - - private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000; - - private readonly _hostUtils: IHostUtils; - private readonly _initData: IInitData; - private readonly _extHostContext: IMainContext; - private readonly _extHostWorkspace: ExtHostWorkspace; - private readonly _extHostConfiguration: ExtHostConfiguration; - // private readonly _environment: IEnvironment; - private readonly _extHostLogService: ExtHostLogService; - - private readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape; - private readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape; - private readonly _mainThreadExtensionsProxy: MainThreadExtensionServiceShape; - - private readonly _almostReadyToRunExtensions: Barrier; - private readonly _readyToStartExtensionHost: Barrier; - private readonly _readyToRunExtensions: Barrier; - private readonly _registry: ExtensionDescriptionRegistry; - private readonly _storage: ExtHostStorage; - // private readonly _storagePath: ExtensionStoragePaths; - private readonly _activator: ExtensionsActivator; - private _extensionPathIndex: Promise> | null; - private readonly _extensionApiFactory: IExtensionApiFactory; - - private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver; }; - - private _started: boolean; - - private readonly _disposables: DisposableStore; - - constructor( - hostUtils: IHostUtils, - initData: IInitData, - extHostContext: IMainContext, - extHostWorkspace: ExtHostWorkspace, - extHostConfiguration: ExtHostConfiguration, - environment: IEnvironment, - extHostLogService: ExtHostLogService, - uriTransformer: IURITransformer | null - ) { - this._hostUtils = hostUtils; - this._initData = initData; - this._extHostContext = extHostContext; - this._extHostWorkspace = extHostWorkspace; - this._extHostConfiguration = extHostConfiguration; - // this._environment = environment; - this._extHostLogService = extHostLogService; - this._disposables = new DisposableStore(); - - this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace); - this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry); - this._mainThreadExtensionsProxy = this._extHostContext.getProxy(MainContext.MainThreadExtensionService); - - this._almostReadyToRunExtensions = new Barrier(); - this._readyToStartExtensionHost = new Barrier(); - this._readyToRunExtensions = new Barrier(); - this._registry = new ExtensionDescriptionRegistry(initData.extensions); - this._storage = new ExtHostStorage(this._extHostContext); - // this._storagePath = new ExtensionStoragePaths(withNullAsUndefined(initData.workspace), initData.environment); - - const hostExtensions = new Set(); - initData.hostExtensions.forEach((extensionId) => hostExtensions.add(ExtensionIdentifier.toKey(extensionId))); - - this._activator = new ExtensionsActivator(this._registry, initData.resolvedExtensions, initData.hostExtensions, { - onExtensionActivationError: (extensionId: ExtensionIdentifier, error: ExtensionActivationError): void => { - this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, error); - }, - - actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise => { - if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) { - const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); - await this._mainThreadExtensionsProxy.$activateExtension(extensionId, activationEvent); - return new HostExtension(); - } - const extensionDescription = this._registry.getExtensionDescription(extensionId)!; - return this._activateExtension(extensionDescription, reason); - } - }); - this._extensionPathIndex = null; - - // initialize API first (i.e. do not release barrier until the API is initialized) - this._extensionApiFactory = createApiFactory( - this._initData, - this._extHostContext, - this._extHostWorkspace, - this._extHostConfiguration, - this, - this._extHostLogService, - this._storage, - uriTransformer - ); - - this._resolvers = Object.create(null); - - this._started = false; - - this._initialize(); - - if (this._initData.autoStart) { - this._startExtensionHost(); - } - } - - private async _initialize(): Promise { - - // WORKER: globally define the vscode module and share that for all extensions - define('vscode', this._extensionApiFactory(nullExtensionDescription, this._registry, await this._extHostConfiguration.getConfigProvider())); - - try { - // const configProvider = await this._extHostConfiguration.getConfigProvider(); - // const extensionPaths = await this.getExtensionPathIndex(); - // NodeModuleRequireInterceptor.INSTANCE.register(new VSCodeNodeModuleFactory(this._extensionApiFactory, extensionPaths, this._registry, configProvider)); - // NodeModuleRequireInterceptor.INSTANCE.register(new KeytarNodeModuleFactory(this._extHostContext.getProxy(MainContext.MainThreadKeytar), this._environment)); - // if (this._initData.remoteAuthority) { - // NodeModuleRequireInterceptor.INSTANCE.register(new OpenNodeModuleFactory( - // this._extHostContext.getProxy(MainContext.MainThreadWindow), - // this._extHostContext.getProxy(MainContext.MainThreadTelemetry), - // extensionPaths - // )); - // } - - // // Do this when extension service exists, but extensions are not being activated yet. - // await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._extHostLogService, this._mainThreadTelemetryProxy); - this._almostReadyToRunExtensions.open(); - - await this._extHostWorkspace.waitForInitializeCall(); - this._readyToStartExtensionHost.open(); - } catch (err) { - errors.onUnexpectedError(err); - } - } - - public async deactivateAll(): Promise { - let allPromises: Promise[] = []; - try { - const allExtensions = this._registry.getAllExtensionDescriptions(); - const allExtensionsIds = allExtensions.map(ext => ext.identifier); - const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id)); - - allPromises = activatedExtensions.map((extensionId) => { - return this._deactivate(extensionId); - }); - } catch (err) { - // TODO: write to log once we have one - } - await allPromises; - } - - public isActivated(extensionId: ExtensionIdentifier): boolean { - if (this._readyToRunExtensions.isOpen()) { - return this._activator.isActivated(extensionId); - } - return false; - } - - private _activateByEvent(activationEvent: string, startup: boolean): Promise { - const reason = new ExtensionActivatedByEvent(startup, activationEvent); - return this._activator.activateByEvent(activationEvent, reason); - } - - private _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { - return this._activator.activateById(extensionId, reason); - } - - public activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { - return this._activateById(extensionId, reason).then(() => { - const extension = this._activator.getActivatedExtension(extensionId); - if (extension.activationFailed) { - // activation failed => bubble up the error as the promise result - return Promise.reject(extension.activationFailedError); - } - return undefined; - }); - } - - public getExtensionRegistry(): Promise { - return this._readyToRunExtensions.wait().then(_ => this._registry); - } - - public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined { - if (this._readyToRunExtensions.isOpen()) { - return this._activator.getActivatedExtension(extensionId).exports; - } else { - return null; - } - } - - // create trie to enable fast 'filename -> extension id' look up - public getExtensionPathIndex(): Promise> { - if (!this._extensionPathIndex) { - const tree = TernarySearchTree.forPaths(); - const extensions = this._registry.getAllExtensionDescriptions().map(ext => { - if (!ext.main) { - return undefined; - } - return this._hostUtils.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext)); - }); - this._extensionPathIndex = Promise.all(extensions).then(() => tree); - } - return this._extensionPathIndex; - } - - private _deactivate(extensionId: ExtensionIdentifier): Promise { - let result = Promise.resolve(undefined); - - if (!this._readyToRunExtensions.isOpen()) { - return result; - } - - if (!this._activator.isActivated(extensionId)) { - return result; - } - - const extension = this._activator.getActivatedExtension(extensionId); - if (!extension) { - return result; - } - - // call deactivate if available - try { - if (typeof extension.module.deactivate === 'function') { - result = Promise.resolve(extension.module.deactivate()).then(undefined, (err) => { - // TODO: Do something with err if this is not the shutdown case - return Promise.resolve(undefined); - }); - } - } catch (err) { - // TODO: Do something with err if this is not the shutdown case - } - - // clean up subscriptions - try { - dispose(extension.subscriptions); - } catch (err) { - // TODO: Do something with err if this is not the shutdown case - } - - return result; - } - - // --- impl - - private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { - this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier); - return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => { - const activationTimes = activatedExtension.activationTimes; - const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); - this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent); - this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes); - return activatedExtension; - }, (err) => { - this._logExtensionActivationTimes(extensionDescription, reason, 'failure'); - throw err; - }); - } - - private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) { - const event = getTelemetryActivationEvent(extensionDescription, reason); - type ExtensionActivationTimesClassification = { - outcome: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - } & TelemetryActivationEventFragment & ExtensionActivationTimesFragment; - - type ExtensionActivationTimesEvent = { - outcome: string - } & ActivationTimesEvent & TelemetryActivationEvent; - - type ActivationTimesEvent = { - startup?: boolean; - codeLoadingTime?: number; - activateCallTime?: number; - activateResolvedTime?: number; - }; - - this._mainThreadTelemetryProxy.$publicLog2('extensionActivationTimes', { - ...event, - ...(activationTimes || {}), - outcome - }); - } - - private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { - const event = getTelemetryActivationEvent(extensionDescription, reason); - type ActivatePluginClassification = {} & TelemetryActivationEventFragment; - this._mainThreadTelemetryProxy.$publicLog2('activatePlugin', event); - if (!extensionDescription.main) { - // Treat the extension as being empty => NOT AN ERROR CASE - return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE)); - } - - this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.identifier.value} ${JSON.stringify(reason)}`); - - const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); - return Promise.all([ - loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder), - this._loadExtensionContext(extensionDescription) - ]).then(values => { - return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder); - }); - } - - private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { - - const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage); - const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage); - - this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`); - return Promise.all([ - globalState.whenReady, - workspaceState.whenReady, - // this._storagePath.whenReady - ]).then(() => { - const that = this; - return Object.freeze({ - globalState, - workspaceState, - subscriptions: [], - get extensionPath() { return extensionDescription.extensionLocation.fsPath; }, - // storagePath: this._storagePath.workspaceValue(extensionDescription), - // globalStoragePath: this._storagePath.globalValue(extensionDescription), - get storagePath(): string { throw new Error('not impl'); },// this._storagePath.workspaceValue(extensionDescription), - get globalStoragePath(): string { throw new Error('not impl'); },// this._storagePath.globalValue(extensionDescription), - asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); }, - logPath: that._extHostLogService.getLogDirectory(extensionDescription.identifier), - executionContext: this._initData.remote.isRemote ? ExtensionExecutionContext.Remote : ExtensionExecutionContext.Local, - }); - }); - } - - private static _callActivate(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { - // Make sure the extension's surface is not undefined - extensionModule = extensionModule || { - activate: undefined, - deactivate: undefined - }; - - return this._callActivateOptional(logService, extensionId, extensionModule, context, activationTimesBuilder).then((extensionExports) => { - return new ActivatedExtension(false, null, activationTimesBuilder.build(), extensionModule, extensionExports, context.subscriptions); - }); - } - - private static _callActivateOptional(logService: ILogService, extensionId: ExtensionIdentifier, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { - if (typeof extensionModule.activate === 'function') { - try { - activationTimesBuilder.activateCallStart(); - logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`); - // const activateResult: Promise = extensionModule.activate.apply(global, [context]); - const activateResult: Promise = extensionModule.activate.apply(undefined, [context]); - activationTimesBuilder.activateCallStop(); - - activationTimesBuilder.activateResolveStart(); - return Promise.resolve(activateResult).then((value) => { - activationTimesBuilder.activateResolveStop(); - return value; - }); - } catch (err) { - return Promise.reject(err); - } - } else { - // No activate found => the module is the extension's exports - return Promise.resolve(extensionModule); - } - } - - // -- eager activation - - // Handle "eager" activation extensions - private _handleEagerExtensions(): Promise { - this._activateByEvent('*', true).then(undefined, (err) => { - console.error(err); - }); - - this._disposables.add(this._extHostWorkspace.onDidChangeWorkspace((e) => this._handleWorkspaceContainsEagerExtensions(e.added))); - const folders = this._extHostWorkspace.workspace ? this._extHostWorkspace.workspace.folders : []; - return this._handleWorkspaceContainsEagerExtensions(folders); - } - - private _handleWorkspaceContainsEagerExtensions(folders: ReadonlyArray): Promise { - if (folders.length === 0) { - return Promise.resolve(undefined); - } - - return Promise.all( - this._registry.getAllExtensionDescriptions().map((desc) => { - return this._handleWorkspaceContainsEagerExtension(folders, desc); - }) - ).then(() => { }); - } - - private _handleWorkspaceContainsEagerExtension(folders: ReadonlyArray, desc: IExtensionDescription): Promise { - const activationEvents = desc.activationEvents; - if (!activationEvents) { - return Promise.resolve(undefined); - } - - if (this.isActivated(desc.identifier)) { - return Promise.resolve(undefined); - } - - const fileNames: string[] = []; - const globPatterns: string[] = []; - - for (const activationEvent of activationEvents) { - if (/^workspaceContains:/.test(activationEvent)) { - const fileNameOrGlob = activationEvent.substr('workspaceContains:'.length); - if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) { - globPatterns.push(fileNameOrGlob); - } else { - fileNames.push(fileNameOrGlob); - } - } - } - - if (fileNames.length === 0 && globPatterns.length === 0) { - return Promise.resolve(undefined); - } - - const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(folders, desc.identifier, fileName))).then(() => { }); - const globPatternPromise = this._activateIfGlobPatterns(folders, desc.identifier, globPatterns); - - return Promise.all([fileNamePromise, globPatternPromise]).then(() => { }); - } - - private async _activateIfFileName(folders: ReadonlyArray, extensionId: ExtensionIdentifier, fileName: string): Promise { - - // find exact path - for (const { uri } of folders) { - if (await this._hostUtils.exists(path.join(URI.revive(uri).fsPath, fileName))) { - // the file was found - return ( - this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${fileName}`)) - .then(undefined, err => console.error(err)) - ); - } - } - - return undefined; - } - - private async _activateIfGlobPatterns(folders: ReadonlyArray, extensionId: ExtensionIdentifier, globPatterns: string[]): Promise { - this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`); - - if (globPatterns.length === 0) { - return Promise.resolve(undefined); - } - - const tokenSource = new CancellationTokenSource(); - const searchP = this._mainThreadWorkspaceProxy.$checkExists(folders.map(folder => folder.uri), globPatterns, tokenSource.token); - - const timer = setTimeout(async () => { - tokenSource.cancel(); - this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContainsTimeout:${globPatterns.join(',')}`)) - .then(undefined, err => console.error(err)); - }, ExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT); - - let exists: boolean = false; - try { - exists = await searchP; - } catch (err) { - if (!errors.isPromiseCanceledError(err)) { - console.error(err); - } - } - - tokenSource.dispose(); - clearTimeout(timer); - - if (exists) { - // a file was found matching one of the glob patterns - return ( - this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${globPatterns.join(',')}`)) - .then(undefined, err => console.error(err)) - ); - } - - return Promise.resolve(undefined); - } - - private _handleExtensionTests(): Promise { - return this._doHandleExtensionTests().then(undefined, error => { - console.error(error); // ensure any error message makes it onto the console - - return Promise.reject(error); - }); - } - - private _doHandleExtensionTests(): Promise { - const { extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment; - if (!(extensionDevelopmentLocationURI && extensionTestsLocationURI && extensionTestsLocationURI.scheme === Schemas.file)) { - return Promise.resolve(undefined); - } - - const extensionTestsPath = originalFSPath(extensionTestsLocationURI); - - // Require the test runner via node require from the provided path - let testRunner: ITestRunner | INewTestRunner | undefined; - let requireError: Error | undefined; - try { - testRunner = require.__$__nodeRequire(extensionTestsPath); - } catch (error) { - requireError = error; - } - - // Execute the runner if it follows the old `run` spec - if (testRunner && typeof testRunner.run === 'function') { - return new Promise((c, e) => { - const oldTestRunnerCallback = (error: Error, failures: number | undefined) => { - if (error) { - e(error.toString()); - } else { - c(undefined); - } - - // after tests have run, we shutdown the host - this._gracefulExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */); - }; - - const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback); - - // Using the new API `run(): Promise` - if (runResult && runResult.then) { - runResult - .then(() => { - c(); - this._gracefulExit(0); - }) - .catch((err: Error) => { - e(err.toString()); - this._gracefulExit(1); - }); - } - }); - } - - // Otherwise make sure to shutdown anyway even in case of an error - else { - this._gracefulExit(1 /* ERROR */); - } - - return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath))); - } - - private _gracefulExit(code: number): void { - // to give the PH process a chance to flush any outstanding console - // messages to the main process, we delay the exit() by some time - setTimeout(() => { - // If extension tests are running, give the exit code to the renderer - if (this._initData.remote.isRemote && !!this._initData.environment.extensionTestsLocationURI) { - this._mainThreadExtensionsProxy.$onExtensionHostExit(code); - return; - } - - this._hostUtils.exit(code); - }, 500); - } - - private _startExtensionHost(): Promise { - if (this._started) { - throw new Error(`Extension host is already started!`); - } - this._started = true; - - return this._readyToStartExtensionHost.wait() - .then(() => this._readyToRunExtensions.open()) - .then(() => this._handleEagerExtensions()) - .then(() => this._handleExtensionTests()) - .then(() => { - this._extHostLogService.info(`eager extensions activated`); - }); - } - - // -- called by extensions - - public registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable { - this._resolvers[authorityPrefix] = resolver; - return toDisposable(() => { - delete this._resolvers[authorityPrefix]; - }); - } - - // -- called by main thread - - public async $resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise { - const authorityPlusIndex = remoteAuthority.indexOf('+'); - if (authorityPlusIndex === -1) { - throw new Error(`Not an authority that can be resolved!`); - } - const authorityPrefix = remoteAuthority.substr(0, authorityPlusIndex); - - await this._almostReadyToRunExtensions.wait(); - await this._activateByEvent(`onResolveRemoteAuthority:${authorityPrefix}`, false); - - const resolver = this._resolvers[authorityPrefix]; - if (!resolver) { - throw new Error(`No remote extension installed to resolve ${authorityPrefix}.`); - } - - try { - const result = await resolver.resolve(remoteAuthority, { resolveAttempt }); - - // Split merged API result into separate authority/options - const authority: ResolvedAuthority = { - authority: remoteAuthority, - host: result.host, - port: result.port - }; - const options: ResolvedOptions = { - extensionHostEnv: result.extensionHostEnv - }; - - return { - type: 'ok', - value: { - authority, - options - } - }; - } catch (err) { - if (err instanceof RemoteAuthorityResolverError) { - return { - type: 'error', - error: { - code: err._code, - message: err._message, - detail: err._detail - } - }; - } - throw err; - } - } - - public $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise { - this._registry.keepOnly(enabledExtensionIds); - return this._startExtensionHost(); - } - - public $activateByEvent(activationEvent: string): Promise { - return ( - this._readyToRunExtensions.wait() - .then(_ => this._activateByEvent(activationEvent, false)) - ); - } - - public async $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise { - await this._readyToRunExtensions.wait(); - if (!this._registry.getExtensionDescription(extensionId)) { - // unknown extension => ignore - return false; - } - await this._activateById(extensionId, new ExtensionActivatedByEvent(false, activationEvent)); - return true; - } - - public async $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { - toAdd.forEach((extension) => (extension).extensionLocation = URI.revive(extension.extensionLocation)); - - const trie = await this.getExtensionPathIndex(); - - await Promise.all(toRemove.map(async (extensionId) => { - const extensionDescription = this._registry.getExtensionDescription(extensionId); - if (!extensionDescription) { - return; - } - const realpathValue = await this._hostUtils.realpath(extensionDescription.extensionLocation.fsPath); - trie.delete(URI.file(realpathValue).fsPath); - })); - - await Promise.all(toAdd.map(async (extensionDescription) => { - const realpathValue = await this._hostUtils.realpath(extensionDescription.extensionLocation.fsPath); - trie.set(URI.file(realpathValue).fsPath, extensionDescription); - })); - - this._registry.deltaExtensions(toAdd, toRemove); - return Promise.resolve(undefined); - } - - public async $test_latency(n: number): Promise { - return n; - } - - public async $test_up(b: VSBuffer): Promise { - return b.byteLength; - } - - public async $test_down(size: number): Promise { - let buff = VSBuffer.alloc(size); - let value = Math.random() % 256; - for (let i = 0; i < size; i++) { - buff.writeUInt8(value, i); - } - return buff; - } - - public async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise { - if (!this._initData.remote.isRemote) { - return; - } - - for (const key in env) { - const value = env[key]; - if (value === null) { - delete process.env[key]; - } else { - process.env[key] = value; - } - } - } -} - -async function loadCommonJSModule(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { - - // fake commonjs world - const module = { exports: {} }; - //@ts-ignore - self['module'] = module; - //@ts-ignore - self['exports'] = module.exports; - - // that's improper but might help extensions that aren't author correctly - // @ts-ignore - self['window'] = self; - - // import the single (!) script, make sure it's a JS-file - const suffix = '.js'; - if (endsWith(modulePath, suffix)) { - importScripts(modulePath); - } else { - importScripts(modulePath + suffix); - } - - // return what it exported - return module.exports as T; -} - -type TelemetryActivationEvent = { - id: string; - name: string; - extensionVersion: string; - publisherDisplayName: string; - activationEvents: string | null; - isBuiltin: boolean; - reason: string; -}; - -function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TelemetryActivationEvent { - const reasonStr = reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : - reason instanceof ExtensionActivatedByAPI ? 'api' : - ''; - const event = { - id: extensionDescription.identifier.value, - name: extensionDescription.name, - extensionVersion: extensionDescription.version, - publisherDisplayName: extensionDescription.publisher, - activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null, - isBuiltin: extensionDescription.isBuiltin, - reason: reasonStr - }; - - return event; -} diff --git a/src/vs/workbench/services/extensions/worker/extensionHostMain.ts b/src/vs/workbench/services/extensions/worker/extensionHostMain.ts deleted file mode 100644 index 54094e1254721..0000000000000 --- a/src/vs/workbench/services/extensions/worker/extensionHostMain.ts +++ /dev/null @@ -1,154 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { timeout } from 'vs/base/common/async'; -import * as errors from 'vs/base/common/errors'; -import { DisposableStore } from 'vs/base/common/lifecycle'; -import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri'; -import { IURITransformer } from 'vs/base/common/uriIpc'; -import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; -import { IInitData, MainContext, MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; -import { ExtHostExtensionService, IHostUtils } from './extHostExtensionService'; -import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; -import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { RPCProtocol } from 'vs/workbench/services/extensions/common/rpcProtocol'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { withNullAsUndefined } from 'vs/base/common/types'; -import { ILogService } from 'vs/platform/log/common/log'; - -// we don't (yet) throw when extensions parse -// uris that have no scheme -setUriThrowOnMissingScheme(false); - -export interface IExitFn { - (code?: number): any; -} - -export interface IConsolePatchFn { - (mainThreadConsole: MainThreadConsoleShape): any; -} - -export interface ILogServiceFn { - (initData: IInitData): ILogService; -} - -export class ExtensionHostMain { - - private _isTerminating: boolean; - private readonly _hostUtils: IHostUtils; - private readonly _extensionService: ExtHostExtensionService; - private readonly _disposables = new DisposableStore(); - - constructor( - protocol: IMessagePassingProtocol, - initData: IInitData, - hostUtils: IHostUtils, - consolePatchFn: IConsolePatchFn, - logServiceFn: ILogServiceFn, - uriTransformer: IURITransformer | null - ) { - this._isTerminating = false; - this._hostUtils = hostUtils; - const rpcProtocol = new RPCProtocol(protocol, null, uriTransformer); - - // ensure URIs are transformed and revived - initData = ExtensionHostMain._transform(initData, rpcProtocol); - - // allow to patch console - consolePatchFn(rpcProtocol.getProxy(MainContext.MainThreadConsole)); - - // services - const extHostLogService = new ExtHostLogService(logServiceFn(initData), initData.logsLocation.fsPath); - this._disposables.add(extHostLogService); - - const extHostWorkspace = new ExtHostWorkspace(rpcProtocol, extHostLogService, withNullAsUndefined(initData.workspace)); - - extHostLogService.info('extension host started'); - extHostLogService.trace('initData', initData); - - const extHostConfiguraiton = new ExtHostConfiguration(rpcProtocol.getProxy(MainContext.MainThreadConfiguration), extHostWorkspace); - this._extensionService = new ExtHostExtensionService( - hostUtils, - initData, - rpcProtocol, - extHostWorkspace, - extHostConfiguraiton, - initData.environment, - extHostLogService, - uriTransformer - ); - - // error forwarding and stack trace scanning - Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) - const extensionErrors = new WeakMap(); - this._extensionService.getExtensionPathIndex().then(map => { - (Error).prepareStackTrace = (error: Error, stackTrace: errors.V8CallSite[]) => { - let stackTraceMessage = ''; - let extension: IExtensionDescription | undefined; - let fileName: string; - for (const call of stackTrace) { - stackTraceMessage += `\n\tat ${call.toString()}`; - fileName = call.getFileName(); - if (!extension && fileName) { - extension = map.findSubstr(fileName); - } - - } - extensionErrors.set(error, extension); - return `${error.name || 'Error'}: ${error.message || ''}${stackTraceMessage}`; - }; - }); - - const mainThreadExtensions = rpcProtocol.getProxy(MainContext.MainThreadExtensionService); - const mainThreadErrors = rpcProtocol.getProxy(MainContext.MainThreadErrors); - errors.setUnexpectedErrorHandler(err => { - const data = errors.transformErrorForSerialization(err); - const extension = extensionErrors.get(err); - if (extension) { - mainThreadExtensions.$onExtensionRuntimeError(extension.identifier, data); - } else { - mainThreadErrors.$onUnexpectedError(data); - } - }); - } - - terminate(): void { - if (this._isTerminating) { - // we are already shutting down... - return; - } - this._isTerminating = true; - - this._disposables.dispose(); - - errors.setUnexpectedErrorHandler((err) => { - // TODO: write to log once we have one - }); - - const extensionsDeactivated = this._extensionService.deactivateAll(); - - // Give extensions 1 second to wrap up any async dispose, then exit in at most 4 seconds - setTimeout(() => { - Promise.race([timeout(4000), extensionsDeactivated]).finally(() => this._hostUtils.exit()); - }, 1000); - } - - private static _transform(initData: IInitData, rpcProtocol: RPCProtocol): IInitData { - initData.extensions.forEach((ext) => (ext).extensionLocation = URI.revive(rpcProtocol.transformIncomingURIs(ext.extensionLocation))); - initData.environment.appRoot = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appRoot)); - initData.environment.appSettingsHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appSettingsHome)); - const extDevLocs = initData.environment.extensionDevelopmentLocationURI; - if (extDevLocs) { - initData.environment.extensionDevelopmentLocationURI = extDevLocs.map(url => URI.revive(rpcProtocol.transformIncomingURIs(url))); - } - initData.environment.extensionTestsLocationURI = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.extensionTestsLocationURI)); - initData.environment.globalStorageHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.globalStorageHome)); - initData.environment.userHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.userHome)); - initData.logsLocation = URI.revive(rpcProtocol.transformIncomingURIs(initData.logsLocation)); - initData.workspace = rpcProtocol.transformIncomingURIs(initData.workspace); - return initData; - } -} diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts index 61a30631c8b71..7d42bf2044d3d 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts @@ -9,9 +9,10 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter } from 'vs/base/common/event'; import { isMessageOfType, MessageType, createMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; -import { IHostUtils } from 'vs/workbench/services/extensions/worker/extHostExtensionService'; -import { ExtensionHostMain } from 'vs/workbench/services/extensions/worker/extensionHostMain'; +import { ExtensionHostMain } from 'vs/workbench/services/extensions/common/extensionHostMain'; import { ConsoleLogService } from 'vs/platform/log/common/log'; +import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService'; +import 'vs/workbench/services/extensions/worker/extHost.services'; // worker-self declare namespace self { @@ -24,10 +25,11 @@ self.close = () => console.trace('An extension called terminate and this was pre let onTerminate = nativeClose; const hostUtil = new class implements IHostUtils { - exit(code?: number | undefined): void { + _serviceBrand: any; + exit(_code?: number | undefined): void { nativeClose(); } - async exists(path: string): Promise { + async exists(_path: string): Promise { return true; } async realpath(path: string): Promise { From 5880f8ab0c9adeb4a16eceee988e0ca50f62046d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 9 Aug 2019 16:20:30 +0200 Subject: [PATCH 045/613] keep empty services in one place --- .../api/worker/extHostStoragePaths.ts | 23 ------------------- .../extensions/worker/extHost.services.ts | 13 +++++++---- 2 files changed, 9 insertions(+), 27 deletions(-) delete mode 100644 src/vs/workbench/api/worker/extHostStoragePaths.ts diff --git a/src/vs/workbench/api/worker/extHostStoragePaths.ts b/src/vs/workbench/api/worker/extHostStoragePaths.ts deleted file mode 100644 index 3d07dff7de05f..0000000000000 --- a/src/vs/workbench/api/worker/extHostStoragePaths.ts +++ /dev/null @@ -1,23 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; - -export class ExtensionStoragePaths implements IExtensionStoragePaths { - - readonly _serviceBrand: undefined; - - readonly whenReady: Promise = Promise.resolve(); - - //todo@joh -> this isn't proper but also hard to get right... - workspaceValue(_extension: IExtensionDescription): string | undefined { - return ''; - } - - globalValue(_extension: IExtensionDescription): string { - return ''; - } -} diff --git a/src/vs/workbench/services/extensions/worker/extHost.services.ts b/src/vs/workbench/services/extensions/worker/extHost.services.ts index 69ad6dbfe4bab..127250f83be86 100644 --- a/src/vs/workbench/services/extensions/worker/extHost.services.ts +++ b/src/vs/workbench/services/extensions/worker/extHost.services.ts @@ -19,8 +19,6 @@ import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensi import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; import { ExtHostExtensionService } from 'vs/workbench/api/worker/extHostExtensionService'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { ExtensionStoragePaths } from 'vs/workbench/api/worker/extHostStoragePaths'; - // register singleton services registerSingleton(IExtHostOutputService, ExtHostOutputService); @@ -30,7 +28,6 @@ registerSingleton(IExtHostConfiguration, ExtHostConfiguration); registerSingleton(IExtHostCommands, ExtHostCommands); registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors); registerSingleton(IExtHostStorage, ExtHostStorage); -registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths); registerSingleton(IExtHostExtensionService, ExtHostExtensionService); // register services that only throw errors @@ -38,7 +35,10 @@ function NotImplementedProxy(name: ServiceIdentifier): { new(): T } { return class { constructor() { return new Proxy({}, { - get(_target: any, prop: any) { + get(target: any, prop: string | number) { + if (target[prop]) { + return target[prop]; + } throw new Error(`Not Implemented: ${name}->${String(prop)}`); } }); @@ -49,3 +49,8 @@ registerSingleton(IExtHostTerminalService, class extends NotImplementedProxy(IEx registerSingleton(IExtHostTask, class extends NotImplementedProxy(IExtHostTask) { }); registerSingleton(IExtHostDebugService, class extends NotImplementedProxy(IExtHostDebugService) { }); registerSingleton(IExtHostSearch, class extends NotImplementedProxy(IExtHostSearch) { }); +registerSingleton(IExtensionStoragePaths, class extends NotImplementedProxy(IExtensionStoragePaths) { + whenReady = Promise.resolve(); + globalValue = () => ''; + workspaceValue = () => ''; +}); From 3d64c7ef94756a04fef8285b70d9abf4ed8b7e08 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 9 Aug 2019 10:03:08 -0700 Subject: [PATCH 046/613] Allow web tests to pass electron path exists check --- test/smoke/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 4972be890f1bf..34364d39ded49 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -133,7 +133,7 @@ if (testCodePath) { process.env.VSCODE_CLI = '1'; } -if (!fs.existsSync(electronPath || '')) { +if (!opts.web && !fs.existsSync(electronPath || '')) { fail(`Can't find Code at ${electronPath}.`); } From 0d740b99131b292d4195d76c0006e7c11921798a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 9 Aug 2019 10:41:07 -0700 Subject: [PATCH 047/613] Enable passing tests --- test/smoke/src/main.ts | 12 +++++++++++- test/smoke/src/vscode/puppeteer-driver.ts | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 34364d39ded49..761684e410430 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -279,9 +279,19 @@ describe('Running Code', () => { } if (opts.web) { + // setupDataLossTests(); setupDataExplorerTests(); - setupDataPreferencesTests(); + // setupDataPreferencesTests(); + setupDataSearchTests(); + setupDataCSSTests(); + // setupDataEditorTests(); + // setupDataDebugTests(); + // setupDataGitTests(); + // setupDataStatusbarTests(); + // setupDataExtensionTests(); setupTerminalTests(); + // setupDataMultirootTests(); + // setupDataLocalizationTests(); return; } diff --git a/test/smoke/src/vscode/puppeteer-driver.ts b/test/smoke/src/vscode/puppeteer-driver.ts index fbe7b26db47db..97fe587c902bb 100644 --- a/test/smoke/src/vscode/puppeteer-driver.ts +++ b/test/smoke/src/vscode/puppeteer-driver.ts @@ -11,6 +11,7 @@ const height = 800; const vscodeToPuppeteerKey = { cmd: 'Meta', ctrl: 'Control', + shift: 'Shift', enter: 'Enter', escape: 'Escape', right: 'ArrowRight', From d224d533c872b78ecbc8ca26dbd796993df31887 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 9 Aug 2019 11:06:01 -0700 Subject: [PATCH 048/613] Add all passing web tests --- test/smoke/src/main.ts | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 761684e410430..9bf6d4a77868e 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -278,35 +278,18 @@ describe('Running Code', () => { }); } - if (opts.web) { - // setupDataLossTests(); - setupDataExplorerTests(); - // setupDataPreferencesTests(); - setupDataSearchTests(); - setupDataCSSTests(); - // setupDataEditorTests(); - // setupDataDebugTests(); - // setupDataGitTests(); - // setupDataStatusbarTests(); - // setupDataExtensionTests(); - setupTerminalTests(); - // setupDataMultirootTests(); - // setupDataLocalizationTests(); - return; - } - - setupDataLossTests(); + if (!opts.web) { setupDataLossTests(); } setupDataExplorerTests(); - setupDataPreferencesTests(); + if (!opts.web) { setupDataPreferencesTests(); } setupDataSearchTests(); setupDataCSSTests(); setupDataEditorTests(); - setupDataDebugTests(); + if (!opts.web) { setupDataDebugTests(); } setupDataGitTests(); setupDataStatusbarTests(); setupDataExtensionTests(); setupTerminalTests(); - setupDataMultirootTests(); + if (!opts.web) { setupDataMultirootTests(); } setupDataLocalizationTests(); }); From eff2a4a4540f521e9e1d6bac9db39793e5b9f21a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 9 Aug 2019 11:14:36 -0700 Subject: [PATCH 049/613] Fix tslint complaining about xterm import --- src/vs/platform/driver/browser/baseDriver.ts | 2 -- tslint.json | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/driver/browser/baseDriver.ts b/src/vs/platform/driver/browser/baseDriver.ts index 7b7d3057db648..fe6fc6e1e4616 100644 --- a/src/vs/platform/driver/browser/baseDriver.ts +++ b/src/vs/platform/driver/browser/baseDriver.ts @@ -4,8 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { getTopLeftOffset } from 'vs/base/browser/dom'; -// TODO: Allow this -// tslint:disable-next-line: import-patterns import { Terminal } from 'xterm'; import { coalesce } from 'vs/base/common/arrays'; import { IElement, IWindowDriver } from 'vs/platform/driver/common/driver'; diff --git a/tslint.json b/tslint.json index 155bd9a41158b..b67d9aeeb5c30 100644 --- a/tslint.json +++ b/tslint.json @@ -175,7 +175,8 @@ "vs/css!./**/*", "**/vs/base/{common,browser}/**", "**/vs/base/parts/*/{common,browser}/**", - "**/vs/platform/*/{common,browser}/**" + "**/vs/platform/*/{common,browser}/**", + "xterm" // xterm is strictly a browser-only component ] }, { From b7bc1563193b05e1d5f1e5173b5e968f24ebbc3c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 9 Aug 2019 11:15:45 -0700 Subject: [PATCH 050/613] Remove resolved TODOs --- test/smoke/src/application.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/smoke/src/application.ts b/test/smoke/src/application.ts index 52fe19dd83458..1daaf4527c305 100644 --- a/test/smoke/src/application.ts +++ b/test/smoke/src/application.ts @@ -66,7 +66,6 @@ export class Application { async start(expectWalkthroughPart = true): Promise { await this._start(); - // TODO: web doesn't show explorer? await this.code.waitForElement('.explorer-folders-view'); if (expectWalkthroughPart) { @@ -139,7 +138,6 @@ export class Application { } await this.code.waitForWindowIds(ids => ids.length > 0); - // TODO: Remove on web? await this.code.waitForElement('.monaco-workbench'); if (this.remote) { From 7721d6e373822fb9a9e9aa3d9e142a211acaff1e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 9 Aug 2019 11:21:40 -0700 Subject: [PATCH 051/613] Support launching web smoketests in headless mode --- test/smoke/src/application.ts | 3 ++- test/smoke/src/main.ts | 6 ++++-- test/smoke/src/vscode/code.ts | 7 ++++--- test/smoke/src/vscode/puppeteer-driver.ts | 4 ++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/test/smoke/src/application.ts b/test/smoke/src/application.ts index 1daaf4527c305..874a17421b2c7 100644 --- a/test/smoke/src/application.ts +++ b/test/smoke/src/application.ts @@ -125,7 +125,8 @@ export class Application { log: this.options.log, extraArgs, remote: this.options.remote, - web: this.options.web + web: this.options.web, + headless: this.options.headless }); this._workbench = new Workbench(this._code, this.userDataPath); diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 9bf6d4a77868e..bb75ee6066b78 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -52,7 +52,8 @@ const opts = minimist(args, { boolean: [ 'verbose', 'remote', - 'web' + 'web', + 'headless' ], default: { verbose: false @@ -214,7 +215,8 @@ function createOptions(): ApplicationOptions { log, screenshotsPath, remote: opts.remote, - web: opts.web + web: opts.web, + headless: opts.headless }; } diff --git a/test/smoke/src/vscode/code.ts b/test/smoke/src/vscode/code.ts index da5326e929c59..f381ed002e97a 100644 --- a/test/smoke/src/vscode/code.ts +++ b/test/smoke/src/vscode/code.ts @@ -101,6 +101,8 @@ export interface SpawnOptions { remote?: boolean; /** Run in the web */ web?: boolean; + /** Run in headless mode (only applies when web is true) */ + headless?: boolean; } async function createDriverHandle(): Promise { @@ -172,7 +174,7 @@ export async function spawn(options: SpawnOptions): Promise { if (options.web) { launch(args); - connectDriver = connectPuppeteerDriver; + connectDriver = connectPuppeteerDriver.bind(connectPuppeteerDriver, !!options.headless); } else { const spawnOptions: cp.SpawnOptions = { env }; child = cp.spawn(electronPath, args, spawnOptions); @@ -180,7 +182,6 @@ export async function spawn(options: SpawnOptions): Promise { child.once('exit', () => instances.delete(child!)); connectDriver = connectElectronDriver; } - return connect(connectDriver, child, outPath, handle, options.logger); } @@ -379,4 +380,4 @@ export function findElements(element: IElement, fn: (element: IElement) => boole } return result; -} \ No newline at end of file +} diff --git a/test/smoke/src/vscode/puppeteer-driver.ts b/test/smoke/src/vscode/puppeteer-driver.ts index 97fe587c902bb..0397c31343634 100644 --- a/test/smoke/src/vscode/puppeteer-driver.ts +++ b/test/smoke/src/vscode/puppeteer-driver.ts @@ -183,12 +183,12 @@ export function launch(_args): void { // TODO: Move puppeteer launch here } -export function connect(outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }> { +export function connect(headless: boolean, outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }> { return new Promise(async (c) => { const browser = await puppeteer.launch({ // Run in Edge dev on macOS // executablePath: '/Applications/Microsoft\ Edge\ Dev.app/Contents/MacOS/Microsoft\ Edge\ Dev', - headless: false, + headless, slowMo: 80, args: [`--window-size=${width},${height}`] }); From 52333e3769d63c152f7fba4108dec316b2c11db4 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 9 Aug 2019 11:30:29 -0700 Subject: [PATCH 052/613] Remove strong typing of xterm access in smoke tests This makes the monaco check happy, while we could add xterm to the list of typings in the monaco tsconfig it's probably too risky. --- src/tsconfig.monaco.json | 2 +- src/vs/platform/driver/browser/baseDriver.ts | 5 ++--- tslint.json | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 32983503724dd..aa0af48b342b8 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -14,7 +14,7 @@ }, "include": [ "typings/require.d.ts", - "./typings/require-monaco.d.ts", + "typings/require-monaco.d.ts", "typings/thenable.d.ts", "typings/es6-promise.d.ts", "typings/lib.es2018.promise.d.ts", diff --git a/src/vs/platform/driver/browser/baseDriver.ts b/src/vs/platform/driver/browser/baseDriver.ts index fe6fc6e1e4616..df59dd6e879f3 100644 --- a/src/vs/platform/driver/browser/baseDriver.ts +++ b/src/vs/platform/driver/browser/baseDriver.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { getTopLeftOffset } from 'vs/base/browser/dom'; -import { Terminal } from 'xterm'; import { coalesce } from 'vs/base/common/arrays'; import { IElement, IWindowDriver } from 'vs/platform/driver/common/driver'; @@ -129,7 +128,7 @@ export abstract class BaseWindowDriver implements IWindowDriver { throw new Error(`Terminal not found: ${selector}`); } - const xterm: Terminal = (element as any).xterm; + const xterm = (element as any).xterm; if (!xterm) { throw new Error(`Xterm not found: ${selector}`); @@ -151,7 +150,7 @@ export abstract class BaseWindowDriver implements IWindowDriver { throw new Error(`Element not found: ${selector}`); } - const xterm: Terminal = (element as any).xterm; + const xterm = (element as any).xterm; if (!xterm) { throw new Error(`Xterm not found: ${selector}`); diff --git a/tslint.json b/tslint.json index b67d9aeeb5c30..155bd9a41158b 100644 --- a/tslint.json +++ b/tslint.json @@ -175,8 +175,7 @@ "vs/css!./**/*", "**/vs/base/{common,browser}/**", "**/vs/base/parts/*/{common,browser}/**", - "**/vs/platform/*/{common,browser}/**", - "xterm" // xterm is strictly a browser-only component + "**/vs/platform/*/{common,browser}/**" ] }, { From 4c2a3af2c8026002388910dc208ea284e05fddbc Mon Sep 17 00:00:00 2001 From: mayaswrath Date: Sun, 11 Aug 2019 13:02:40 -0400 Subject: [PATCH 053/613] #77879 multi cursor support for toggle breakpoint --- .../debug/browser/debugEditorActions.ts | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 060f919707434..2e8409ed9a824 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -35,21 +35,27 @@ class ToggleBreakpointAction extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { - const debugService = accessor.get(IDebugService); - - const position = editor.getPosition(); - if (editor.hasModel() && position) { + if (editor.hasModel()) { + const debugService = accessor.get(IDebugService); const modelUri = editor.getModel().uri; - const bps = debugService.getModel().getBreakpoints({ lineNumber: position.lineNumber, uri: modelUri }); - - if (bps.length) { - return Promise.all(bps.map(bp => debugService.removeBreakpoints(bp.getId()))); - } - if (debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { - return debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber }], 'debugEditorActions.toggleBreakpointAction'); + const lineNumbers = new Set(editor._getCursors().getAll() + .filter(cs => cs.modelState.position) + .map(cs => cs.modelState.position.lineNumber)); + + const promises = Array>(); + lineNumbers.forEach(lineNumber => { + const bps = debugService.getModel().getBreakpoints({ lineNumber: lineNumber, uri: modelUri }); + + if (bps.length) { + bps.map(bp => debugService.removeBreakpoints(bp.getId())).forEach(p => promises.push(p)); + } else if (debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { + promises.push(debugService.addBreakpoints(modelUri, [{ lineNumber: lineNumber }], 'debugEditorActions.toggleBreakpointAction')); + } + }); + if (promises.length > 0) { + return Promise.all(promises); } } - return Promise.resolve(); } } From a3bff518015b31538e5a4651bc9cfb7fb4562d5d Mon Sep 17 00:00:00 2001 From: mayaswrath Date: Sun, 11 Aug 2019 13:06:23 -0400 Subject: [PATCH 054/613] removed unnecessary check --- src/vs/workbench/contrib/debug/browser/debugEditorActions.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 2e8409ed9a824..53d6608548e3f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -52,9 +52,7 @@ class ToggleBreakpointAction extends EditorAction { promises.push(debugService.addBreakpoints(modelUri, [{ lineNumber: lineNumber }], 'debugEditorActions.toggleBreakpointAction')); } }); - if (promises.length > 0) { - return Promise.all(promises); - } + return Promise.all(promises); } return Promise.resolve(); } From 9d709b8ddded896015680179370ccd9b4a2b99f8 Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Mon, 12 Aug 2019 03:11:56 -0500 Subject: [PATCH 055/613] Force refresh on setInput in settingEditor. Fixes #78931 --- src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index ab5ec782a3958..986cef518185a 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -846,7 +846,7 @@ export class SettingsEditor2 extends BaseEditor { this._register(model.onDidChangeGroups(() => this.onConfigUpdate())); this.defaultSettingsEditorModel = model; - return this.onConfigUpdate(); + return this.onConfigUpdate(undefined, true); }); } return Promise.resolve(null); From f41f8a8d077b591fa8f2acbea08fb6af25d970ea Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 12 Aug 2019 10:55:41 +0200 Subject: [PATCH 056/613] move webWorkerExtHostStarter --- .../webWorkerExtensionHostStarter.ts | 0 .../services/extensions/electron-browser/extensionService.ts | 2 +- tslint.json | 3 ++- 3 files changed, 3 insertions(+), 2 deletions(-) rename src/vs/workbench/services/extensions/{electron-browser => browser}/webWorkerExtensionHostStarter.ts (100%) diff --git a/src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts similarity index 100% rename from src/vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter.ts rename to src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 8d8b85b0f01a7..ec33b228a2cc2 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -34,7 +34,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IProductService } from 'vs/platform/product/common/product'; import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints'; -import { WebWorkerExtensionHostStarter } from 'vs/workbench/services/extensions/electron-browser/webWorkerExtensionHostStarter'; +import { WebWorkerExtensionHostStarter } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter'; class DeltaExtensionsQueueItem { constructor( diff --git a/tslint.json b/tslint.json index cb43495ba0886..f5d3c5ba2ab64 100644 --- a/tslint.json +++ b/tslint.json @@ -452,11 +452,12 @@ "restrictions": [ "vs/nls", "vs/css!./**/*", - "**/vs/base/**/{common,browser}/**", + "**/vs/base/**/{common,browser,worker}/**", "**/vs/platform/**/{common,browser}/**", "**/vs/editor/{common,browser}/**", "**/vs/workbench/workbench.web.api", "**/vs/workbench/{common,browser}/**", + "**/vs/workbench/api/{common,browser}/**", "**/vs/workbench/services/**/{common,browser}/**", "vscode-textmate", "onigasm-umd" From 7212908c2c2687b55f20cff0ca9c10cad7579f90 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 12 Aug 2019 11:00:37 +0200 Subject: [PATCH 057/613] some clean up --- .../browser/webWorkerExtensionHostStarter.ts | 41 +++++-------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index 4c8492f14096a..ae158efeae838 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -3,12 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; @@ -26,9 +23,9 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { + private _toDispose = new DisposableStore(); + private _isTerminating: boolean = false; private _protocol?: IMessagePassingProtocol; - private _isTerminating?: boolean; - private _toDispose: IDisposable[] = []; private readonly _onDidExit = new Emitter<[number, string | null]>(); readonly onExit: Event<[number, string | null]> = this._onDidExit.event; @@ -67,7 +64,8 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { ); // keep for cleanup - this._toDispose.push(worker, emitter); + this._toDispose.add(emitter); + this._toDispose.add(worker); const protocol: IMessagePassingProtocol = { onMessage: emitter.event, @@ -94,16 +92,15 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { dispose(): void { if (!this._protocol) { - // nothing else to do - dispose(this._toDispose); + this._toDispose.dispose(); return; } - if (!this._isTerminating) { - this._isTerminating = true; - - this._protocol.send(createMessageOfType(MessageType.Terminate)); - setTimeout(() => dispose(this._toDispose), 10 * 1000); + if (this._isTerminating) { + return; } + this._isTerminating = true; + this._protocol.send(createMessageOfType(MessageType.Terminate)); + setTimeout(() => this._toDispose.dispose(), 10 * 1000); } getInspectPort(): number | undefined { @@ -153,19 +150,3 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { }); } } - -class WorkerExtensionHost extends Disposable implements IWorkbenchContribution { - - constructor() { - super(); - // new ExtensionHostWebWorker().start().then(protocol => { - - // }); - } -} - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( - WorkerExtensionHost, - LifecyclePhase.Ready -); - From 777c424d5c50d72e372a451c321f4f4b14731048 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 12 Aug 2019 11:03:29 +0200 Subject: [PATCH 058/613] no process, prefer async/await --- .../browser/webWorkerExtensionHostStarter.ts | 79 +++++++++---------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index ae158efeae838..e8c1d06dd9e23 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -107,46 +107,43 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { return undefined; } - private _createExtHostInitData(): Promise { - return Promise.all([this._telemetryService.getTelemetryInfo(), this._extensions]) - .then(([telemetryInfo, extensionDescriptions]) => { - const workspace = this._contextService.getWorkspace(); - const r: IInitData = { - commit: this._productService.commit, - version: this._productService.version, - parentPid: process.pid, - environment: { - isExtensionDevelopmentDebug: false, // < todo@joh - appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, - appSettingsHome: this._environmentService.appSettingsHome ? this._environmentService.appSettingsHome : undefined, - appName: this._productService.nameLong, - appUriScheme: this._productService.urlProtocol, - appLanguage: platform.language, - extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, - extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, - globalStorageHome: URI.file(this._environmentService.globalStorageHome), - userHome: URI.file(this._environmentService.userHome), - webviewResourceRoot: this._environmentService.webviewResourceRoot, - webviewCspSource: this._environmentService.webviewCspSource, - }, - workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { - configuration: workspace.configuration || undefined, - id: workspace.id, - name: this._labelService.getWorkspaceLabel(workspace) - }, - resolvedExtensions: [], - hostExtensions: [], - extensions: extensionDescriptions, - telemetryInfo, - logLevel: this._logService.getLevel(), - logsLocation: this._extensionHostLogsLocation, - autoStart: true,// < todo@joh this._autoStart, - remote: { - authority: this._environmentService.configuration.remoteAuthority, - isRemote: false - }, - }; - return r; - }); + private async _createExtHostInitData(): Promise { + const [telemetryInfo, extensionDescriptions] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._extensions]); + const workspace = this._contextService.getWorkspace(); + return { + commit: this._productService.commit, + version: this._productService.version, + parentPid: -1, + environment: { + isExtensionDevelopmentDebug: false, + appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, + appSettingsHome: this._environmentService.appSettingsHome ? this._environmentService.appSettingsHome : undefined, + appName: this._productService.nameLong, + appUriScheme: this._productService.urlProtocol, + appLanguage: platform.language, + extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, + extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, + globalStorageHome: URI.file(this._environmentService.globalStorageHome), + userHome: URI.file(this._environmentService.userHome), + webviewResourceRoot: this._environmentService.webviewResourceRoot, + webviewCspSource: this._environmentService.webviewCspSource, + }, + workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { + configuration: workspace.configuration || undefined, + id: workspace.id, + name: this._labelService.getWorkspaceLabel(workspace) + }, + resolvedExtensions: [], + hostExtensions: [], + extensions: extensionDescriptions, + telemetryInfo, + logLevel: this._logService.getLevel(), + logsLocation: this._extensionHostLogsLocation, + autoStart: true, + remote: { + authority: this._environmentService.configuration.remoteAuthority, + isRemote: false + }, + }; } } From 94c69e0a4b2d1b223a69ae5c89d5abc72a37c972 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 12 Aug 2019 11:19:10 +0200 Subject: [PATCH 059/613] web ext host in web client, not in desktop client --- .../services/extensions/browser/extensionService.ts | 6 ++++++ .../extensions/browser/webWorkerExtensionHostStarter.ts | 4 ++-- .../extensions/electron-browser/extensionService.ts | 8 +------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 077598d16d68f..9969f28423420 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -18,6 +18,8 @@ import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/co import { RemoteExtensionHostClient, IInitDataProvider } from 'vs/workbench/services/extensions/common/remoteExtensionHostClient'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { WebWorkerExtensionHostStarter } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter'; +import { URI } from 'vs/base/common/uri'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -61,6 +63,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten protected _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { const result: ExtensionHostProcessManager[] = []; + const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, Promise.resolve([]), URI.parse('empty:value')); + const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents); + result.push(webHostProcessManager); + const remoteAgentConnection = this._remoteAgentService.getConnection()!; const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, this.getExtensions(), this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index e8c1d06dd9e23..a88545f4e31d2 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -123,8 +123,8 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, - globalStorageHome: URI.file(this._environmentService.globalStorageHome), - userHome: URI.file(this._environmentService.userHome), + globalStorageHome: URI.parse('fake:globalStorageHome'), //todo@joh URI.file(this._environmentService.globalStorageHome), + userHome: URI.parse('fake:userHome'), //todo@joh URI.file(this._environmentService.userHome), webviewResourceRoot: this._environmentService.webviewResourceRoot, webviewCspSource: this._environmentService.webviewCspSource, }, diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index ec33b228a2cc2..272f399268f5c 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -34,7 +34,6 @@ import { IFileService } from 'vs/platform/files/common/files'; import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IProductService } from 'vs/platform/product/common/product'; import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints'; -import { WebWorkerExtensionHostStarter } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter'; class DeltaExtensionsQueueItem { constructor( @@ -350,16 +349,11 @@ export class ExtensionService extends AbstractExtensionService implements IExten } const result: ExtensionHostProcessManager[] = []; - const workerExtensions: Record = { ['jrieken.helloworld']: true }; - const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions.then(e => e.filter(e => !workerExtensions[e.identifier.value])), this._extensionHostLogsLocation); + const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._extensionHostLogsLocation); const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, true, extHostProcessWorker, null, initialActivationEvents); result.push(extHostProcessManager); - const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, extensions.then(e => e.filter(e => workerExtensions[e.identifier.value])), this._extensionHostLogsLocation); - const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents); - result.push(webHostProcessManager); - const remoteAgentConnection = this._remoteAgentService.getConnection(); if (remoteAgentConnection) { const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, this.getExtensions(), this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); From 493d512f0b91b738f0ea0830d72bfe4029e284ab Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 12 Aug 2019 11:56:30 +0200 Subject: [PATCH 060/613] properly expose remote info --- .../services/extensions/browser/extensionService.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 9969f28423420..cd80cb266fab7 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -63,11 +63,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten protected _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { const result: ExtensionHostProcessManager[] = []; - const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, Promise.resolve([]), URI.parse('empty:value')); - const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents); + const remoteAgentConnection = this._remoteAgentService.getConnection()!; + + const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, Promise.resolve([]), URI.parse('empty:value')); //todo@joh + const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); result.push(webHostProcessManager); - const remoteAgentConnection = this._remoteAgentService.getConnection()!; const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, this.getExtensions(), this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); result.push(remoteExtHostProcessManager); From 2700f41dab7a4edfd0263876feba74c5e55423fe Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 12 Aug 2019 14:47:20 +0200 Subject: [PATCH 061/613] use desktop and web ext host logging service --- .../workbench/api/common/extHost.api.impl.ts | 11 +-- .../api/common/extHostExtensionService.ts | 23 +++-- .../workbench/api/common/extHostLogService.ts | 34 -------- src/vs/workbench/api/node/extHost.services.ts | 3 + .../api/node/extHostExtensionService.ts | 4 +- .../workbench/api/node/extHostLogService.ts | 36 ++++++++ .../workbench/api/worker/extHostLogService.ts | 84 +++++++++++++++++++ .../extensions/common/extensionHostMain.ts | 11 ++- .../services/extensions/node/proxyResolver.ts | 10 +-- .../extensions/worker/extHost.services.ts | 5 +- tslint.json | 8 ++ 11 files changed, 160 insertions(+), 69 deletions(-) delete mode 100644 src/vs/workbench/api/common/extHostLogService.ts create mode 100644 src/vs/workbench/api/node/extHostLogService.ts create mode 100644 src/vs/workbench/api/worker/extHostLogService.ts diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index e8fec87502911..0c4b489095d5b 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -15,7 +15,7 @@ import { OverviewRulerLane } from 'vs/editor/common/model'; import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; import { score } from 'vs/editor/common/modes/languageSelector'; import * as files from 'vs/platform/files/common/files'; -import { ExtHostContext, MainContext } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, MainContext, ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands'; import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard'; import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; @@ -33,7 +33,6 @@ import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem'; import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures'; import { ExtHostLanguages } from 'vs/workbench/api/common/extHostLanguages'; -import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; import { ExtHostMessageService } from 'vs/workbench/api/common/extHostMessageService'; import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput'; import { ExtHostProgress } from 'vs/workbench/api/common/extHostProgress'; @@ -95,10 +94,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const uriTransformer = accessor.get(IURITransformerService); const rpcProtocol = accessor.get(IExtHostRpcService); const extHostStorage = accessor.get(IExtHostStorage); - const extHostLogService = accessor.get(ILogService); + const extHostLogService = accessor.get(ILogService); // register addressable instances - rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService); + rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService); rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace); rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration); rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService); @@ -145,10 +144,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostStatusBar = new ExtHostStatusBar(rpcProtocol); const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments); - // Register an output channel for exthost log - const outputChannelName = initData.remote.isRemote ? nls.localize('remote extension host Log', "Remote Extension Host") : nls.localize('extension host Log', "Extension Host"); - extHostOutputService.createOutputChannelFromLogFile(outputChannelName, extHostLogService.logFile); - // Register API-ish commands ExtHostApiCommands.register(extHostCommands); diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index cd25ceb4bbadb..3b59d2d692d49 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -14,7 +14,6 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostExtensionServiceShape, IInitData, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IResolveAuthorityResult } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator'; -import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; @@ -75,7 +74,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio protected readonly _instaService: IInstantiationService; protected readonly _extHostWorkspace: ExtHostWorkspace; protected readonly _extHostConfiguration: ExtHostConfiguration; - protected readonly _extHostLogService: ExtHostLogService; + protected readonly _logService: ILogService; protected readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape; protected readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape; @@ -102,7 +101,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio @IExtHostRpcService extHostContext: IExtHostRpcService, @IExtHostWorkspace extHostWorkspace: IExtHostWorkspace, @IExtHostConfiguration extHostConfiguration: IExtHostConfiguration, - @ILogService extHostLogService: ExtHostLogService, + @ILogService logService: ILogService, @IExtHostInitDataService initData: IExtHostInitDataService, @IExtensionStoragePaths storagePath: IExtensionStoragePaths ) { @@ -112,7 +111,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio this._extHostWorkspace = extHostWorkspace; this._extHostConfiguration = extHostConfiguration; - this._extHostLogService = extHostLogService; + this._logService = logService; this._disposables = new DisposableStore(); this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace); @@ -329,14 +328,14 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE)); } - this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.identifier.value} ${JSON.stringify(reason)}`); + this._logService.info(`ExtensionService#_doActivateExtension ${extensionDescription.identifier.value} ${JSON.stringify(reason)}`); const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); return Promise.all([ this._loadCommonJSModule(extensionDescription.main, activationTimesBuilder), this._loadExtensionContext(extensionDescription) ]).then(values => { - return AbstractExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder); + return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder); }); } @@ -347,7 +346,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage); const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage); - this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`); + this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`); return Promise.all([ globalState.whenReady, workspaceState.whenReady, @@ -359,10 +358,10 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio workspaceState, subscriptions: [], get extensionPath() { return extensionDescription.extensionLocation.fsPath; }, - storagePath: this._storagePath.workspaceValue(extensionDescription), - globalStoragePath: this._storagePath.globalValue(extensionDescription), + get storagePath() { return that._storagePath.workspaceValue(extensionDescription); }, + get globalStoragePath() { return that._storagePath.globalValue(extensionDescription); }, asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); }, - logPath: that._extHostLogService.getLogDirectory(extensionDescription.identifier), + get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); }, executionContext: this._initData.remote.isRemote ? ExtensionExecutionContext.Remote : ExtensionExecutionContext.Local, }); }); @@ -479,7 +478,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio } private async _activateIfGlobPatterns(folders: ReadonlyArray, extensionId: ExtensionIdentifier, globPatterns: string[]): Promise { - this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`); + this._logService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId.value}, entryPoint: workspaceContains`); if (globPatterns.length === 0) { return Promise.resolve(undefined); @@ -606,7 +605,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio .then(() => this._handleEagerExtensions()) .then(() => this._handleExtensionTests()) .then(() => { - this._extHostLogService.info(`eager extensions activated`); + this._logService.info(`eager extensions activated`); }); } diff --git a/src/vs/workbench/api/common/extHostLogService.ts b/src/vs/workbench/api/common/extHostLogService.ts deleted file mode 100644 index d67a19801d3ae..0000000000000 --- a/src/vs/workbench/api/common/extHostLogService.ts +++ /dev/null @@ -1,34 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { join } from 'vs/base/common/path'; -import { ILogService, DelegatedLogService, LogLevel } from 'vs/platform/log/common/log'; -import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; -import { URI } from 'vs/base/common/uri'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; - -export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape { - - private _logsPath: string; - readonly logFile: URI; - - constructor( - delegate: ILogService, - logsPath: string, - ) { - super(delegate); - this._logsPath = logsPath; - this.logFile = URI.file(join(logsPath, `${ExtensionHostLogFileName}.log`)); - } - - $setLevel(level: LogLevel): void { - this.setLevel(level); - } - - getLogDirectory(extensionID: ExtensionIdentifier): string { - return join(this._logsPath, extensionID.value); - } -} diff --git a/src/vs/workbench/api/node/extHost.services.ts b/src/vs/workbench/api/node/extHost.services.ts index 153413e086e8c..a227d8a67b3f7 100644 --- a/src/vs/workbench/api/node/extHost.services.ts +++ b/src/vs/workbench/api/node/extHost.services.ts @@ -24,8 +24,11 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; // register singleton services +registerSingleton(ILogService, ExtHostLogService); registerSingleton(IExtHostOutputService, ExtHostOutputService2); registerSingleton(IExtHostWorkspace, ExtHostWorkspace); registerSingleton(IExtHostDecorations, ExtHostDecorations); diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index efbf84cf6413e..99c3a6629d4cd 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -41,14 +41,14 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { } // Do this when extension service exists, but extensions are not being activated yet. - await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._extHostLogService, this._mainThreadTelemetryProxy); + await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._logService, this._mainThreadTelemetryProxy); } protected _loadCommonJSModule(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { let r: T | null = null; activationTimesBuilder.codeLoadingStart(); - this._extHostLogService.info(`ExtensionService#loadCommonJSModule ${modulePath}`); + this._logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`); try { r = require.__$__nodeRequire(modulePath); } catch (e) { diff --git a/src/vs/workbench/api/node/extHostLogService.ts b/src/vs/workbench/api/node/extHostLogService.ts new file mode 100644 index 0000000000000..f29d417620ded --- /dev/null +++ b/src/vs/workbench/api/node/extHostLogService.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { join } from 'vs/base/common/path'; +import { ILogService, DelegatedLogService, LogLevel } from 'vs/platform/log/common/log'; +import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; +import { URI } from 'vs/base/common/uri'; +import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { Schemas } from 'vs/base/common/network'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; +import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput'; + +export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape { + + constructor( + @IExtHostInitDataService initData: IExtHostInitDataService, + @IExtHostOutputService extHostOutputService: IExtHostOutputService + ) { + if (initData.logsLocation.scheme !== Schemas.file) { throw new Error('Only file-logging supported'); } + super(new SpdLogService(ExtensionHostLogFileName, initData.logsLocation.fsPath, initData.logLevel)); + + // Register an output channel for exthost log + extHostOutputService.createOutputChannelFromLogFile( + initData.remote.isRemote ? localize('remote extension host Log', "Remote Extension Host") : localize('extension host Log', "Extension Host"), + URI.file(join(initData.logsLocation.fsPath, `${ExtensionHostLogFileName}.log`)) + ); + } + + $setLevel(level: LogLevel): void { + this.setLevel(level); + } +} diff --git a/src/vs/workbench/api/worker/extHostLogService.ts b/src/vs/workbench/api/worker/extHostLogService.ts new file mode 100644 index 0000000000000..79211c61945eb --- /dev/null +++ b/src/vs/workbench/api/worker/extHostLogService.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log'; +import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput'; +import * as vscode from 'vscode'; + +export class ExtHostLogService extends AbstractLogService implements ILogService, ExtHostLogServiceShape { + + _serviceBrand: any; + + private readonly _logChannel: vscode.OutputChannel; + + constructor( + @IExtHostInitDataService initData: IExtHostInitDataService, + @IExtHostOutputService extHostOutputService: IExtHostOutputService + ) { + super(); + this.setLevel(initData.logLevel); + this._logChannel = extHostOutputService.createOutputChannel('Log (Worker Extension Host)'); + } + + $setLevel(level: LogLevel): void { + this.setLevel(level); + } + + trace(_message: string, ..._args: any[]): void { + if (this.getLevel() <= LogLevel.Trace) { + this._logChannel.appendLine(this._format(arguments)); + } + } + + debug(_message: string, ..._args: any[]): void { + if (this.getLevel() <= LogLevel.Debug) { + this._logChannel.appendLine(this._format(arguments)); + } + } + + info(_message: string, ..._args: any[]): void { + if (this.getLevel() <= LogLevel.Info) { + this._logChannel.appendLine(this._format(arguments)); + } + } + + warn(_message: string, ..._args: any[]): void { + if (this.getLevel() <= LogLevel.Warning) { + this._logChannel.appendLine(this._format(arguments)); + } + } + + error(_message: string | Error, ..._args: any[]): void { + if (this.getLevel() <= LogLevel.Error) { + this._logChannel.appendLine(this._format(arguments)); + } + } + + critical(_message: string | Error, ..._args: any[]): void { + if (this.getLevel() <= LogLevel.Critical) { + this._logChannel.appendLine(String(arguments)); + } + } + + private _format(args: any): string { + let result = ''; + + for (let i = 0; i < args.length; i++) { + let a = args[i]; + + if (typeof a === 'object') { + try { + a = JSON.stringify(a); + } catch (e) { } + } + + result += (i > 0 ? ' ' : '') + a; + } + + return result; + } +} diff --git a/src/vs/workbench/services/extensions/common/extensionHostMain.ts b/src/vs/workbench/services/extensions/common/extensionHostMain.ts index d1c8e25185bab..555d4f2f7c821 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostMain.ts @@ -10,7 +10,6 @@ import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri'; import { IURITransformer } from 'vs/base/common/uriIpc'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { IInitData, MainContext, MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; import { RPCProtocol } from 'vs/workbench/services/extensions/common/rpcProtocol'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; @@ -65,21 +64,21 @@ export class ExtensionHostMain { consolePatchFn(rpcProtocol.getProxy(MainContext.MainThreadConsole)); // services - const extHostLogService = new ExtHostLogService(logServiceFn(initData), initData.logsLocation.fsPath); - this._disposables.add(extHostLogService); // bootstrap services const services = new ServiceCollection(...getSingletonServiceDescriptors()); services.set(IExtHostInitDataService, { _serviceBrand: undefined, ...initData }); services.set(IExtHostRpcService, new ExtHostRpcService(rpcProtocol)); - services.set(ILogService, extHostLogService); services.set(IURITransformerService, new URITransformerService(uriTransformer)); services.set(IHostUtils, hostUtils); const instaService: IInstantiationService = new InstantiationService(services, true); - extHostLogService.info('extension host started'); - extHostLogService.trace('initData', initData); + const logService = instaService.invokeFunction(accessor => accessor.get(ILogService)); + this._disposables.add(logService); + + logService.info('extension host started'); + logService.trace('initData', initData); // todo@joh -> not soo nice... this._extensionService = instaService.invokeFunction(accessor => accessor.get(IExtHostExtensionService)); diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 6aa6ba1e61797..64c2e0a526e59 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -17,11 +17,11 @@ import { IExtHostWorkspaceProvider } from 'vs/workbench/api/common/extHostWorksp import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; import { ProxyAgent } from 'vscode-proxy-agent'; import { MainThreadTelemetryShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { URI } from 'vs/base/common/uri'; import { promisify } from 'util'; +import { ILogService } from 'vs/platform/log/common/log'; interface ConnectionResult { proxy: string; @@ -34,7 +34,7 @@ export function connectProxyResolver( extHostWorkspace: IExtHostWorkspaceProvider, configProvider: ExtHostConfigProvider, extensionService: ExtHostExtensionService, - extHostLogService: ExtHostLogService, + extHostLogService: ILogService, mainThreadTelemetry: MainThreadTelemetryShape ) { const resolveProxy = setupProxyResolution(extHostWorkspace, configProvider, extHostLogService, mainThreadTelemetry); @@ -47,7 +47,7 @@ const maxCacheEntries = 5000; // Cache can grow twice that much due to 'oldCache function setupProxyResolution( extHostWorkspace: IExtHostWorkspaceProvider, configProvider: ExtHostConfigProvider, - extHostLogService: ExtHostLogService, + extHostLogService: ILogService, mainThreadTelemetry: MainThreadTelemetryShape ) { const env = process.env; @@ -421,7 +421,7 @@ function configureModuleLoading(extensionService: ExtHostExtensionService, looku }); } -function useSystemCertificates(extHostLogService: ExtHostLogService, useSystemCertificates: boolean, opts: http.RequestOptions, callback: () => void) { +function useSystemCertificates(extHostLogService: ILogService, useSystemCertificates: boolean, opts: http.RequestOptions, callback: () => void) { if (useSystemCertificates) { getCaCertificates(extHostLogService) .then(caCertificates => { @@ -443,7 +443,7 @@ function useSystemCertificates(extHostLogService: ExtHostLogService, useSystemCe } let _caCertificates: ReturnType | Promise; -async function getCaCertificates(extHostLogService: ExtHostLogService) { +async function getCaCertificates(extHostLogService: ILogService) { if (!_caCertificates) { _caCertificates = readCaCertificates() .then(res => res && res.certs.length ? res : undefined) diff --git a/src/vs/workbench/services/extensions/worker/extHost.services.ts b/src/vs/workbench/services/extensions/worker/extHost.services.ts index 127250f83be86..bf4a779155408 100644 --- a/src/vs/workbench/services/extensions/worker/extHost.services.ts +++ b/src/vs/workbench/services/extensions/worker/extHost.services.ts @@ -19,8 +19,11 @@ import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensi import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; import { ExtHostExtensionService } from 'vs/workbench/api/worker/extHostExtensionService'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ExtHostLogService } from 'vs/workbench/api/worker/extHostLogService'; // register singleton services +registerSingleton(ILogService, ExtHostLogService); registerSingleton(IExtHostOutputService, ExtHostOutputService); registerSingleton(IExtHostWorkspace, ExtHostWorkspace); registerSingleton(IExtHostDecorations, ExtHostDecorations); @@ -51,6 +54,4 @@ registerSingleton(IExtHostDebugService, class extends NotImplementedProxy(IExtHo registerSingleton(IExtHostSearch, class extends NotImplementedProxy(IExtHostSearch) { }); registerSingleton(IExtensionStoragePaths, class extends NotImplementedProxy(IExtensionStoragePaths) { whenReady = Promise.resolve(); - globalValue = () => ''; - workspaceValue = () => ''; }); diff --git a/tslint.json b/tslint.json index f5d3c5ba2ab64..a85f54758d42d 100644 --- a/tslint.json +++ b/tslint.json @@ -391,6 +391,14 @@ "**/vs/workbench/contrib/*/common/**" ] }, + { + "target": "**/vs/workbench/api/worker/**", + "restrictions": [ + "vscode", + "vs/nls", + "**/vs/**/{common,worker}/**" + ] + }, { "target": "**/vs/workbench/electron-browser/**", "restrictions": [ From 8ed95311cdf869c36aa14835487aeb5a34ea15a9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 12 Aug 2019 14:50:44 +0200 Subject: [PATCH 062/613] remove ILogServiceFn function --- .../services/extensions/common/extensionHostMain.ts | 5 ----- .../extensions/node/extensionHostProcessSetup.ts | 9 ++------- .../services/extensions/worker/extensionHostWorker.ts | 2 -- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/services/extensions/common/extensionHostMain.ts b/src/vs/workbench/services/extensions/common/extensionHostMain.ts index 555d4f2f7c821..7f6cd8d027850 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostMain.ts @@ -34,10 +34,6 @@ export interface IConsolePatchFn { (mainThreadConsole: MainThreadConsoleShape): any; } -export interface ILogServiceFn { - (initData: IInitData): ILogService; -} - export class ExtensionHostMain { private _isTerminating: boolean; @@ -50,7 +46,6 @@ export class ExtensionHostMain { initData: IInitData, hostUtils: IHostUtils, consolePatchFn: IConsolePatchFn, - logServiceFn: ILogServiceFn, uriTransformer: IURITransformer | null ) { this._isTerminating = false; diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index f708e29da7c02..0a067fb0a9fe8 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -14,14 +14,12 @@ import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net' import product from 'vs/platform/product/node/product'; import { IInitData, MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; -import { ExtensionHostMain, IExitFn, ILogServiceFn } from 'vs/workbench/services/extensions/common/extensionHostMain'; +import { ExtensionHostMain, IExitFn } from 'vs/workbench/services/extensions/common/extensionHostMain'; import { VSBuffer } from 'vs/base/common/buffer'; -import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; import { IURITransformer, URITransformer, IRawURITransformer } from 'vs/base/common/uriIpc'; import { exists } from 'vs/base/node/pfs'; import { realpath } from 'vs/base/node/extpath'; import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService'; -import { SpdLogService } from 'vs/platform/log/node/spdlogService'; import 'vs/workbench/api/node/extHost.services'; interface ParsedExtHostArgs { @@ -83,8 +81,6 @@ function patchPatchedConsole(mainThreadConsole: MainThreadConsoleShape): void { }; } -const createLogService: ILogServiceFn = initData => new SpdLogService(ExtensionHostLogFileName, initData.logsLocation.fsPath, initData.logLevel); - interface IRendererConnection { protocol: IMessagePassingProtocol; initData: IInitData; @@ -206,7 +202,7 @@ async function createExtHostProtocol(): Promise { } function connectToRenderer(protocol: IMessagePassingProtocol): Promise { - return new Promise((c, e) => { + return new Promise((c) => { // Listen init data message const first = protocol.onMessage(raw => { @@ -336,7 +332,6 @@ export async function startExtensionHostProcess(): Promise { initData, hostUtils, patchPatchedConsole, - createLogService, uriTransformer ); diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts index 7d42bf2044d3d..49bba4083da48 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts @@ -10,7 +10,6 @@ import { Emitter } from 'vs/base/common/event'; import { isMessageOfType, MessageType, createMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; import { ExtensionHostMain } from 'vs/workbench/services/extensions/common/extensionHostMain'; -import { ConsoleLogService } from 'vs/platform/log/common/log'; import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService'; import 'vs/workbench/services/extensions/worker/extHost.services'; @@ -114,7 +113,6 @@ export function create(postMessage: (message: any, transfer?: Transferable[]) => data.initData, hostUtil, () => { }, - () => new ConsoleLogService(), null, ); From 58a32d0b44841a6365cf14a9b07d66755f5c5df6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 12 Aug 2019 15:04:02 +0200 Subject: [PATCH 063/613] remove IConsolePatchFn function --- .../api/node/extHostExtensionService.ts | 10 ++++++++++ .../extensions/common/extensionHostMain.ts | 4 ---- .../extensions/node/extensionHostProcessSetup.ts | 16 +--------------- .../extensions/worker/extensionHostWorker.ts | 6 ------ 4 files changed, 11 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 99c3a6629d4cd..8f1f5d6ee75f0 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -43,6 +43,16 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { // Do this when extension service exists, but extensions are not being activated yet. await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._logService, this._mainThreadTelemetryProxy); + // Use IPC messages to forward console-calls, note that the console is + // already patched to use`process.send()` + const nativeProcessSend = process.send!; + const mainThreadConsole = this._extHostContext.getProxy(MainContext.MainThreadConsole); + process.send = (...args: any[]) => { + if (args.length === 0 || !args[0] || args[0].type !== '__$console') { + return nativeProcessSend.apply(process, args); + } + mainThreadConsole.$logExtensionHostMessage(args[0]); + }; } protected _loadCommonJSModule(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { diff --git a/src/vs/workbench/services/extensions/common/extensionHostMain.ts b/src/vs/workbench/services/extensions/common/extensionHostMain.ts index 7f6cd8d027850..625c60db92bb7 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostMain.ts @@ -45,7 +45,6 @@ export class ExtensionHostMain { protocol: IMessagePassingProtocol, initData: IInitData, hostUtils: IHostUtils, - consolePatchFn: IConsolePatchFn, uriTransformer: IURITransformer | null ) { this._isTerminating = false; @@ -55,9 +54,6 @@ export class ExtensionHostMain { // ensure URIs are transformed and revived initData = ExtensionHostMain._transform(initData, rpcProtocol); - // allow to patch console - consolePatchFn(rpcProtocol.getProxy(MainContext.MainThreadConsole)); - // services // bootstrap services diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index 0a067fb0a9fe8..1e88709312390 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -12,7 +12,7 @@ import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { PersistentProtocol, ProtocolConstants, createBufferedEvent } from 'vs/base/parts/ipc/common/ipc.net'; import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import product from 'vs/platform/product/node/product'; -import { IInitData, MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ExtensionHostMain, IExitFn } from 'vs/workbench/services/extensions/common/extensionHostMain'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -68,19 +68,6 @@ function patchProcess(allowExit: boolean) { }; } -// use IPC messages to forward console-calls -function patchPatchedConsole(mainThreadConsole: MainThreadConsoleShape): void { - // The console is already patched to use `process.send()` - const nativeProcessSend = process.send!; - process.send = (...args: any[]) => { - if (args.length === 0 || !args[0] || args[0].type !== '__$console') { - return nativeProcessSend.apply(process, args); - } - - mainThreadConsole.$logExtensionHostMessage(args[0]); - }; -} - interface IRendererConnection { protocol: IMessagePassingProtocol; initData: IInitData; @@ -331,7 +318,6 @@ export async function startExtensionHostProcess(): Promise { renderer.protocol, initData, hostUtils, - patchPatchedConsole, uriTransformer ); diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts index 49bba4083da48..3ed289be3977a 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts @@ -102,17 +102,11 @@ export function create(postMessage: (message: any, transfer?: Transferable[]) => const res = new ExtensionWorker(postMessage); connectToRenderer(res.protocol).then(data => { - // console.log('INIT_DATA', data.initData); - - // data.protocol.onMessage(msg => { - // // console.log('SOME MSG', msg.toString()); - // }); const extHostMain = new ExtensionHostMain( data.protocol, data.initData, hostUtil, - () => { }, null, ); From aac154c718c53d260c8390e7a554dacadd48e995 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 12 Aug 2019 15:08:53 +0200 Subject: [PATCH 064/613] :lipstick: --- .../services/extensions/common/extensionHostMain.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/extensions/common/extensionHostMain.ts b/src/vs/workbench/services/extensions/common/extensionHostMain.ts index 625c60db92bb7..16356a4a96cba 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostMain.ts @@ -54,8 +54,6 @@ export class ExtensionHostMain { // ensure URIs are transformed and revived initData = ExtensionHostMain._transform(initData, rpcProtocol); - // services - // bootstrap services const services = new ServiceCollection(...getSingletonServiceDescriptors()); services.set(IExtHostInitDataService, { _serviceBrand: undefined, ...initData }); @@ -65,13 +63,18 @@ export class ExtensionHostMain { const instaService: IInstantiationService = new InstantiationService(services, true); + // todo@joh + // ugly self - inject const logService = instaService.invokeFunction(accessor => accessor.get(ILogService)); this._disposables.add(logService); logService.info('extension host started'); logService.trace('initData', initData); - // todo@joh -> not soo nice... + // todo@joh + // ugly self - inject + // must call initialize *after* creating the extension service + // because `initialize` itself creates instances that depend on it this._extensionService = instaService.invokeFunction(accessor => accessor.get(IExtHostExtensionService)); this._extensionService.initialize(); From 3611aaf54fb6bdc1515f010ee932aafc02335221 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 12 Aug 2019 15:21:08 +0200 Subject: [PATCH 065/613] add autoStart property --- .../workbench/services/extensions/browser/extensionService.ts | 2 +- .../extensions/browser/webWorkerExtensionHostStarter.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index cd80cb266fab7..a32024e35aca2 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -65,7 +65,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten const remoteAgentConnection = this._remoteAgentService.getConnection()!; - const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, Promise.resolve([]), URI.parse('empty:value')); //todo@joh + const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, Promise.resolve([]), URI.parse('empty:value')); //todo@joh const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); result.push(webHostProcessManager); diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index a88545f4e31d2..1f04364fa5d11 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -31,7 +31,7 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { readonly onExit: Event<[number, string | null]> = this._onDidExit.event; constructor( - // private readonly _autoStart: boolean, + private readonly _autoStart: boolean, private readonly _extensions: Promise, private readonly _extensionHostLogsLocation: URI, @ITelemetryService private readonly _telemetryService: ITelemetryService, @@ -139,7 +139,7 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: this._extensionHostLogsLocation, - autoStart: true, + autoStart: this._autoStart, remote: { authority: this._environmentService.configuration.remoteAuthority, isRemote: false From 6861a68ec4edd9fa9af6af951d33a8789e1e1451 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 12 Aug 2019 17:05:24 +0200 Subject: [PATCH 066/613] Use env path to resolve executable in TerminalProcess (#78940) Candidate fix for #78898 --- src/vs/workbench/contrib/terminal/node/terminalProcess.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts index 2fbc8a25e7462..86a63a0b8439b 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts @@ -89,7 +89,9 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess }, async (err) => { if (err && err.code === 'ENOENT') { let cwd = shellLaunchConfig.cwd instanceof URI ? shellLaunchConfig.cwd.path : shellLaunchConfig.cwd!; - const executable = await findExecutable(shellLaunchConfig.executable!, cwd); + // Try to get path + const envPaths: string[] | undefined = (shellLaunchConfig.env && shellLaunchConfig.env.PATH) ? shellLaunchConfig.env.PATH.split(path.delimiter) : undefined; + const executable = await findExecutable(shellLaunchConfig.executable!, cwd, envPaths); if (!executable) { return Promise.reject(SHELL_PATH_INVALID_EXIT_CODE); } From 2b40fd525c193396bfa0ab2cc79963adfe56dff2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 12 Aug 2019 09:41:13 -0700 Subject: [PATCH 067/613] Update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 076ad75b4b7a7..5e9d2b5243770 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "462afd2bf9dd37b39205412486925bc10b7fbf69", + "distro": "361c5503f7168938c8752fa5964f1b658fa75178", "author": { "name": "Microsoft Corporation" }, @@ -157,4 +157,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} \ No newline at end of file +} From 4106fe8259fad9da00dba64e8b58afff212b5d34 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 21 Jun 2019 01:03:50 +0000 Subject: [PATCH 068/613] merge conflicts --- src/buildfile.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/buildfile.js b/src/buildfile.js index e45dbc2746158..f9946efc7038b 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +function entrypoint (name) { + return [{ name: name, include: [], exclude: ['vs/css', 'vs/nls'] }]; +} + exports.base = [{ name: 'vs/base/common/worker/simpleWorker', include: ['vs/editor/common/services/editorSimpleWorker'], @@ -20,10 +24,14 @@ exports.serviceWorker = [{ }]; exports.workbench = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.desktop.main']); -exports.workbenchWeb = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.web.api']); +exports.workbenchWeb = entrypoint('vs/workbench/workbench.web.api'); + +exports.keyboardMaps = [ + entrypoint('vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.linux'), + entrypoint('vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.darwin'), + entrypoint('vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.win') +]; exports.code = require('./vs/code/buildfile').collectModules(); -exports.entrypoint = function (name) { - return [{ name: name, include: [], exclude: ['vs/css', 'vs/nls'] }]; -}; +exports.entrypoint = entrypoint; From bdb1a49ebba5e8b321a9fd3c623e3d8de99fda45 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 12 Aug 2019 10:04:12 -0700 Subject: [PATCH 069/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5e9d2b5243770..bab0d04717253 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "361c5503f7168938c8752fa5964f1b658fa75178", + "distro": "99979734e2234cdb1e7f4988339c5df3fd3aa521", "author": { "name": "Microsoft Corporation" }, From dc8bbb259ec78c4f235a8f3d7386b5b6b6551f86 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 12 Aug 2019 10:28:26 -0700 Subject: [PATCH 070/613] Fix #78884 --- src/vs/workbench/browser/workbench.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 7c36ea51726f7..8bd0501679d90 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -246,7 +246,7 @@ import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform' // Window let windowTitleDescription = nls.localize('windowTitle', "Controls the window title based on the active editor. Variables are substituted based on the context:"); - windowTitleDescription += [ + windowTitleDescription += '\n- ' + [ nls.localize('activeEditorShort', "`\${activeEditorShort}`: the file name (e.g. myFile.txt)."), nls.localize('activeEditorMedium', "`\${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt)."), nls.localize('activeEditorLong', "`\${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt)."), From 8ffdc18c1d6bd9b7a5e5d65a0efb7cf29ae65ba0 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 12 Aug 2019 10:19:35 -0700 Subject: [PATCH 071/613] Use application insights module, #78475, fixes #78840 --- package.json | 1 + remote/package.json | 1 + remote/web/package.json | 1 + remote/web/yarn.lock | 68 +++++++++++++++++ remote/yarn.lock | 68 +++++++++++++++++ src/typings/applicationinsights-web.d.ts | 59 +++++++++++++++ src/vs/code/browser/workbench/workbench.html | 3 - src/vs/code/browser/workbench/workbench.js | 3 +- .../telemetry/browser/telemetryService.ts | 73 +++---------------- tslint.json | 3 +- yarn.lock | 68 +++++++++++++++++ 11 files changed, 280 insertions(+), 68 deletions(-) create mode 100644 src/typings/applicationinsights-web.d.ts diff --git a/package.json b/package.json index bab0d04717253..71f19c8c72aaf 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "update-distro": "node build/npm/update-distro.js" }, "dependencies": { + "@microsoft/applicationinsights-web": "^2.1.1", "applicationinsights": "1.0.8", "graceful-fs": "4.1.11", "http-proxy-agent": "^2.1.0", diff --git a/remote/package.json b/remote/package.json index 4547f9eed803a..8cae3c0ed4501 100644 --- a/remote/package.json +++ b/remote/package.json @@ -2,6 +2,7 @@ "name": "vscode-reh", "version": "0.0.0", "dependencies": { + "@microsoft/applicationinsights-web": "^2.1.1", "applicationinsights": "1.0.8", "getmac": "1.4.1", "graceful-fs": "4.1.11", diff --git a/remote/web/package.json b/remote/web/package.json index c7a487b6c2d3d..f54afe1a3e6e8 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -2,6 +2,7 @@ "name": "vscode-web", "version": "0.0.0", "dependencies": { + "@microsoft/applicationinsights-web": "^2.1.1", "onigasm-umd": "^2.2.2", "vscode-textmate": "^4.1.1", "xterm": "3.15.0-beta67", diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index b624eb3729027..23ad784a554f5 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -2,6 +2,69 @@ # yarn lockfile v1 +"@microsoft/applicationinsights-analytics-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-analytics-js/-/applicationinsights-analytics-js-2.1.1.tgz#6d09c1915f808026e2d45165d04802f09affed59" + integrity sha512-VKIutoFKY99CyKwxLUuj6Vnq14/QwXo9/QSQDpYnHEjo+uKn7QmLsHqWw0K9uYNfNAXt4BZimX/zDg6jZtzeXg== + dependencies: + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-channel-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.1.1.tgz#e205eddd93e49d17d9e0711a612b4bfc9810888f" + integrity sha512-fYr9IAqtaEr9AmaPaL3SLQVT3t3GQzl+n74gpNKyAVakDIm0nYQ/bimjdcAhJMDf1VGNSPg/xICneyuZg7Wxlg== + dependencies: + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-common@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.1.1.tgz#27e6074584a7a3a8ca3f11f7ff2b7ff0f395bf2d" + integrity sha512-2hkS1Ia1FmAjCuYZ5JlG20/WgObqdsKtmK5YALAFGHIB4KSQ/Za1qazS+7GsG+E0F9UJivNWL1geUIcNqg5Qjg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-core-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.1.1.tgz#30fb6a519cc1c6119c419c4811ce72c260217d9e" + integrity sha512-4t4wf6SKqIcWEQDPg/uOhm+BxtHhu/AFreyEoYZmMfcxzAu33h1FtTQRtxBNbYH1+thiNZCh80yUpnT7d9Hrlw== + dependencies: + tslib "^1.9.3" + +"@microsoft/applicationinsights-dependencies-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-dependencies-js/-/applicationinsights-dependencies-js-2.1.1.tgz#8154c3efcb24617d015d0bce7c2cc47797a8d3c4" + integrity sha512-yhb4EToBp+aI+qLo0h5NDNtoo3sDFV60uyIOK843YjzXqVotcXX/lRShlghTkJtYH09QhrdzDjViUHnD4sMFSQ== + dependencies: + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-properties-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-properties-js/-/applicationinsights-properties-js-2.1.1.tgz#ca34232766eb16167b5d87693e2ae5d94f2a1559" + integrity sha512-8l+/ppw6xKTam2RL4EHZ52Lcf217olw81j6kyBNKtIcGwSnLNHrFwEeF3vBWIteG2JKzlg1GhGjrkB3oxXsV2g== + dependencies: + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-web@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web/-/applicationinsights-web-2.1.1.tgz#1a44eddda7c244b88d9eb052dab6c855682e4f05" + integrity sha512-crvhCkNsNxkFuPWmttyWNSAA96D5FxBtKS6UA9MV9f9XHevTfchf/E3AuU9JZcsXufWMQLwLrUQ9ZiA1QJ0EWA== + dependencies: + "@microsoft/applicationinsights-analytics-js" "2.1.1" + "@microsoft/applicationinsights-channel-js" "2.1.1" + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + "@microsoft/applicationinsights-dependencies-js" "2.1.1" + "@microsoft/applicationinsights-properties-js" "2.1.1" + nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" @@ -24,6 +87,11 @@ semver-umd@^5.5.3: resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.3.tgz#b64d7a2d4f5a717b369d56e31940a38e47e34d1e" integrity sha512-HOnQrn2iKnVe/xlqCTzMXQdvSz3rPbD0DmQXYuQ+oK1dpptGFfPghonQrx5JHl2O7EJwDqtQnjhE7ME23q6ngw== +tslib@^1.9.3: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + vscode-textmate@^4.1.1: version "4.2.2" resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.2.2.tgz#0b4dabc69a6fba79a065cb6b615f66eac07c8f4c" diff --git a/remote/yarn.lock b/remote/yarn.lock index 3e5d4690cf7a9..ca4abc44e3700 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -2,6 +2,69 @@ # yarn lockfile v1 +"@microsoft/applicationinsights-analytics-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-analytics-js/-/applicationinsights-analytics-js-2.1.1.tgz#6d09c1915f808026e2d45165d04802f09affed59" + integrity sha512-VKIutoFKY99CyKwxLUuj6Vnq14/QwXo9/QSQDpYnHEjo+uKn7QmLsHqWw0K9uYNfNAXt4BZimX/zDg6jZtzeXg== + dependencies: + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-channel-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.1.1.tgz#e205eddd93e49d17d9e0711a612b4bfc9810888f" + integrity sha512-fYr9IAqtaEr9AmaPaL3SLQVT3t3GQzl+n74gpNKyAVakDIm0nYQ/bimjdcAhJMDf1VGNSPg/xICneyuZg7Wxlg== + dependencies: + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-common@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.1.1.tgz#27e6074584a7a3a8ca3f11f7ff2b7ff0f395bf2d" + integrity sha512-2hkS1Ia1FmAjCuYZ5JlG20/WgObqdsKtmK5YALAFGHIB4KSQ/Za1qazS+7GsG+E0F9UJivNWL1geUIcNqg5Qjg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-core-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.1.1.tgz#30fb6a519cc1c6119c419c4811ce72c260217d9e" + integrity sha512-4t4wf6SKqIcWEQDPg/uOhm+BxtHhu/AFreyEoYZmMfcxzAu33h1FtTQRtxBNbYH1+thiNZCh80yUpnT7d9Hrlw== + dependencies: + tslib "^1.9.3" + +"@microsoft/applicationinsights-dependencies-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-dependencies-js/-/applicationinsights-dependencies-js-2.1.1.tgz#8154c3efcb24617d015d0bce7c2cc47797a8d3c4" + integrity sha512-yhb4EToBp+aI+qLo0h5NDNtoo3sDFV60uyIOK843YjzXqVotcXX/lRShlghTkJtYH09QhrdzDjViUHnD4sMFSQ== + dependencies: + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-properties-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-properties-js/-/applicationinsights-properties-js-2.1.1.tgz#ca34232766eb16167b5d87693e2ae5d94f2a1559" + integrity sha512-8l+/ppw6xKTam2RL4EHZ52Lcf217olw81j6kyBNKtIcGwSnLNHrFwEeF3vBWIteG2JKzlg1GhGjrkB3oxXsV2g== + dependencies: + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-web@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web/-/applicationinsights-web-2.1.1.tgz#1a44eddda7c244b88d9eb052dab6c855682e4f05" + integrity sha512-crvhCkNsNxkFuPWmttyWNSAA96D5FxBtKS6UA9MV9f9XHevTfchf/E3AuU9JZcsXufWMQLwLrUQ9ZiA1QJ0EWA== + dependencies: + "@microsoft/applicationinsights-analytics-js" "2.1.1" + "@microsoft/applicationinsights-channel-js" "2.1.1" + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + "@microsoft/applicationinsights-dependencies-js" "2.1.1" + "@microsoft/applicationinsights-properties-js" "2.1.1" + agent-base@4, agent-base@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce" @@ -1024,6 +1087,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +tslib@^1.9.3: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + typechecker@^4.3.0: version "4.7.0" resolved "https://registry.yarnpkg.com/typechecker/-/typechecker-4.7.0.tgz#5249f427358f45b7250c4924fd4d01ed9ba435e9" diff --git a/src/typings/applicationinsights-web.d.ts b/src/typings/applicationinsights-web.d.ts new file mode 100644 index 0000000000000..5af4903525c72 --- /dev/null +++ b/src/typings/applicationinsights-web.d.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module '@microsoft/applicationinsights-web' { + export interface IConfig { + instrumentationKey?: string; + endpointUrl?: string; + emitLineDelimitedJson?: boolean; + accountId?: string; + sessionRenewalMs?: number; + sessionExpirationMs?: number; + maxBatchSizeInBytes?: number; + maxBatchInterval?: number; + enableDebug?: boolean; + disableExceptionTracking?: boolean; + disableTelemetry?: boolean; + verboseLogging?: boolean; + diagnosticLogInterval?: number; + samplingPercentage?: number; + autoTrackPageVisitTime?: boolean; + disableAjaxTracking?: boolean; + overridePageViewDuration?: boolean; + maxAjaxCallsPerView?: number; + disableDataLossAnalysis?: boolean; + disableCorrelationHeaders?: boolean; + correlationHeaderExcludedDomains?: string[]; + disableFlushOnBeforeUnload?: boolean; + enableSessionStorageBuffer?: boolean; + isCookieUseDisabled?: boolean; + cookieDomain?: string; + isRetryDisabled?: boolean; + url?: string; + isStorageUseDisabled?: boolean; + isBeaconApiDisabled?: boolean; + sdkExtension?: string; + isBrowserLinkTrackingEnabled?: boolean; + appId?: string; + enableCorsCorrelation?: boolean; + } + + export interface ISnippet { + config: IConfig; + } + + export interface IEventTelemetry { + name: string; + properties?: { [key: string]: string }; + measurements?: { [key: string]: number }; + } + + export class ApplicationInsights { + constructor(config: ISnippet); + loadAppInsights(): void; + trackEvent(data: IEventTelemetry): void; + flush(): void; + } +} diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 5b06636edbbc6..9b8e42d3a3bd5 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -24,9 +24,6 @@ - - - diff --git a/src/vs/code/browser/workbench/workbench.js b/src/vs/code/browser/workbench/workbench.js index 65fae7c82df83..240ca34df05ed 100644 --- a/src/vs/code/browser/workbench/workbench.js +++ b/src/vs/code/browser/workbench/workbench.js @@ -16,6 +16,7 @@ 'xterm-addon-search': `${window.location.origin}/node_modules/xterm-addon-search/lib/xterm-addon-search.js`, 'xterm-addon-web-links': `${window.location.origin}/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`, 'semver-umd': `${window.location.origin}/node_modules/semver-umd/lib/semver-umd.js`, + '@microsoft/applicationinsights-web': `${window.location.origin}/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js`, } }); @@ -24,4 +25,4 @@ api.create(document.body, options); }); -})(); \ No newline at end of file +})(); diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index 652aaf3a2de0d..eac9dcfb1d0a7 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -15,67 +15,10 @@ import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/pla import { IStorageService } from 'vs/platform/storage/common/storage'; import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/browser/workbenchCommonProperties'; import { IProductService } from 'vs/platform/product/common/product'; - -interface IConfig { - instrumentationKey?: string; - endpointUrl?: string; - emitLineDelimitedJson?: boolean; - accountId?: string; - sessionRenewalMs?: number; - sessionExpirationMs?: number; - maxBatchSizeInBytes?: number; - maxBatchInterval?: number; - enableDebug?: boolean; - disableExceptionTracking?: boolean; - disableTelemetry?: boolean; - verboseLogging?: boolean; - diagnosticLogInterval?: number; - samplingPercentage?: number; - autoTrackPageVisitTime?: boolean; - disableAjaxTracking?: boolean; - overridePageViewDuration?: boolean; - maxAjaxCallsPerView?: number; - disableDataLossAnalysis?: boolean; - disableCorrelationHeaders?: boolean; - correlationHeaderExcludedDomains?: string[]; - disableFlushOnBeforeUnload?: boolean; - enableSessionStorageBuffer?: boolean; - isCookieUseDisabled?: boolean; - cookieDomain?: string; - isRetryDisabled?: boolean; - url?: string; - isStorageUseDisabled?: boolean; - isBeaconApiDisabled?: boolean; - sdkExtension?: string; - isBrowserLinkTrackingEnabled?: boolean; - appId?: string; - enableCorsCorrelation?: boolean; -} - -declare class Microsoft { - public static ApplicationInsights: { - Initialization: { - new(init: { config: IConfig }): AppInsights; - } - }; -} - -declare interface IAppInsightsClient { - config: IConfig; - - /** Log a user action or other occurrence. */ - trackEvent: (name: string, properties?: { [key: string]: string }, measurements?: { [key: string]: number }) => void; - - /** Immediately send all queued telemetry. Synchronous. */ - flush(): void; -} - -interface AppInsights { - loadAppInsights: () => IAppInsightsClient; -} +import { ApplicationInsights } from '@microsoft/applicationinsights-web'; export class WebTelemetryAppender implements ITelemetryAppender { - private _aiClient?: IAppInsightsClient; + private _aiClient?: ApplicationInsights; constructor(aiKey: string, private _logService: ILogService) { const initConfig = { @@ -89,8 +32,8 @@ export class WebTelemetryAppender implements ITelemetryAppender { } }; - const appInsights = new Microsoft.ApplicationInsights.Initialization(initConfig); - this._aiClient = appInsights.loadAppInsights(); + this._aiClient = new ApplicationInsights(initConfig); + this._aiClient.loadAppInsights(); } log(eventName: string, data: any): void { @@ -101,7 +44,11 @@ export class WebTelemetryAppender implements ITelemetryAppender { data = validateTelemetryData(data); this._logService.trace(`telemetry/${eventName}`, data); - this._aiClient.trackEvent('monacoworkbench/' + eventName, data.properties, data.measurements); + this._aiClient.trackEvent({ + name: 'monacoworkbench/' + eventName, + properties: data.properties, + measurements: data.measurements + }); } flush(): Promise { @@ -167,4 +114,4 @@ export class TelemetryService extends Disposable implements ITelemetryService { } } -registerSingleton(ITelemetryService, TelemetryService); \ No newline at end of file +registerSingleton(ITelemetryService, TelemetryService); diff --git a/tslint.json b/tslint.json index 733a56ac409fe..e10b559308db6 100644 --- a/tslint.json +++ b/tslint.json @@ -446,7 +446,8 @@ "**/vs/workbench/{common,browser}/**", "**/vs/workbench/services/**/{common,browser}/**", "vscode-textmate", - "onigasm-umd" + "onigasm-umd", + "@microsoft/applicationinsights-web" ] }, { diff --git a/yarn.lock b/yarn.lock index 6eafd48d052de..62a825ad5948f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -95,6 +95,69 @@ lodash "^4.17.11" to-fast-properties "^2.0.0" +"@microsoft/applicationinsights-analytics-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-analytics-js/-/applicationinsights-analytics-js-2.1.1.tgz#6d09c1915f808026e2d45165d04802f09affed59" + integrity sha512-VKIutoFKY99CyKwxLUuj6Vnq14/QwXo9/QSQDpYnHEjo+uKn7QmLsHqWw0K9uYNfNAXt4BZimX/zDg6jZtzeXg== + dependencies: + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-channel-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.1.1.tgz#e205eddd93e49d17d9e0711a612b4bfc9810888f" + integrity sha512-fYr9IAqtaEr9AmaPaL3SLQVT3t3GQzl+n74gpNKyAVakDIm0nYQ/bimjdcAhJMDf1VGNSPg/xICneyuZg7Wxlg== + dependencies: + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-common@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.1.1.tgz#27e6074584a7a3a8ca3f11f7ff2b7ff0f395bf2d" + integrity sha512-2hkS1Ia1FmAjCuYZ5JlG20/WgObqdsKtmK5YALAFGHIB4KSQ/Za1qazS+7GsG+E0F9UJivNWL1geUIcNqg5Qjg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-core-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.1.1.tgz#30fb6a519cc1c6119c419c4811ce72c260217d9e" + integrity sha512-4t4wf6SKqIcWEQDPg/uOhm+BxtHhu/AFreyEoYZmMfcxzAu33h1FtTQRtxBNbYH1+thiNZCh80yUpnT7d9Hrlw== + dependencies: + tslib "^1.9.3" + +"@microsoft/applicationinsights-dependencies-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-dependencies-js/-/applicationinsights-dependencies-js-2.1.1.tgz#8154c3efcb24617d015d0bce7c2cc47797a8d3c4" + integrity sha512-yhb4EToBp+aI+qLo0h5NDNtoo3sDFV60uyIOK843YjzXqVotcXX/lRShlghTkJtYH09QhrdzDjViUHnD4sMFSQ== + dependencies: + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-properties-js@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-properties-js/-/applicationinsights-properties-js-2.1.1.tgz#ca34232766eb16167b5d87693e2ae5d94f2a1559" + integrity sha512-8l+/ppw6xKTam2RL4EHZ52Lcf217olw81j6kyBNKtIcGwSnLNHrFwEeF3vBWIteG2JKzlg1GhGjrkB3oxXsV2g== + dependencies: + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + tslib "^1.9.3" + +"@microsoft/applicationinsights-web@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web/-/applicationinsights-web-2.1.1.tgz#1a44eddda7c244b88d9eb052dab6c855682e4f05" + integrity sha512-crvhCkNsNxkFuPWmttyWNSAA96D5FxBtKS6UA9MV9f9XHevTfchf/E3AuU9JZcsXufWMQLwLrUQ9ZiA1QJ0EWA== + dependencies: + "@microsoft/applicationinsights-analytics-js" "2.1.1" + "@microsoft/applicationinsights-channel-js" "2.1.1" + "@microsoft/applicationinsights-common" "2.1.1" + "@microsoft/applicationinsights-core-js" "2.1.1" + "@microsoft/applicationinsights-dependencies-js" "2.1.1" + "@microsoft/applicationinsights-properties-js" "2.1.1" + "@types/commander@^2.11.0": version "2.12.2" resolved "https://registry.yarnpkg.com/@types/commander/-/commander-2.12.2.tgz#183041a23842d4281478fa5d23c5ca78e6fd08ae" @@ -8961,6 +9024,11 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== +tslib@^1.9.3: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + tslint@^5.16.0: version "5.16.0" resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.16.0.tgz#ae61f9c5a98d295b9a4f4553b1b1e831c1984d67" From 1f4c94ecb81fc00094868dbc214a82ed69653e63 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 12 Aug 2019 10:47:42 -0700 Subject: [PATCH 072/613] Pick up TS 3.6 dev For #78973 --- extensions/package.json | 2 +- extensions/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index 52ece5e2051d0..57d5f5da31157 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.5.2" + "typescript": "3.6.0-dev.20190810" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 8e552194c13ec..233cf189e1462 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.2.tgz#a09e1dc69bc9551cadf17dba10ee42cf55e5d56c" - integrity sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA== +typescript@3.6.0-dev.20190810: + version "3.6.0-dev.20190810" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.0-dev.20190810.tgz#dda80279480131eec9b05e3b78182a1ba1efe105" + integrity sha512-gubcQ8Sn2G5AO1KhjvLpoFrutV7o/ZJ7wCDBC1IKgNI8R2vadIxTystJxAFqkb9boQ7tyRrZ6FwM5EL5ZYfJrg== From cafd7658a7835cf7cdf2a2f6b0391e4845f0b3aa Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 12 Aug 2019 11:58:55 -0500 Subject: [PATCH 073/613] Add CURRENT_SECONDS_UNIX snippet variable --- src/vs/editor/contrib/snippet/snippetVariables.ts | 3 +++ src/vs/editor/contrib/snippet/test/snippetVariables.test.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/src/vs/editor/contrib/snippet/snippetVariables.ts b/src/vs/editor/contrib/snippet/snippetVariables.ts index b56f15882dcf7..a1c080d2b4537 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -27,6 +27,7 @@ export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze( 'CURRENT_DAY_NAME_SHORT': true, 'CURRENT_MONTH_NAME': true, 'CURRENT_MONTH_NAME_SHORT': true, + 'CURRENT_SECONDS_UNIX': true, 'SELECTION': true, 'CLIPBOARD': true, 'TM_SELECTED_TEXT': true, @@ -245,6 +246,8 @@ export class TimeBasedVariableResolver implements VariableResolver { return TimeBasedVariableResolver.monthNames[new Date().getMonth()]; } else if (name === 'CURRENT_MONTH_NAME_SHORT') { return TimeBasedVariableResolver.monthNamesShort[new Date().getMonth()]; + } else if (name === 'CURRENT_SECONDS_UNIX') { + return String(Math.floor(Date.now() / 1000)); } return undefined; diff --git a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts index 7a5367085e77f..b4412f1d13006 100644 --- a/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetVariables.test.ts @@ -281,6 +281,7 @@ suite('Snippet Variables Resolver', function () { assertVariableResolve3(resolver, 'CURRENT_DAY_NAME_SHORT'); assertVariableResolve3(resolver, 'CURRENT_MONTH_NAME'); assertVariableResolve3(resolver, 'CURRENT_MONTH_NAME_SHORT'); + assertVariableResolve3(resolver, 'CURRENT_SECONDS_UNIX'); }); test('creating snippet - format-condition doesn\'t work #53617', function () { From 5ea298a88f8528bfdd0d08fab08dbe1267b0eb7a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 12 Aug 2019 11:54:39 -0700 Subject: [PATCH 074/613] Bump ripgrep for remote too --- remote/package.json | 2 +- remote/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/remote/package.json b/remote/package.json index 8cae3c0ed4501..d3ad3afaed721 100644 --- a/remote/package.json +++ b/remote/package.json @@ -19,7 +19,7 @@ "vscode-chokidar": "2.1.7", "vscode-minimist": "^1.2.1", "vscode-proxy-agent": "0.4.0", - "vscode-ripgrep": "^1.5.5", + "vscode-ripgrep": "^1.5.6", "vscode-textmate": "^4.2.2", "xterm": "3.15.0-beta98", "xterm-addon-search": "0.2.0-beta3", diff --git a/remote/yarn.lock b/remote/yarn.lock index ca4abc44e3700..b271a9fd5cc14 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -1191,10 +1191,10 @@ vscode-proxy-agent@0.4.0: https-proxy-agent "2.2.1" socks-proxy-agent "4.0.1" -vscode-ripgrep@^1.5.5: - version "1.5.5" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.5.tgz#24c0e9cb356cf889c98e15ecb58f9cf654a1d961" - integrity sha512-OrPrAmcun4+uZAuNcQvE6CCPskh+5AsjANod/Q3zRcJcGNxgoOSGlQN9RPtatkUNmkN8Nn8mZBnS1jMylu/dKg== +vscode-ripgrep@^1.5.6: + version "1.5.6" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.6.tgz#93bf5c99ca5f8248950a305e224f6ca153c30af4" + integrity sha512-WRIM9XpUj6dsfdAmuI3ANbmT1ysPUVsYy/2uCLDHJa9kbiB4T7uGvFnnc0Rgx2qQnyRAwL7PeWaFgUljPPxf2g== vscode-textmate@^4.2.2: version "4.2.2" From 0c87c73ce54901bed44df7aa7e1cb3f099c39263 Mon Sep 17 00:00:00 2001 From: Jarnin Fang Date: Mon, 12 Aug 2019 12:00:12 -0700 Subject: [PATCH 075/613] Issue 78480: Clear Filter command link includes dot --- src/vs/workbench/contrib/markers/browser/markersPanel.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/markers/browser/markersPanel.ts b/src/vs/workbench/contrib/markers/browser/markersPanel.ts index 71b13da14132f..36f1b9bd36576 100644 --- a/src/vs/workbench/contrib/markers/browser/markersPanel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersPanel.ts @@ -534,8 +534,10 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { const span1 = dom.append(container, dom.$('span')); span1.textContent = Messages.MARKERS_PANEL_NO_PROBLEMS_FILTERS; const link = dom.append(container, dom.$('a.messageAction')); - link.textContent = localize('clearFilter', "Clear Filter."); + link.textContent = localize('clearFilter', "Clear Filter"); link.setAttribute('tabIndex', '0'); + const span2 = dom.append(container, dom.$('span')); + span2.textContent = '.'; dom.addStandardDisposableListener(link, dom.EventType.CLICK, () => this.filterAction.filterText = ''); dom.addStandardDisposableListener(link, dom.EventType.KEY_DOWN, (e: IKeyboardEvent) => { if (e.equals(KeyCode.Enter) || e.equals(KeyCode.Space)) { From eb8fe0e1ee38cab923d678314dc2af761da230bc Mon Sep 17 00:00:00 2001 From: Chris May Date: Sat, 10 Aug 2019 21:47:12 +0100 Subject: [PATCH 076/613] Add a configuration option to show/hide icons in the breadcrumbs view --- .../workbench/browser/parts/editor/breadcrumbs.ts | 6 ++++++ .../browser/parts/editor/breadcrumbsControl.ts | 13 ++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts index 85aed7a9bf40e..0bd6c69fbdc3f 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts @@ -71,6 +71,7 @@ export abstract class BreadcrumbsConfig { static FilePath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.filePath'); static SymbolPath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.symbolPath'); static SymbolSortOrder = BreadcrumbsConfig._stub<'position' | 'name' | 'type'>('breadcrumbs.symbolSortOrder'); + static Icons = BreadcrumbsConfig._stub('breadcrumbs.icons'); static FileExcludes = BreadcrumbsConfig._stub('files.exclude'); @@ -160,6 +161,11 @@ Registry.as(Extensions.Configuration).registerConfigurat localize('symbolSortOrder.name', "Show symbol outline in alphabetical order."), localize('symbolSortOrder.type', "Show symbol outline in symbol type order."), ] + }, + 'breadcrumbs.icons': { + description: localize('icons', "Render breadcrumb items with icons."), + type: 'boolean', + default: true } } }); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 0614722d53682..60bb6b091a89f 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -143,6 +143,7 @@ export class BreadcrumbsControl { private readonly _ckBreadcrumbsActive: IContextKey; private readonly _cfUseQuickPick: BreadcrumbsConfig; + private readonly _cfShowIcons: BreadcrumbsConfig; readonly domNode: HTMLDivElement; private readonly _widget: BreadcrumbsWidget; @@ -185,6 +186,7 @@ export class BreadcrumbsControl { this._ckBreadcrumbsActive = BreadcrumbsControl.CK_BreadcrumbsActive.bindTo(this._contextKeyService); this._cfUseQuickPick = BreadcrumbsConfig.UseQuickPick.bindTo(_configurationService); + this._cfShowIcons = BreadcrumbsConfig.Icons.bindTo(_configurationService); this._disposables.add(breadcrumbsService.register(this._editorGroup.id, this._widget)); } @@ -196,6 +198,7 @@ export class BreadcrumbsControl { this._ckBreadcrumbsVisible.reset(); this._ckBreadcrumbsActive.reset(); this._cfUseQuickPick.dispose(); + this._cfShowIcons.dispose(); this._widget.dispose(); this.domNode.remove(); } @@ -246,15 +249,23 @@ export class BreadcrumbsControl { dom.toggleClass(this.domNode, 'backslash-path', this._labelService.getSeparator(uri.scheme, uri.authority) === '\\'); const updateBreadcrumbs = () => { - const items = model.getElements().map(element => new Item(element, this._options, this._instantiationService)); + const showIcons = this._cfShowIcons.getValue(); + const options: IBreadcrumbsControlOptions = { + ...this._options, + showFileIcons: this._options.showFileIcons && showIcons, + showSymbolIcons: this._options.showSymbolIcons && showIcons + }; + const items = model.getElements().map(element => new Item(element, options, this._instantiationService)); this._widget.setItems(items); this._widget.reveal(items[items.length - 1]); }; const listener = model.onDidUpdate(updateBreadcrumbs); + const configListener = this._cfShowIcons.onDidChange(updateBreadcrumbs); updateBreadcrumbs(); this._breadcrumbsDisposables.clear(); this._breadcrumbsDisposables.add(model); this._breadcrumbsDisposables.add(listener); + this._breadcrumbsDisposables.add(configListener); // close picker on hide/update this._breadcrumbsDisposables.add({ From e133b267684430e3c5be0ce4ea55f90f318b853b Mon Sep 17 00:00:00 2001 From: Chris May Date: Sun, 11 Aug 2019 10:00:41 +0100 Subject: [PATCH 077/613] Modify Breadcrumb Item equality test to include icon display option --- src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 60bb6b091a89f..39927e683a4d7 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -69,7 +69,9 @@ class Item extends BreadcrumbsItem { return false; } if (this.element instanceof FileElement && other.element instanceof FileElement) { - return isEqual(this.element.uri, other.element.uri, false); + return (isEqual(this.element.uri, other.element.uri, false) && + this.options.showFileIcons === other.options.showFileIcons && + this.options.showSymbolIcons === other.options.showSymbolIcons); } if (this.element instanceof TreeElement && other.element instanceof TreeElement) { return this.element.id === other.element.id; From 240c2079c38c76296671fd8e14c6a574efc3c1f0 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 12 Aug 2019 13:39:21 -0700 Subject: [PATCH 078/613] Add workspace.id property to stats telemetry --- .../diagnostics/common/diagnosticsService.ts | 8 ++- .../diagnostics/node/diagnosticsService.ts | 10 ++-- .../stats/electron-browser/workspaceStats.ts | 15 +++++- .../electron-browser/workspaceStatsService.ts | 50 +++++++++++-------- 4 files changed, 55 insertions(+), 28 deletions(-) diff --git a/src/vs/platform/diagnostics/common/diagnosticsService.ts b/src/vs/platform/diagnostics/common/diagnosticsService.ts index ae854fcfb7caf..48cc4eb90817b 100644 --- a/src/vs/platform/diagnostics/common/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/common/diagnosticsService.ts @@ -63,6 +63,10 @@ export interface PerformanceInfo { workspaceInfo?: string; } +export interface IWorkspaceInformation extends IWorkspace { + telemetryId: string | undefined; +} + export const ID = 'diagnosticsService'; export const IDiagnosticsService = createDecorator(ID); @@ -72,9 +76,9 @@ export interface IDiagnosticsService { getPerformanceInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise; getSystemInfo(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise; getDiagnostics(mainProcessInfo: IMainProcessInfo, remoteInfo: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]): Promise; - reportWorkspaceStats(workspace: IWorkspace): Promise; + reportWorkspaceStats(workspace: IWorkspaceInformation): Promise; } export function isRemoteDiagnosticError(x: any): x is IRemoteDiagnosticError { return !!x.hostName && !!x.errorMessage; -} \ No newline at end of file +} diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index 9e74621348c63..13552c9ab44e6 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 } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { IMachineInfo, WorkspaceStats, WorkspaceStatItem, IDiagnosticsService, PerformanceInfo, SystemInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError, isRemoteDiagnosticError, IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnosticsService'; import { readdir, stat, exists, readFile } from 'fs'; import { join, basename } from 'vs/base/common/path'; import { parse, ParseError } from 'vs/base/common/json'; @@ -16,7 +16,6 @@ import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { ProcessItem } from 'vs/base/common/processes'; import { IMainProcessInfo } from 'vs/platform/launch/common/launchService'; -import { IWorkspace } from 'vs/platform/workspace/common/workspace'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export interface VersionInfo { @@ -514,7 +513,7 @@ export class DiagnosticsService implements IDiagnosticsService { } } - public async reportWorkspaceStats(workspace: IWorkspace): Promise { + public async reportWorkspaceStats(workspace: IWorkspaceInformation): Promise { workspace.folders.forEach(folder => { const folderUri = URI.revive(folder.uri); if (folderUri.scheme === 'file') { @@ -525,16 +524,19 @@ export class DiagnosticsService implements IDiagnosticsService { count: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; }; type WorkspaceStatsClassification = { + 'workspace.id': { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; fileTypes: WorkspaceStatItemClassification; configTypes: WorkspaceStatItemClassification; launchConfigs: WorkspaceStatItemClassification; }; type WorkspaceStatsEvent = { + 'workspace.id': string | undefined; fileTypes: WorkspaceStatItem[]; configTypes: WorkspaceStatItem[]; launchConfigs: WorkspaceStatItem[]; }; this.telemetryService.publicLog2('workspace.stats', { + 'workspace.id': workspace.telemetryId, fileTypes: stats.fileTypes, configTypes: stats.configFiles, launchConfigs: stats.launchConfigFiles @@ -545,4 +547,4 @@ export class DiagnosticsService implements IDiagnosticsService { } }); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts b/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts index 89baf8b21e451..1eac917c60cea 100644 --- a/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts +++ b/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts @@ -15,6 +15,7 @@ import { endsWith } from 'vs/base/common/strings'; import { ITextFileService, } from 'vs/workbench/services/textfile/common/textfiles'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService'; +import { IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnosticsService'; const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/; const SshUrlMatcher = /^([^@:]+@)?([^:]+):(.+)$/; @@ -175,10 +176,20 @@ export class WorkspaceStats implements IWorkbenchContribution { this.reportProxyStats(); const diagnosticsChannel = this.sharedProcessService.getChannel('diagnostics'); - diagnosticsChannel.call('reportWorkspaceStats', this.contextService.getWorkspace()); + diagnosticsChannel.call('reportWorkspaceStats', this.getWorkspaceInformation()); } - + private getWorkspaceInformation(): IWorkspaceInformation { + const workspace = this.contextService.getWorkspace(); + const state = this.contextService.getWorkbenchState(); + const id = this.workspaceStatsService.getTelemetryWorkspaceId(workspace, state); + return { + id: workspace.id, + telemetryId: id, + folders: workspace.folders, + configuration: workspace.configuration + }; + } private reportWorkspaceTags(tags: Tags): void { /* __GDPR__ diff --git a/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts b/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts index 9d0e84928f00b..b56c4a821ca17 100644 --- a/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts +++ b/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts @@ -5,7 +5,7 @@ import * as crypto from 'crypto'; import { IFileService, IResolveFileResult, IFileStat } from 'vs/platform/files/common/files'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWindowService, IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { INotificationService, IPromptChoice } from 'vs/platform/notification/common/notification'; @@ -98,6 +98,12 @@ export const IWorkspaceStatsService = createDecorator('w export interface IWorkspaceStatsService { _serviceBrand: any; getTags(): Promise; + + /** + * Returns an id for the workspace, different from the id returned by the context service. A hash based + * on the folder uri or workspace configuration, not time-based, and undefined for empty workspaces. + */ + getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined; } @@ -124,6 +130,28 @@ export class WorkspaceStatsService implements IWorkspaceStatsService { return this._tags; } + public getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined { + function createHash(uri: URI): string { + return crypto.createHash('sha1').update(uri.scheme === Schemas.file ? uri.fsPath : uri.toString()).digest('hex'); + } + + let workspaceId: string | undefined; + switch (state) { + case WorkbenchState.EMPTY: + workspaceId = undefined; + break; + case WorkbenchState.FOLDER: + workspaceId = createHash(workspace.folders[0].uri); + break; + case WorkbenchState.WORKSPACE: + if (workspace.configuration) { + workspaceId = createHash(workspace.configuration); + } + } + + return workspaceId; + } + /* __GDPR__FRAGMENT__ "WorkspaceTags" : { "workbench.filesToOpenOrCreate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -226,25 +254,7 @@ export class WorkspaceStatsService implements IWorkspaceStatsService { const state = this.contextService.getWorkbenchState(); const workspace = this.contextService.getWorkspace(); - function createHash(uri: URI): string { - return crypto.createHash('sha1').update(uri.scheme === Schemas.file ? uri.fsPath : uri.toString()).digest('hex'); - } - - let workspaceId: string | undefined; - switch (state) { - case WorkbenchState.EMPTY: - workspaceId = undefined; - break; - case WorkbenchState.FOLDER: - workspaceId = createHash(workspace.folders[0].uri); - break; - case WorkbenchState.WORKSPACE: - if (workspace.configuration) { - workspaceId = createHash(workspace.configuration); - } - } - - tags['workspace.id'] = workspaceId; + tags['workspace.id'] = this.getTelemetryWorkspaceId(workspace, state); const { filesToOpenOrCreate, filesToDiff } = configuration; tags['workbench.filesToOpenOrCreate'] = filesToOpenOrCreate && filesToOpenOrCreate.length || 0; From 02702327c1fea8b6c6425b84adb004c41cb84769 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 12 Aug 2019 13:44:07 -0700 Subject: [PATCH 079/613] Make sure webviews are rendered properly with experimental grid layout Fixes #78729 --- .../contrib/webview/electron-browser/webviewElement.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 3c030feb23544..20daf1ec4bc04 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -566,7 +566,7 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { } public layout(): void { - if (!this._webview) { + if (!this._webview || this._webview.style.width === '0px') { return; } const contents = this._webview.getWebContents(); From 36e113e5a63c7d9179db93e259d3667e1e84b0e8 Mon Sep 17 00:00:00 2001 From: mmulet Date: Mon, 12 Aug 2019 15:49:21 -0500 Subject: [PATCH 080/613] Fixed Issue 78731 (#78732) * Fixed Issue 78731 The error handler for OpenLinkOccurrence handled the case of 'invalid' or 'message', but the function returns an error of new Error('invalid') or new Error('missing') instead of 'invalid' or 'missing'. This causes the editor to throw an unhandled exception instead of a warning message. This fix checks for an err.message as well as err for 'invalid' or 'missing' --- src/vs/editor/contrib/links/links.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/links/links.ts b/src/vs/editor/contrib/links/links.ts index 53ea1177390b9..f9f19e5211caf 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -299,10 +299,12 @@ class LinkDetector implements editorCommon.IEditorContribution { return this.openerService.open(uri, { openToSide }); }, err => { + const messageOrError = + err instanceof Error ? (err).message : err; // different error cases - if (err === 'invalid') { + if (messageOrError === 'invalid') { this.notificationService.warn(nls.localize('invalid.url', 'Failed to open this link because it is not well-formed: {0}', link.url!.toString())); - } else if (err === 'missing') { + } else if (messageOrError === 'missing') { this.notificationService.warn(nls.localize('missing.url', 'Failed to open this link because its target is missing.')); } else { onUnexpectedError(err); From b6843f92d0428bb182ce858e6dbee1f1ac3f7e2c Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 12 Aug 2019 13:47:08 -0700 Subject: [PATCH 081/613] re-enable grid layout by default --- src/vs/workbench/browser/workbench.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 8bd0501679d90..22ca16ef4873e 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -237,7 +237,7 @@ import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform' 'workbench.useExperimentalGridLayout': { 'type': 'boolean', 'description': nls.localize('workbench.useExperimentalGridLayout', "Enables the grid layout for the workbench. This setting may enable additional layout options for workbench components."), - 'default': false, + 'default': true, 'scope': ConfigurationScope.APPLICATION } } From dacf3319f3c660be883987ac72055cc6d57cd564 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 12 Aug 2019 13:56:48 -0700 Subject: [PATCH 082/613] Update vscode-telemetry-extractor (for vscode-ripgrep) --- build/package.json | 2 +- build/yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build/package.json b/build/package.json index a382150c3f004..7562988862047 100644 --- a/build/package.json +++ b/build/package.json @@ -42,7 +42,7 @@ "tslint": "^5.9.1", "typescript": "3.5.2", "vsce": "1.48.0", - "vscode-telemetry-extractor": "1.5.3", + "vscode-telemetry-extractor": "^1.5.4", "xml2js": "^0.4.17" }, "scripts": { diff --git a/build/yarn.lock b/build/yarn.lock index 23ac4d9b84da3..7e4f11d8c60ac 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2313,19 +2313,19 @@ vsce@1.48.0: yauzl "^2.3.1" yazl "^2.2.2" -vscode-ripgrep@^1.5.5: - version "1.5.5" - resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.5.tgz#24c0e9cb356cf889c98e15ecb58f9cf654a1d961" - integrity sha512-OrPrAmcun4+uZAuNcQvE6CCPskh+5AsjANod/Q3zRcJcGNxgoOSGlQN9RPtatkUNmkN8Nn8mZBnS1jMylu/dKg== +vscode-ripgrep@^1.5.6: + version "1.5.6" + resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.5.6.tgz#93bf5c99ca5f8248950a305e224f6ca153c30af4" + integrity sha512-WRIM9XpUj6dsfdAmuI3ANbmT1ysPUVsYy/2uCLDHJa9kbiB4T7uGvFnnc0Rgx2qQnyRAwL7PeWaFgUljPPxf2g== -vscode-telemetry-extractor@1.5.3: - version "1.5.3" - resolved "https://registry.yarnpkg.com/vscode-telemetry-extractor/-/vscode-telemetry-extractor-1.5.3.tgz#c17f9065a47425edafd23ea161e80c23274e009d" - integrity sha512-feioJ1e1KyMa9rzblnLbSOduo+Ny0l62H3/bSDgfgCSnU/km+tTSYxPBvZHVr7iQfQGC95J61yC/ObqS9EbaQg== +vscode-telemetry-extractor@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/vscode-telemetry-extractor/-/vscode-telemetry-extractor-1.5.4.tgz#bcb0d17667fa1b77715e3a3bf372ade18f846782" + integrity sha512-MN9LNPo0Rc6cy3sIWTAG97PTWkEKdRnP0VeYoS8vjKSNtG9CAsrUxHgFfYoHm2vNK/ijd0a4NzETyVGO2kT6hw== dependencies: command-line-args "^5.1.1" ts-morph "^3.1.3" - vscode-ripgrep "^1.5.5" + vscode-ripgrep "^1.5.6" vso-node-api@6.1.2-preview: version "6.1.2-preview" From e1025004f6ced93727cadb16fb75547357fce57f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 12 Aug 2019 14:34:17 -0700 Subject: [PATCH 083/613] Fix #78598 --- .../contrib/preferences/browser/media/settingsEditor2.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 43d1569f30577..5e8238fde91b1 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -143,7 +143,7 @@ max-width: 952px; /* 1000 - 24*2 padding */ margin-left: -476px; - z-index: 1000; + z-index: 11; } .settings-editor > .settings-body .settings-tree-container .setting-toolbar-container { @@ -182,7 +182,7 @@ .settings-editor > .settings-body .settings-toc-container { width: 100%; pointer-events: none; - z-index: 100; + z-index: 10; position: absolute; } @@ -511,4 +511,4 @@ .settings-editor.search-mode > .settings-body .settings-toc-container .monaco-list-row .settings-toc-count { display: block; -} \ No newline at end of file +} From 231da466d59b28ecf1415f72f7a25cd264871225 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 12 Aug 2019 14:39:52 -0700 Subject: [PATCH 084/613] Make sure we also persist webview content options Fixes #78203 --- .../contrib/webview/browser/webviewEditorInputFactory.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts index b5f3ce40819ad..a8dbabc2c8726 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts @@ -45,7 +45,7 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { const data: SerializedWebview = { viewType: input.viewType, title: input.getName(), - options: input.webview.options, + options: { ...input.webview.options, ...input.webview.contentOptions }, extensionLocation: input.extension ? input.extension.location : undefined, extensionId: input.extension && input.extension.id ? input.extension.id.value : undefined, state: input.webview.state, @@ -118,4 +118,4 @@ function reviveState(state: unknown | undefined): undefined | string { return (state as any).state; } return undefined; -} \ No newline at end of file +} From 525f927adc26a381690d4a65fd6497707f6efe5a Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 12 Aug 2019 14:48:19 -0700 Subject: [PATCH 085/613] Adjust line height for decorations on block minimap, fixes #78807 --- src/vs/editor/browser/viewParts/minimap/minimap.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index c883223ae3175..1182f2ee8ab98 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -742,10 +742,6 @@ export class Minimap extends ViewPart { canvasContext.clearRect(0, 0, canvasInnerWidth, canvasInnerHeight); - // If the minimap is rendered using blocks, text takes up half the line height - const lineHeightRatio = renderMinimap === RenderMinimap.LargeBlocks || renderMinimap === RenderMinimap.SmallBlocks ? 0.5 : 1; - const height = lineHeight * lineHeightRatio; - // Loop over decorations, ignoring those that don't have the minimap property set and rendering rectangles for each line the decoration spans const lineOffsetMap = new Map(); for (let i = 0; i < decorations.length; i++) { @@ -756,7 +752,7 @@ export class Minimap extends ViewPart { } for (let line = decoration.range.startLineNumber; line <= decoration.range.endLineNumber; line++) { - this.renderDecorationOnLine(canvasContext, lineOffsetMap, decoration, layout, line, height, lineHeight, tabSize, characterWidth); + this.renderDecorationOnLine(canvasContext, lineOffsetMap, decoration, layout, line, lineHeight, lineHeight, tabSize, characterWidth); } } From a726cef3c3e168b1506c7114ea05f563fb7f50e8 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 12 Aug 2019 14:51:36 -0700 Subject: [PATCH 086/613] Check that modules in both the base package.json and remote/ have the same version installed --- build/npm/postinstall.js | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index cd41092192c8d..6b1159bc65cdd 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -78,4 +78,42 @@ const processTreeDts = path.join('node_modules', 'windows-process-tree', 'typing if (fs.existsSync(processTreeDts)) { console.log('Removing windows-process-tree.d.ts'); fs.unlinkSync(processTreeDts); -} \ No newline at end of file +} + +function getInstalledVersion(packageName, cwd) { + const opts = {}; + if (cwd) { + opts.cwd = cwd; + } + + const result = cp.spawnSync(yarn, ['list', '--pattern', packageName], opts); + const stdout = result.stdout.toString(); + const match = stdout.match(new RegExp(packageName + '@(\\S+)')); + if (!match || !match[1]) { + throw new Error('Unexpected output from yarn list: ' + stdout); + } + + return match[1]; +} + +function assertSameVersionsBetweenFolders(packageName, otherFolder) { + const baseVersion = getInstalledVersion(packageName); + const otherVersion = getInstalledVersion(packageName, otherFolder); + + if (baseVersion !== otherVersion) { + throw new Error(`Mismatched versions installed for ${packageName}: root has ${baseVersion}, ./${otherFolder} has ${otherVersion}. These should be the same!`); + } +} + +// Check that modules in both the base package.json and remote/ have the same version installed +const requireSameVersionsInRemote = [ + 'xterm', + 'xterm-addon-search', + 'xterm-addon-web-links', + 'node-pty', + 'vscode-ripgrep' +]; + +requireSameVersionsInRemote.forEach(packageName => { + assertSameVersionsBetweenFolders(packageName, 'remote'); +}); From 41414c2b5f492d1df445c84f9d2d0ae46aa17f43 Mon Sep 17 00:00:00 2001 From: TypeScript Tools Date: Mon, 12 Aug 2019 15:06:41 -0700 Subject: [PATCH 087/613] Strip a path list out of an error message The error, which happens quite frequently, contains an excessively long path list. The message itself was cleaned up in https://github.com/microsoft/TypeScript/pull/32785 but we add some additional filtering to the editor since older server versions are still in use. --- .../src/tsServer/serverError.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/tsServer/serverError.ts b/extensions/typescript-language-features/src/tsServer/serverError.ts index cb5cb8035f1e4..b51ca545bff0a 100644 --- a/extensions/typescript-language-features/src/tsServer/serverError.ts +++ b/extensions/typescript-language-features/src/tsServer/serverError.ts @@ -40,7 +40,16 @@ export class TypeScriptServerError extends Error { if (errorText) { const errorPrefix = 'Error processing request. '; if (errorText.startsWith(errorPrefix)) { - const prefixFreeErrorText = errorText.substr(errorPrefix.length); + let prefixFreeErrorText = errorText.substr(errorPrefix.length); + + // Prior to https://github.com/microsoft/TypeScript/pull/32785, this error + // returned and excessively long and detailed list of paths. Since server-side + // filtering doesn't have sufficient granularity to drop these specific + // messages, we sanitize them here. + if (prefixFreeErrorText.indexOf('Could not find sourceFile') >= 0) { + prefixFreeErrorText = prefixFreeErrorText.replace(/ in \[[^\]]*\]/g, ''); + } + const newlineIndex = prefixFreeErrorText.indexOf('\n'); if (newlineIndex >= 0) { // Newline expected between message and stack. From 3f5eee78bce05490546988d6eb7a68e95247bf05 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 12 Aug 2019 15:06:22 -0700 Subject: [PATCH 088/613] Inline renderBody --- .../electron-browser/releaseNotesEditor.ts | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/electron-browser/releaseNotesEditor.ts index 65d12c8087724..b91e684cfdae2 100644 --- a/src/vs/workbench/contrib/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/electron-browser/releaseNotesEditor.ts @@ -28,24 +28,6 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { generateUuid } from 'vs/base/common/uuid'; -function renderBody( - body: string, - css: string -): string { - const styleSheetPath = require.toUrl('./media/markdown.css').replace('file://', 'vscode-resource://'); - return ` - - - - - - - - - ${body} - `; -} - export class ReleaseNotesManager { private readonly _releaseNotesCache = new Map>(); @@ -188,8 +170,18 @@ export class ReleaseNotesManager { const content = await this.renderContent(text); const colorMap = TokenizationRegistry.getColorMap(); const css = colorMap ? generateTokensCSSForColorMap(colorMap) : ''; - const body = renderBody(content, css); - return body; + const styleSheetPath = require.toUrl('./media/markdown.css').replace('file://', 'vscode-resource://'); + return ` + + + + + + + + + ${content} + `; } private async renderContent(text: string): Promise { From d8d1e872427ed7e3da62e52c4c8127267a6f428c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 12 Aug 2019 15:08:34 -0700 Subject: [PATCH 089/613] Inline renderBody --- .../extensions/browser/extensionEditor.ts | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index d29d891120b4a..810bc187d7c8f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -54,22 +54,6 @@ import { URI } from 'vs/base/common/uri'; import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/common/webview'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -function renderBody(body: string): string { - const styleSheetPath = require.toUrl('./media/markdown.css').replace('file://', 'vscode-resource://'); - return ` - - - - - - - - - ${body} - - `; -} - function removeEmbeddedSVGs(documentContent: string): string { const newDocument = new DOMParser().parseFromString(documentContent, 'text/html'); @@ -550,7 +534,7 @@ export class ExtensionEditor extends BaseEditor { private openMarkdown(cacheResult: CacheResult, noContentCopy: string): Promise { return this.loadContents(() => cacheResult) .then(marked.parse) - .then(renderBody) + .then(content => this.renderBody(content)) .then(removeEmbeddedSVGs) .then(body => { const webviewElement = this.webviewService.createWebview('extensionEditor', @@ -588,6 +572,22 @@ export class ExtensionEditor extends BaseEditor { }); } + private renderBody(body: string): string { + const styleSheetPath = require.toUrl('./media/markdown.css').replace('file://', 'vscode-resource://'); + return ` + + + + + + + + + ${body} + + `; + } + private openReadme(): Promise { return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available.")); } From 52dcb723a39ae75bee1bd56b3312d7fcdc87aeed Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 12 Aug 2019 15:32:21 -0700 Subject: [PATCH 090/613] Inline the markdown css for release notes Don't depend as much on vscode-resource --- .../extensions/browser/extensionEditor.ts | 187 +++++++++++++++++- .../extensions/browser/media/markdown.css | 175 ---------------- .../contrib/webview/browser/pre/index.html | 5 +- 3 files changed, 179 insertions(+), 188 deletions(-) delete mode 100644 src/vs/workbench/contrib/extensions/browser/media/markdown.css diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 810bc187d7c8f..e7cc0836f23a9 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -50,9 +50,9 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry'; import { isUndefined } from 'vs/base/common/types'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { URI } from 'vs/base/common/uri'; import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/common/webview'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { generateUuid } from 'vs/base/common/uuid'; function removeEmbeddedSVGs(documentContent: string): string { const newDocument = new DOMParser().parseFromString(documentContent, 'text/html'); @@ -180,7 +180,7 @@ export class ExtensionEditor extends BaseEditor { @IStorageService storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, - @IWebviewService private readonly webviewService: IWebviewService + @IWebviewService private readonly webviewService: IWebviewService, ) { super(ExtensionEditor.ID, telemetryService, themeService, storageService); this.extensionReadme = null; @@ -543,9 +543,6 @@ export class ExtensionEditor extends BaseEditor { }, { svgWhiteList: this.extensionsWorkbenchService.allowedBadgeProviders, - localResourceRoots: [ - URI.parse(require.toUrl('./media')) - ] }); webviewElement.mountTo(this.content); this.contentDisposables.add(webviewElement.onDidFocus(() => this.fireOnDidFocus())); @@ -572,14 +569,186 @@ export class ExtensionEditor extends BaseEditor { }); } - private renderBody(body: string): string { - const styleSheetPath = require.toUrl('./media/markdown.css').replace('file://', 'vscode-resource://'); + private async renderBody(body: string): Promise { + const nonce = generateUuid(); return ` - - + + diff --git a/src/vs/workbench/contrib/extensions/browser/media/markdown.css b/src/vs/workbench/contrib/extensions/browser/media/markdown.css deleted file mode 100644 index 521ae95b08274..0000000000000 --- a/src/vs/workbench/contrib/extensions/browser/media/markdown.css +++ /dev/null @@ -1,175 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -body { - padding: 10px 20px; - line-height: 22px; -} - -img { - max-width: 100%; - max-height: 100%; -} - -a { - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -a:focus, -input:focus, -select:focus, -textarea:focus { - outline: 1px solid -webkit-focus-ring-color; - outline-offset: -1px; -} - -hr { - border: 0; - height: 2px; - border-bottom: 2px solid; -} - -h1 { - padding-bottom: 0.3em; - line-height: 1.2; - border-bottom-width: 1px; - border-bottom-style: solid; -} - -h1, h2, h3 { - font-weight: normal; -} - -table { - border-collapse: collapse; -} - -table > thead > tr > th { - text-align: left; - border-bottom: 1px solid; -} - -table > thead > tr > th, -table > thead > tr > td, -table > tbody > tr > th, -table > tbody > tr > td { - padding: 5px 10px; -} - -table > tbody > tr + tr > td { - border-top: 1px solid; -} - -blockquote { - margin: 0 7px 0 5px; - padding: 0 16px 0 10px; - border-left-width: 5px; - border-left-style: solid; -} - -code { - font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; - font-size: 14px; - line-height: 19px; -} - -.mac code { - font-size: 12px; - line-height: 18px; -} - -code > div { - padding: 16px; - border-radius: 3px; - overflow: auto; -} - -#scroll-to-top { - position: fixed; - width: 40px; - height: 40px; - right: 25px; - bottom: 25px; - background-color:#444444; - border-radius: 50%; - cursor: pointer; - box-shadow: 1px 1px 1px rgba(0,0,0,.25); - outline: none; - display: flex; - justify-content: center; - align-items: center; -} - -#scroll-to-top:hover { - background-color:#007acc; - box-shadow: 2px 2px 2px rgba(0,0,0,.25); -} - -body.vscode-light #scroll-to-top { - background-color: #949494; -} - -body.vscode-high-contrast #scroll-to-top:hover { - background-color: #007acc; -} - -body.vscode-high-contrast #scroll-to-top { - background-color: black; - border: 2px solid #6fc3df; - box-shadow: none; -} -body.vscode-high-contrast #scroll-to-top:hover { - background-color: #007acc; -} - -#scroll-to-top span.icon::before { - content: ""; - display: block; - /* Chevron up icon */ - background:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxNiAxNiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTYgMTY7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojRkZGRkZGO30KCS5zdDF7ZmlsbDpub25lO30KPC9zdHlsZT4KPHRpdGxlPnVwY2hldnJvbjwvdGl0bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04LDUuMWwtNy4zLDcuM0wwLDExLjZsOC04bDgsOGwtMC43LDAuN0w4LDUuMXoiLz4KPHJlY3QgY2xhc3M9InN0MSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ii8+Cjwvc3ZnPgo='); - width: 16px; - height: 16px; -} - -/** Theming */ -.vscode-light code > div { - background-color: rgba(220, 220, 220, 0.4); -} - -.vscode-dark code > div { - background-color: rgba(10, 10, 10, 0.4); -} - -.vscode-high-contrast code > div { - background-color: rgb(0, 0, 0); -} - -.vscode-high-contrast h1 { - border-color: rgb(0, 0, 0); -} - -.vscode-light table > thead > tr > th { - border-color: rgba(0, 0, 0, 0.69); -} - -.vscode-dark table > thead > tr > th { - border-color: rgba(255, 255, 255, 0.69); -} - -.vscode-light h1, -.vscode-light hr, -.vscode-light table > tbody > tr + tr > td { - border-color: rgba(0, 0, 0, 0.18); -} - -.vscode-dark h1, -.vscode-dark hr, -.vscode-dark table > tbody > tr + tr > td { - border-color: rgba(255, 255, 255, 0.18); -} diff --git a/src/vs/workbench/contrib/webview/browser/pre/index.html b/src/vs/workbench/contrib/webview/browser/pre/index.html index ac53ce590e20e..e301f5ea90d74 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/index.html +++ b/src/vs/workbench/contrib/webview/browser/pre/index.html @@ -3,9 +3,6 @@ - - Virtual Document @@ -16,4 +13,4 @@ - \ No newline at end of file + From b60d3067a29f8cc71b9a5a3a1da5f2203b3d1d7b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 13 Aug 2019 08:08:45 +0200 Subject: [PATCH 091/613] watermark - fix bad disposable use --- .../contrib/watermark/browser/watermark.ts | 85 +++++++------------ 1 file changed, 33 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts index 54c9b8620f5db..5a1ee63bf03d3 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./watermark'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { assign } from 'vs/base/common/objects'; import { isMacintosh, OS } from 'vs/base/common/platform'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -37,51 +37,17 @@ interface WatermarkEntry { mac?: boolean; } -const showCommands: WatermarkEntry = { - text: nls.localize('watermark.showCommands', "Show All Commands"), - id: ShowAllCommandsAction.ID -}; -const quickOpen: WatermarkEntry = { - text: nls.localize('watermark.quickOpen', "Go to File"), - id: QUICKOPEN_ACTION_ID -}; -const openFileNonMacOnly: WatermarkEntry = { - text: nls.localize('watermark.openFile', "Open File"), - id: OpenFileAction.ID, - mac: false -}; -const openFolderNonMacOnly: WatermarkEntry = { - text: nls.localize('watermark.openFolder', "Open Folder"), - id: OpenFolderAction.ID, - mac: false -}; -const openFileOrFolderMacOnly: WatermarkEntry = { - text: nls.localize('watermark.openFileFolder', "Open File or Folder"), - id: OpenFileFolderAction.ID, - mac: true -}; -const openRecent: WatermarkEntry = { - text: nls.localize('watermark.openRecent', "Open Recent"), - id: 'workbench.action.openRecent' -}; -const newUntitledFile: WatermarkEntry = { - text: nls.localize('watermark.newUntitledFile', "New Untitled File"), - id: GlobalNewUntitledFileAction.ID -}; +const showCommands: WatermarkEntry = { text: nls.localize('watermark.showCommands', "Show All Commands"), id: ShowAllCommandsAction.ID }; +const quickOpen: WatermarkEntry = { text: nls.localize('watermark.quickOpen', "Go to File"), id: QUICKOPEN_ACTION_ID }; +const openFileNonMacOnly: WatermarkEntry = { text: nls.localize('watermark.openFile', "Open File"), id: OpenFileAction.ID, mac: false }; +const openFolderNonMacOnly: WatermarkEntry = { text: nls.localize('watermark.openFolder', "Open Folder"), id: OpenFolderAction.ID, mac: false }; +const openFileOrFolderMacOnly: WatermarkEntry = { text: nls.localize('watermark.openFileFolder', "Open File or Folder"), id: OpenFileFolderAction.ID, mac: true }; +const openRecent: WatermarkEntry = { text: nls.localize('watermark.openRecent', "Open Recent"), id: 'workbench.action.openRecent' }; +const newUntitledFile: WatermarkEntry = { text: nls.localize('watermark.newUntitledFile', "New Untitled File"), id: GlobalNewUntitledFileAction.ID }; const newUntitledFileMacOnly: WatermarkEntry = assign({ mac: true }, newUntitledFile); -const toggleTerminal: WatermarkEntry = { - text: nls.localize({ key: 'watermark.toggleTerminal', comment: ['toggle is a verb here'] }, "Toggle Terminal"), - id: TERMINAL_COMMAND_ID.TOGGLE -}; - -const findInFiles: WatermarkEntry = { - text: nls.localize('watermark.findInFiles', "Find in Files"), - id: FindInFilesActionId -}; -const startDebugging: WatermarkEntry = { - text: nls.localize('watermark.startDebugging', "Start Debugging"), - id: StartAction.ID -}; +const toggleTerminal: WatermarkEntry = { text: nls.localize({ key: 'watermark.toggleTerminal', comment: ['toggle is a verb here'] }, "Toggle Terminal"), id: TERMINAL_COMMAND_ID.TOGGLE }; +const findInFiles: WatermarkEntry = { text: nls.localize('watermark.findInFiles', "Find in Files"), id: FindInFilesActionId }; +const startDebugging: WatermarkEntry = { text: nls.localize('watermark.startDebugging', "Start Debugging"), id: StartAction.ID }; const noFolderEntries = [ showCommands, @@ -103,13 +69,13 @@ const folderEntries = [ const WORKBENCH_TIPS_ENABLED_KEY = 'workbench.tips.enabled'; export class WatermarkContribution extends Disposable implements IWorkbenchContribution { - private watermark: HTMLElement; + private watermarkDisposable = this._register(new DisposableStore()); private enabled: boolean; private workbenchState: WorkbenchState; constructor( - @ILifecycleService lifecycleService: ILifecycleService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IKeybindingService private readonly keybindingService: IKeybindingService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @@ -117,13 +83,20 @@ export class WatermarkContribution extends Disposable implements IWorkbenchContr @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService ) { super(); - this.workbenchState = contextService.getWorkbenchState(); - lifecycleService.onShutdown(this.dispose, this); + this.workbenchState = contextService.getWorkbenchState(); this.enabled = this.configurationService.getValue(WORKBENCH_TIPS_ENABLED_KEY); + + this.registerListeners(); + if (this.enabled) { this.create(); } + } + + private registerListeners(): void { + this.lifecycleService.onShutdown(this.dispose, this); + this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(WORKBENCH_TIPS_ENABLED_KEY)) { const enabled = this.configurationService.getValue(WORKBENCH_TIPS_ENABLED_KEY); @@ -137,6 +110,7 @@ export class WatermarkContribution extends Disposable implements IWorkbenchContr } } })); + this._register(this.contextService.onDidChangeWorkbenchState(e => { const previousWorkbenchState = this.workbenchState; this.workbenchState = this.contextService.getWorkbenchState(); @@ -157,6 +131,7 @@ export class WatermarkContribution extends Disposable implements IWorkbenchContr const selected = folder ? folderEntries : noFolderEntries .filter(entry => !('mac' in entry) || entry.mac === isMacintosh) .filter(entry => !!CommandsRegistry.getCommand(entry.id)); + const update = () => { dom.clearNode(box); selected.map(entry => { @@ -169,10 +144,14 @@ export class WatermarkContribution extends Disposable implements IWorkbenchContr dd.innerHTML = keybinding.element.outerHTML; }); }; + update(); + dom.prepend(container.firstElementChild as HTMLElement, this.watermark); - this._register(this.keybindingService.onDidUpdateKeybindings(update)); - this._register(this.editorGroupsService.onDidLayout(dimension => this.handleEditorPartSize(container, dimension))); + + this.watermarkDisposable.add(this.keybindingService.onDidUpdateKeybindings(update)); + this.watermarkDisposable.add(this.editorGroupsService.onDidLayout(dimension => this.handleEditorPartSize(container, dimension))); + this.handleEditorPartSize(container, this.editorGroupsService.contentDimension); } @@ -187,9 +166,11 @@ export class WatermarkContribution extends Disposable implements IWorkbenchContr private destroy(): void { if (this.watermark) { this.watermark.remove(); + const container = this.layoutService.getContainer(Parts.EDITOR_PART); container.classList.remove('has-watermark'); - this.dispose(); + + this.watermarkDisposable.clear(); } } From 5a162ad795378cd339535cb5d80f04af471c9974 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 13 Aug 2019 08:15:57 +0200 Subject: [PATCH 092/613] main.ts => desktop.main.ts --- src/vs/code/electron-browser/workbench/workbench.js | 2 +- .../{main.contribution.ts => desktop.contribution.ts} | 0 .../workbench/electron-browser/{main.ts => desktop.main.ts} | 0 src/vs/workbench/workbench.desktop.main.ts | 4 ++-- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/vs/workbench/electron-browser/{main.contribution.ts => desktop.contribution.ts} (100%) rename src/vs/workbench/electron-browser/{main.ts => desktop.main.ts} (100%) diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index cdbe49c1b3a3d..98e67927fe123 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -27,7 +27,7 @@ bootstrapWindow.load([ perf.mark('main/startup'); // @ts-ignore - return require('vs/workbench/electron-browser/main').main(configuration); + return require('vs/workbench/electron-browser/desktop.main').main(configuration); }); }, { removeDeveloperKeybindingsAfterLoad: true, diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/desktop.contribution.ts similarity index 100% rename from src/vs/workbench/electron-browser/main.contribution.ts rename to src/vs/workbench/electron-browser/desktop.contribution.ts diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/desktop.main.ts similarity index 100% rename from src/vs/workbench/electron-browser/main.ts rename to src/vs/workbench/electron-browser/desktop.main.ts diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index cb1fd2b2a2ee7..eade62cbe0370 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -20,8 +20,8 @@ import 'vs/workbench/workbench.common.main'; //#region --- workbench (desktop main) -import 'vs/workbench/electron-browser/main.contribution'; -import 'vs/workbench/electron-browser/main'; +import 'vs/workbench/electron-browser/desktop.contribution'; +import 'vs/workbench/electron-browser/desktop.main'; //#endregion From ffa22b268f451b5211abbe2d2a47e6b753b55b1a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 13 Aug 2019 08:19:29 +0200 Subject: [PATCH 093/613] :lipstick: --- src/vs/workbench/browser/parts/editor/editorGroupView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index bbf73ff14da69..dcfc2b8e358e6 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -798,7 +798,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#region openEditor() - openEditor(editor: EditorInput, options?: EditorOptions): Promise { + async openEditor(editor: EditorInput, options?: EditorOptions): Promise { // Guard against invalid inputs if (!editor) { @@ -814,7 +814,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } // Proceed with opening - return this.doOpenEditor(editor, options).then(withUndefinedAsNull); + return withUndefinedAsNull(await this.doOpenEditor(editor, options)); } private doOpenEditor(editor: EditorInput, options?: EditorOptions): Promise { From 33656944bd7bb91910fce736c5078bc4c3d2944c Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 13 Aug 2019 09:52:33 +0200 Subject: [PATCH 094/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 71f19c8c72aaf..a371ab6f68433 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "99979734e2234cdb1e7f4988339c5df3fd3aa521", + "distro": "a7dba33608e7866342d550c5cd6ed038bba999b4", "author": { "name": "Microsoft Corporation" }, From 22a3f3ec839960758c76429f13358b0d5db00980 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 13 Aug 2019 09:54:20 +0200 Subject: [PATCH 095/613] :lipstick: --- extensions/git/package.json | 4 ++-- extensions/git/package.nls.json | 2 +- extensions/git/src/git.ts | 7 +++---- extensions/git/src/repository.ts | 6 +++--- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index cac86ec78b086..66b8731c6d9b0 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1355,14 +1355,14 @@ "default": false, "description": "%config.supportCancellation%" }, - "git.sortOrderForBranch": { + "git.branchSortOrder": { "type": "string", "enum": [ "committerdate", "alphabetically" ], "default": "committerdate", - "description": "%config.sortOrderForBranch%" + "description": "%config.branchSortOrder%" } } }, diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index e252233509804..7d90b4b15f2bd 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -127,7 +127,7 @@ "config.confirmForcePush": "Controls whether to ask for confirmation before force-pushing.", "config.openDiffOnClick": "Controls whether the diff editor should be opened when clicking a change. Otherwise the regular editor will be opened.", "config.supportCancellation": "Controls whether a notification comes up when running the Sync action, which allows the user to cancel the operation.", - "config.sortOrderForBranch": "Controls the sort order for branches", + "config.branchSortOrder": "Controls the sort order for branches.", "colors.added": "Color for added resources.", "colors.modified": "Color for modified resources.", "colors.deleted": "Color for deleted resources.", diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index bf2e5cd73c613..412cc5df92f0e 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1624,12 +1624,11 @@ export class Repository { .map(([ref]) => ({ name: ref, type: RefType.Head } as Branch)); } - async getRefs(sortBranchListByCommitterDate?: Boolean): Promise { + async getRefs(opts?: { sort?: 'alphabetically' | 'committerdate' }): Promise { const args = ['for-each-ref', '--format', '%(refname) %(objectname)']; - if (sortBranchListByCommitterDate) { - args.push('--sort'); - args.push('-committerdate'); + if (opts && opts.sort && opts.sort !== 'alphabetically') { + args.push('--sort', opts.sort); } const result = await this.run(args); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index e306d051c9cae..bf37d036d5ca1 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -701,7 +701,7 @@ export class Repository implements Disposable { onConfigListener(updateIndexGroupVisibility, this, this.disposables); updateIndexGroupVisibility(); - const onConfigListenerForBranchSortOrder = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.sortOrderForBranch', root)); + const onConfigListenerForBranchSortOrder = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.branchSortOrder', root)); onConfigListenerForBranchSortOrder(this.updateModelState, this, this.disposables); this.mergeGroup.hideWhenEmpty = true; @@ -1408,7 +1408,6 @@ export class Repository implements Disposable { const config = workspace.getConfiguration('git'); const shouldIgnore = config.get('ignoreLimitWarning') === true; const useIcons = !config.get('decorations.enabled', true); - const sortBranchListByCommitterDate = config.get('sortOrderForBranch') === 'committerdate'; this.isRepositoryHuge = didHitLimit; if (didHitLimit && !shouldIgnore && !this.didWarnAboutLimit) { @@ -1458,7 +1457,8 @@ export class Repository implements Disposable { // noop } - const [refs, remotes, submodules, rebaseCommit] = await Promise.all([this.repository.getRefs(sortBranchListByCommitterDate), this.repository.getRemotes(), this.repository.getSubmodules(), this.getRebaseCommit()]); + const sort = config.get<'alphabetically' | 'committerdate'>('branchSortOrder') || 'alphabetically'; + const [refs, remotes, submodules, rebaseCommit] = await Promise.all([this.repository.getRefs({ sort }), this.repository.getRemotes(), this.repository.getSubmodules(), this.getRebaseCommit()]); this._HEAD = HEAD; this._refs = refs; From 22e31cc37e97be7a8a7ac1424d7f1f9546dde823 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 13 Aug 2019 10:31:12 +0200 Subject: [PATCH 096/613] :lipstick: --- .../workbench/contrib/extensions/browser/extensionsViews.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 5f905a190e230..ae585737ab747 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -17,8 +17,8 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { append, $, toggleClass } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Delegate, Renderer, IExtensionsViewState } from 'vs/workbench/contrib/extensions/browser/extensionsList'; -import { IExtension, IExtensionsWorkbenchService, ExtensionState } from '../common/extensions'; -import { Query } from '../common/extensionQuery'; +import { IExtension, IExtensionsWorkbenchService, ExtensionState } from 'vs/workbench/contrib/extensions/common/extensions'; +import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; From d0c08100fadd3a7e9c692895e292730d0ec9a80d Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 13 Aug 2019 10:42:35 +0200 Subject: [PATCH 097/613] Update C grammars --- extensions/cpp/cgmanifest.json | 4 +- extensions/cpp/syntaxes/cpp.tmLanguage.json | 262 +++++++++++++++++- .../test/colorize-results/test-23630_cpp.json | 18 +- .../test/colorize-results/test-23850_cpp.json | 18 +- .../cpp/test/colorize-results/test_cc.json | 20 +- 5 files changed, 283 insertions(+), 39 deletions(-) diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index 8ab81224b2a08..b8eb057983851 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jeff-hykin/cpp-textmate-grammar", "repositoryUrl": "https://github.com/jeff-hykin/cpp-textmate-grammar", - "commitHash": "218448eb46260864352d569db13be6cb20767e92" + "commitHash": "031ef619bef4c5a1ca46e6fa69d7c913e0c32068" } }, "license": "MIT", - "version": "1.12.21", + "version": "1.13.2", "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from https://github.com/atom/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { diff --git a/extensions/cpp/syntaxes/cpp.tmLanguage.json b/extensions/cpp/syntaxes/cpp.tmLanguage.json index 963a7c6cc283b..4517b91af426d 100644 --- a/extensions/cpp/syntaxes/cpp.tmLanguage.json +++ b/extensions/cpp/syntaxes/cpp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/74c2c0eaad8f647e98a188da0f95a64f7239cbe0", + "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/031ef619bef4c5a1ca46e6fa69d7c913e0c32068", "name": "C++", "scopeName": "source.cpp", "patterns": [ @@ -121,6 +121,250 @@ "match": "(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*pragma\\s+mark)\\s+(.*)", "captures": { @@ -210,7 +454,7 @@ "name": "entity.other.attribute-name.pragma.preprocessor.cpp" }, { - "include": "#number_literal" + "include": "#preprocessor_number_literal" }, { "include": "#line_continuation_character" @@ -417,7 +661,7 @@ "include": "#string_context_c" }, { - "include": "#number_literal" + "include": "#preprocessor_number_literal" }, { "include": "#line_continuation_character" @@ -425,7 +669,7 @@ ] }, "diagnostic": { - "name": "meta.preprocessor.diagnostic.cpp", + "name": "meta.preprocessor.diagnostic.$reference(directive).cpp", "begin": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*((?:error|warning)))\\b\\s*", "beginCaptures": { "1": { @@ -667,13 +911,13 @@ "begin": "\\G\\s*(\\()", "beginCaptures": { "1": { - "name": "punctuation.definition.parameters.begin.cpp" + "name": "punctuation.definition.parameters.begin.preprocessor.cpp" } }, "end": "(\\))", "endCaptures": { "1": { - "name": "punctuation.definition.parameters.end.cpp" + "name": "punctuation.definition.parameters.end.preprocessor.cpp" } }, "patterns": [ @@ -745,7 +989,7 @@ }, "patterns": [ { - "name": "meta.conditional.preprocessor.cpp", + "name": "meta.preprocessor.conditional.cpp", "begin": "\\G(?<=ifndef|ifdef|if)", "end": "(? Date: Tue, 13 Aug 2019 10:49:21 +0200 Subject: [PATCH 098/613] web - fix formatting of paths on windows --- src/vs/workbench/contrib/remote/common/remote.contribution.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 9235c739fb014..f91419b99ca8d 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -36,7 +36,8 @@ export class LabelContribution implements IWorkbenchContribution { formatting: { label: '${path}', separator: remoteEnvironment.os === OperatingSystem.Windows ? '\\' : '/', - tildify: remoteEnvironment.os !== OperatingSystem.Windows + tildify: remoteEnvironment.os !== OperatingSystem.Windows, + normalizeDriveLetter: remoteEnvironment.os === OperatingSystem.Windows } }); } From ca181db1b357969b8efc4df3593adf633145bb36 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 13 Aug 2019 11:09:54 +0200 Subject: [PATCH 099/613] Update documentation in tasks API to say that Global tasks are not currently supported Related to #78817 --- src/vs/vscode.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 38ff2ab34d18c..bdef68b2ecade 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5208,7 +5208,7 @@ declare module 'vscode' { */ export enum TaskScope { /** - * The task is a global task + * The task is a global task. Global tasks are currrently not supported. */ Global = 1, @@ -5237,7 +5237,7 @@ declare module 'vscode' { * Creates a new task. * * @param definition The task definition as defined in the taskDefinitions extension point. - * @param scope Specifies the task's scope. It is either a global or a workspace task or a task for a specific workspace folder. + * @param scope Specifies the task's scope. It is either a global or a workspace task or a task for a specific workspace folder. Global tasks are currently not supported. * @param name The task's name. Is presented in the user interface. * @param source The task's source (e.g. 'gulp', 'npm', ...). Is presented in the user interface. * @param execution The process or shell execution. From c23b596e7284f70e1b58ab379c64327f236b6ab3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 13 Aug 2019 10:40:44 +0200 Subject: [PATCH 100/613] :lipstick: --- src/vs/base/browser/ui/grid/grid.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index a35f9b2cdf430..4586d752d0b27 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -7,7 +7,7 @@ import 'vs/css!./gridview'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { Disposable } from 'vs/base/common/lifecycle'; import { tail2 as tail, equals } from 'vs/base/common/arrays'; -import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize, ILayoutController, LayoutController } from './gridview'; +import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize, LayoutController, IGridViewOptions } from './gridview'; import { Event } from 'vs/base/common/event'; import { InvisibleSizing } from 'vs/base/browser/ui/splitview/splitview'; @@ -193,11 +193,8 @@ export namespace Sizing { export interface IGridStyles extends IGridViewStyles { } -export interface IGridOptions { - readonly styles?: IGridStyles; - readonly proportionalLayout?: boolean; +export interface IGridOptions extends IGridViewOptions { readonly firstViewVisibleCachedSize?: number; - readonly layoutController?: ILayoutController; } export class Grid extends Disposable { From 45fec0124c405e29cf39c29abb631056d7f66afa Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 13 Aug 2019 11:33:16 +0200 Subject: [PATCH 101/613] splitview: add orthogonal size grid: combine layout and orthogonalLayout fixes #78173 --- src/vs/base/browser/ui/grid/gridview.ts | 85 +++++++++++-------- src/vs/base/browser/ui/splitview/splitview.ts | 38 +++++---- .../browser/ui/splitview/splitview.test.ts | 33 +++++-- 3 files changed, 98 insertions(+), 58 deletions(-) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 5cefe23c54952..38d1131affe90 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -76,6 +76,11 @@ export class LayoutController implements ILayoutController { constructor(public isLayoutEnabled: boolean) { } } +export class MultiplexLayoutController implements ILayoutController { + get isLayoutEnabled(): boolean { return this.layoutControllers.every(l => l.isLayoutEnabled); } + constructor(private layoutControllers: ILayoutController[]) { } +} + export interface IGridViewOptions { readonly styles?: IGridViewStyles; readonly proportionalLayout?: boolean; // default true @@ -170,6 +175,7 @@ class BranchNode implements ISplitView, IDisposable { constructor( readonly orientation: Orientation, + readonly layoutController: ILayoutController, styles: IGridViewStyles, readonly proportionalLayout: boolean, size: number = 0, @@ -181,7 +187,7 @@ class BranchNode implements ISplitView, IDisposable { this.element = $('.monaco-grid-branch-node'); this.splitview = new SplitView(this.element, { orientation, styles, proportionalLayout }); - this.splitview.layout(size); + this.splitview.layout(size, orthogonalSize); const onDidSashReset = Event.map(this.splitview.onDidSashReset, i => [i]); this.splitviewSashResetDisposable = onDidSashReset(this._onDidSashReset.fire, this._onDidSashReset); @@ -198,12 +204,19 @@ class BranchNode implements ISplitView, IDisposable { } } - layout(size: number): void { - this._orthogonalSize = size; + layout(size: number, orthogonalSize: number | undefined): void { + if (!this.layoutController.isLayoutEnabled) { + return; + } - for (const child of this.children) { - child.orthogonalLayout(size); + if (typeof orthogonalSize !== 'number') { + throw new Error('Invalid state'); } + + this._size = orthogonalSize; + this._orthogonalSize = size; + + this.splitview.layout(orthogonalSize, size); } setVisible(visible: boolean): void { @@ -212,11 +225,6 @@ class BranchNode implements ISplitView, IDisposable { } } - orthogonalLayout(size: number): void { - this._size = size; - this.splitview.layout(size); - } - addChild(node: Node, size: number | Sizing, index: number): void { if (index < 0 || index > this.children.length) { throw new Error('Invalid index'); @@ -539,12 +547,18 @@ class LeafNode implements ISplitView, IDisposable { // noop } - layout(size: number): void { - this._size = size; + layout(size: number, orthogonalSize: number | undefined): void { + if (!this.layoutController.isLayoutEnabled) { + return; + } - if (this.layoutController.isLayoutEnabled) { - this.view.layout(this.width, this.height, orthogonal(this.orientation)); + if (typeof orthogonalSize !== 'number') { + throw new Error('Invalid state'); } + + this._size = size; + this._orthogonalSize = orthogonalSize; + this.view.layout(this.width, this.height, orthogonal(this.orientation)); } setVisible(visible: boolean): void { @@ -553,14 +567,6 @@ class LeafNode implements ISplitView, IDisposable { } } - orthogonalLayout(size: number): void { - this._orthogonalSize = size; - - if (this.layoutController.isLayoutEnabled) { - this.view.layout(this.width, this.height, orthogonal(this.orientation)); - } - } - dispose(): void { } } @@ -568,7 +574,7 @@ type Node = BranchNode | LeafNode; function flipNode(node: T, size: number, orthogonalSize: number): T { if (node instanceof BranchNode) { - const result = new BranchNode(orthogonal(node.orientation), node.styles, node.proportionalLayout, size, orthogonalSize); + const result = new BranchNode(orthogonal(node.orientation), node.layoutController, node.styles, node.proportionalLayout, size, orthogonalSize); let totalSize = 0; @@ -589,7 +595,7 @@ function flipNode(node: T, size: number, orthogonalSize: number) return result as T; } else { - return new LeafNode((node as LeafNode).view, orthogonal(node.orientation), (node as LeafNode).layoutController, orthogonalSize) as T; + return new LeafNode((node as LeafNode).view, orthogonal(node.orientation), node.layoutController, orthogonalSize) as T; } } @@ -634,8 +640,9 @@ export class GridView implements IDisposable { const { size, orthogonalSize } = this._root; this.root = flipNode(this._root, orthogonalSize, size); - this.root.layout(size); - this.root.orthogonalLayout(orthogonalSize); + this.root.layout(size, orthogonalSize); + // this.root.layout(size); + // this.root.orthogonalLayout(orthogonalSize); } get width(): number { return this.root.width; } @@ -649,14 +656,25 @@ export class GridView implements IDisposable { private _onDidChange = new Relay(); readonly onDidChange = this._onDidChange.event; + /** + * The first layout controller makes sure layout only propagates + * to the views after the very first call to gridview.layout() + */ + private firstLayoutController: LayoutController; private layoutController: LayoutController; constructor(options: IGridViewOptions = {}) { this.element = $('.monaco-grid-view'); this.styles = options.styles || defaultStyles; this.proportionalLayout = typeof options.proportionalLayout !== 'undefined' ? !!options.proportionalLayout : true; - this.root = new BranchNode(Orientation.VERTICAL, this.styles, this.proportionalLayout); - this.layoutController = options.layoutController || new LayoutController(true); + + this.firstLayoutController = new LayoutController(false); + this.layoutController = new MultiplexLayoutController([ + this.firstLayoutController, + ...(options.layoutController ? [options.layoutController] : []) + ]); + + this.root = new BranchNode(Orientation.VERTICAL, this.layoutController, this.styles, this.proportionalLayout); } style(styles: IGridViewStyles): void { @@ -665,9 +683,10 @@ export class GridView implements IDisposable { } layout(width: number, height: number): void { + this.firstLayoutController.isLayoutEnabled = true; + const [size, orthogonalSize] = this.root.orientation === Orientation.HORIZONTAL ? [height, width] : [width, height]; - this.root.layout(size); - this.root.orthogonalLayout(orthogonalSize); + this.root.layout(size, orthogonalSize); } addView(view: IView, size: number | Sizing, location: number[]): void { @@ -694,9 +713,8 @@ export class GridView implements IDisposable { grandParent.removeChild(parentIndex); - const newParent = new BranchNode(parent.orientation, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize); + const newParent = new BranchNode(parent.orientation, parent.layoutController, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize); grandParent.addChild(newParent, parent.size, parentIndex); - newParent.orthogonalLayout(parent.orthogonalSize); const newSibling = new LeafNode(parent.view, grandParent.orientation, this.layoutController, parent.size); newParent.addChild(newSibling, newSiblingSize, 0); @@ -827,9 +845,6 @@ export class GridView implements IDisposable { fromParent.addChild(toNode, fromSize, fromIndex); toParent.addChild(fromNode, toSize, toIndex); - - fromParent.layout(fromParent.orthogonalSize); - toParent.layout(toParent.orthogonalSize); } } diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 02c583d1e069f..b5102c9f9b304 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -24,12 +24,12 @@ const defaultStyles: ISplitViewStyles = { }; export interface ISplitViewOptions { - orientation?: Orientation; // default Orientation.VERTICAL - styles?: ISplitViewStyles; - orthogonalStartSash?: Sash; - orthogonalEndSash?: Sash; - inverseAltBehavior?: boolean; - proportionalLayout?: boolean; // default true + readonly orientation?: Orientation; // default Orientation.VERTICAL + readonly styles?: ISplitViewStyles; + readonly orthogonalStartSash?: Sash; + readonly orthogonalEndSash?: Sash; + readonly inverseAltBehavior?: boolean; + readonly proportionalLayout?: boolean; // default true } /** @@ -48,7 +48,7 @@ export interface IView { readonly onDidChange: Event; readonly priority?: LayoutPriority; readonly snap?: boolean; - layout(size: number, orientation: Orientation): void; + layout(size: number, orthogonalSize: number | undefined): void; setVisible?(visible: boolean): void; } @@ -125,13 +125,13 @@ abstract class ViewItem { dom.addClass(container, 'visible'); } - layout(): void { + layout(_orthogonalSize: number | undefined): void { this.container.scrollTop = 0; this.container.scrollLeft = 0; } - layoutView(orientation: Orientation): void { - this.view.layout(this.size, orientation); + layoutView(orthogonalSize: number | undefined): void { + this.view.layout(this.size, orthogonalSize); } dispose(): IView { @@ -142,19 +142,19 @@ abstract class ViewItem { class VerticalViewItem extends ViewItem { - layout(): void { - super.layout(); + layout(orthogonalSize: number | undefined): void { + super.layout(orthogonalSize); this.container.style.height = `${this.size}px`; - this.layoutView(Orientation.VERTICAL); + this.layoutView(orthogonalSize); } } class HorizontalViewItem extends ViewItem { - layout(): void { - super.layout(); + layout(orthogonalSize: number | undefined): void { + super.layout(orthogonalSize); this.container.style.width = `${this.size}px`; - this.layoutView(Orientation.HORIZONTAL); + this.layoutView(orthogonalSize); } } @@ -205,6 +205,7 @@ export class SplitView extends Disposable { private sashContainer: HTMLElement; private viewContainer: HTMLElement; private size = 0; + private orthogonalSize: number | undefined; private contentSize = 0; private proportions: undefined | number[] = undefined; private viewItems: ViewItem[] = []; @@ -475,9 +476,10 @@ export class SplitView extends Disposable { return viewItem.cachedVisibleSize; } - layout(size: number): void { + layout(size: number, orthogonalSize?: number): void { const previousSize = Math.max(this.size, this.contentSize); this.size = size; + this.orthogonalSize = orthogonalSize; if (!this.proportions) { const indexes = range(this.viewItems.length); @@ -820,7 +822,7 @@ export class SplitView extends Disposable { this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); // Layout views - this.viewItems.forEach(item => item.layout()); + this.viewItems.forEach(item => item.layout(this.orthogonalSize)); // Layout sashes this.sashItems.forEach(item => item.sash.layout()); diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index 504e665f21a8c..c5e9cfce763d4 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Emitter } from 'vs/base/common/event'; -import { SplitView, IView, Orientation, Sizing, LayoutPriority } from 'vs/base/browser/ui/splitview/splitview'; +import { SplitView, IView, Sizing, LayoutPriority } from 'vs/base/browser/ui/splitview/splitview'; import { Sash, SashState } from 'vs/base/browser/ui/sash/sash'; class TestView implements IView { @@ -27,7 +27,9 @@ class TestView implements IView { private _size = 0; get size(): number { return this._size; } - private _onDidLayout = new Emitter<{ size: number; orientation: Orientation }>(); + private _orthogonalSize: number | undefined = 0; + get orthogonalSize(): number | undefined { return this._orthogonalSize; } + private _onDidLayout = new Emitter<{ size: number; orthogonalSize: number | undefined }>(); readonly onDidLayout = this._onDidLayout.event; private _onDidFocus = new Emitter(); @@ -41,9 +43,10 @@ class TestView implements IView { assert(_minimumSize <= _maximumSize, 'splitview view minimum size must be <= maximum size'); } - layout(size: number, orientation: Orientation): void { + layout(size: number, orthogonalSize: number | undefined): void { this._size = size; - this._onDidLayout.fire({ size, orientation }); + this._orthogonalSize = orthogonalSize; + this._onDidLayout.fire({ size, orthogonalSize }); } focus(): void { @@ -523,4 +526,24 @@ suite('Splitview', () => { view2.dispose(); view1.dispose(); }); -}); \ No newline at end of file + + test('orthogonal size propagates to views', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const view3 = new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.Low); + const splitview = new SplitView(container, { proportionalLayout: false }); + splitview.layout(200); + + splitview.addView(view1, Sizing.Distribute); + splitview.addView(view2, Sizing.Distribute); + splitview.addView(view3, Sizing.Distribute); + + splitview.layout(200, 100); + assert.deepEqual([view1.orthogonalSize, view2.orthogonalSize, view3.orthogonalSize], [100, 100, 100]); + + splitview.dispose(); + view3.dispose(); + view2.dispose(); + view1.dispose(); + }); +}); From d94bb19d81dcd30da4958d0354c92f329e333e66 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 13 Aug 2019 12:07:53 +0200 Subject: [PATCH 102/613] strictPropertyInitialization in tasks and custom tree view Part of #78168 --- .../api/browser/mainThreadTreeViews.ts | 2 +- .../browser/parts/views/customView.ts | 54 ++++++++++--------- src/vs/workbench/common/views.ts | 2 +- .../tasks/browser/abstractTaskService.ts | 18 +++---- .../tasks/browser/terminalTaskSystem.ts | 25 +++++---- .../contrib/tasks/common/problemCollectors.ts | 16 +++--- .../contrib/tasks/common/problemMatcher.ts | 10 ++-- .../contrib/tasks/common/taskConfiguration.ts | 2 +- .../tasks/common/taskDefinitionRegistry.ts | 2 +- .../workbench/contrib/tasks/common/tasks.ts | 11 ++-- 10 files changed, 77 insertions(+), 65 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 2a1e3f5bc5fdc..79968a69f71fa 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -124,7 +124,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._dataProviders.forEach((dataProvider, treeViewId) => { const treeView = this.getTreeView(treeViewId); if (treeView) { - treeView.dataProvider = null; + treeView.dataProvider = undefined; } }); this._dataProviders.clear(); diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 1163a29492076..6c714028d8f46 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -161,12 +161,12 @@ export class CustomTreeView extends Disposable implements ITreeView { private _showCollapseAllAction = false; private focused: boolean = false; - private domNode: HTMLElement; - private treeContainer: HTMLElement; + private domNode!: HTMLElement; + private treeContainer!: HTMLElement; private _messageValue: string | undefined; - private messageElement: HTMLDivElement; - private tree: WorkbenchAsyncDataTree; - private treeLabels: ResourceLabels; + private messageElement!: HTMLDivElement; + private tree: WorkbenchAsyncDataTree | undefined; + private treeLabels: ResourceLabels | undefined; private root: ITreeItem; private elementsToRefresh: ITreeItem[] = []; private menus: TitleMenus; @@ -218,12 +218,12 @@ export class CustomTreeView extends Disposable implements ITreeView { this.create(); } - private _dataProvider: ITreeViewDataProvider | null; - get dataProvider(): ITreeViewDataProvider | null { + private _dataProvider: ITreeViewDataProvider | undefined; + get dataProvider(): ITreeViewDataProvider | undefined { return this._dataProvider; } - set dataProvider(dataProvider: ITreeViewDataProvider | null) { + set dataProvider(dataProvider: ITreeViewDataProvider | undefined) { if (dataProvider) { this._dataProvider = new class implements ITreeViewDataProvider { async getChildren(node: ITreeItem): Promise { @@ -238,7 +238,7 @@ export class CustomTreeView extends Disposable implements ITreeView { this.updateMessage(); this.refresh(); } else { - this._dataProvider = null; + this._dataProvider = undefined; this.updateMessage(); } } @@ -399,7 +399,7 @@ export class CustomTreeView extends Disposable implements ITreeView { if (!e.browserEvent) { return; } - const selection = this.tree.getSelection(); + const selection = this.tree!.getSelection(); if ((selection.length === 1) && selection[0].command) { this.commandService.executeCommand(selection[0].command.id, ...(selection[0].command.arguments || [])); } @@ -416,7 +416,7 @@ export class CustomTreeView extends Disposable implements ITreeView { event.preventDefault(); event.stopPropagation(); - this.tree.setFocus([node]); + this.tree!.setFocus([node]); const actions = treeMenus.getResourceContextActions(node); if (!actions.length) { return; @@ -436,13 +436,13 @@ export class CustomTreeView extends Disposable implements ITreeView { onHide: (wasCancelled?: boolean) => { if (wasCancelled) { - this.tree.domFocus(); + this.tree!.domFocus(); } }, getActionsContext: () => ({ $treeViewId: this.id, $treeItemHandle: node.handle }), - actionRunner: new MultipleSelectionActionRunner(() => this.tree.getSelection()) + actionRunner: new MultipleSelectionActionRunner(() => this.tree!.getSelection()) }); } @@ -479,8 +479,8 @@ export class CustomTreeView extends Disposable implements ITreeView { DOM.clearNode(this.messageElement); } - private _height: number; - private _width: number; + private _height: number = 0; + private _width: number = 0; layout(height: number, width: number) { if (height && width) { this._height = height; @@ -532,10 +532,11 @@ export class CustomTreeView extends Disposable implements ITreeView { } async expand(itemOrItems: ITreeItem | ITreeItem[]): Promise { - if (this.tree) { + const tree = this.tree; + if (tree) { itemOrItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems]; await Promise.all(itemOrItems.map(element => { - return this.tree.expand(element, false); + return tree.expand(element, false); })); } return Promise.resolve(undefined); @@ -575,18 +576,19 @@ export class CustomTreeView extends Disposable implements ITreeView { private refreshing: boolean = false; private async doRefresh(elements: ITreeItem[]): Promise { - if (this.tree) { + const tree = this.tree; + if (tree) { this.refreshing = true; const parents: Set = new Set(); elements.forEach(element => { if (element !== this.root) { - const parent = this.tree.getParentElement(element); + const parent = tree.getParentElement(element); parents.add(parent); } else { parents.add(element); } }); - await Promise.all(Array.from(parents.values()).map(element => this.tree.updateChildren(element, true))); + await Promise.all(Array.from(parents.values()).map(element => tree.updateChildren(element, true))); this.refreshing = false; this.updateContentAreas(); if (this.focused) { @@ -772,7 +774,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer; + private _tree: WorkbenchAsyncDataTree | undefined; constructor(private themeService: IWorkbenchThemeService) { super(); @@ -790,11 +792,15 @@ class Aligner extends Disposable { return false; } - const parent: ITreeItem = this._tree.getParentElement(treeItem) || this._tree.getInput(); - if (this.hasIcon(parent)) { + if (this._tree) { + const parent: ITreeItem = this._tree.getParentElement(treeItem) || this._tree.getInput(); + if (this.hasIcon(parent)) { + return false; + } + return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c)); + } else { return false; } - return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c)); } private hasIcon(node: ITreeItem): boolean { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index c9ec1a841f638..fe73a0c029448 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -300,7 +300,7 @@ export interface IViewsService { export interface ITreeView extends IDisposable { - dataProvider: ITreeViewDataProvider | null; + dataProvider: ITreeViewDataProvider | undefined; showCollapseAllAction: boolean; diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 5451b8eb6f4b9..17e627687400b 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -179,10 +179,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer private static nextHandle: number = 0; - private _schemaVersion: JsonSchemaVersion; - private _executionEngine: ExecutionEngine; - private _workspaceFolders: IWorkspaceFolder[]; - private _ignoredWorkspaceFolders: IWorkspaceFolder[]; + private _schemaVersion: JsonSchemaVersion | undefined; + private _executionEngine: ExecutionEngine | undefined; + private _workspaceFolders: IWorkspaceFolder[] | undefined; + private _ignoredWorkspaceFolders: IWorkspaceFolder[] | undefined; private _showIgnoreMessage?: boolean; private _providers: Map; private _providerTypes: Map; @@ -192,7 +192,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected _taskSystem?: ITaskSystem; protected _taskSystemListener?: IDisposable; - private _recentlyUsedTasks: LinkedMap; + private _recentlyUsedTasks: LinkedMap | undefined; protected _taskRunningState: IContextKey; @@ -375,28 +375,28 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!this._workspaceFolders) { this.updateSetup(); } - return this._workspaceFolders; + return this._workspaceFolders!; } private get ignoredWorkspaceFolders(): IWorkspaceFolder[] { if (!this._ignoredWorkspaceFolders) { this.updateSetup(); } - return this._ignoredWorkspaceFolders; + return this._ignoredWorkspaceFolders!; } protected get executionEngine(): ExecutionEngine { if (this._executionEngine === undefined) { this.updateSetup(); } - return this._executionEngine; + return this._executionEngine!; } private get schemaVersion(): JsonSchemaVersion { if (this._schemaVersion === undefined) { this.updateSetup(); } - return this._schemaVersion; + return this._schemaVersion!; } private get showIgnoreMessage(): boolean { diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 9d52970cf2cd5..fb838d2e49e8a 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -155,9 +155,10 @@ export class TerminalTaskSystem implements ITaskSystem { private idleTaskTerminals: LinkedMap; private sameTaskTerminals: IStringDictionary; private taskSystemInfoResolver: TaskSystemInfoResolver; - private lastTask: VerifiedTask; - private currentTask: VerifiedTask; - private isRerun: boolean; + private lastTask: VerifiedTask | undefined; + // Should always be set in run + private currentTask!: VerifiedTask; + private isRerun: boolean = false; private readonly _onDidStateChange: Emitter; @@ -485,28 +486,32 @@ export class TerminalTaskSystem implements ITaskSystem { } private reexecuteCommand(task: CustomTask | ContributedTask, trigger: string): Promise { - const workspaceFolder = this.currentTask.workspaceFolder = this.lastTask.workspaceFolder; + const lastTask = this.lastTask; + if (!lastTask) { + return Promise.reject(new Error('No task previously run')); + } + const workspaceFolder = this.currentTask.workspaceFolder = lastTask.workspaceFolder; let variables = new Set(); this.collectTaskVariables(variables, task); // Check that the task hasn't changed to include new variables let hasAllVariables = true; variables.forEach(value => { - if (value.substring(2, value.length - 1) in this.lastTask.getVerifiedTask().resolvedVariables) { + if (value.substring(2, value.length - 1) in lastTask.getVerifiedTask().resolvedVariables) { hasAllVariables = false; } }); if (!hasAllVariables) { - return this.resolveVariablesFromSet(this.lastTask.getVerifiedTask().systemInfo, this.lastTask.getVerifiedTask().workspaceFolder, task, variables).then((resolvedVariables) => { + return this.resolveVariablesFromSet(lastTask.getVerifiedTask().systemInfo, lastTask.getVerifiedTask().workspaceFolder, task, variables).then((resolvedVariables) => { this.currentTask.resolvedVariables = resolvedVariables; - return this.executeInTerminal(task, trigger, new VariableResolver(this.lastTask.getVerifiedTask().workspaceFolder, this.lastTask.getVerifiedTask().systemInfo, resolvedVariables.variables, this.configurationResolverService), workspaceFolder!); + return this.executeInTerminal(task, trigger, new VariableResolver(lastTask.getVerifiedTask().workspaceFolder, lastTask.getVerifiedTask().systemInfo, resolvedVariables.variables, this.configurationResolverService), workspaceFolder!); }, reason => { return Promise.reject(reason); }); } else { - this.currentTask.resolvedVariables = this.lastTask.getVerifiedTask().resolvedVariables; - return this.executeInTerminal(task, trigger, new VariableResolver(this.lastTask.getVerifiedTask().workspaceFolder, this.lastTask.getVerifiedTask().systemInfo, this.lastTask.getVerifiedTask().resolvedVariables.variables, this.configurationResolverService), workspaceFolder!); + this.currentTask.resolvedVariables = lastTask.getVerifiedTask().resolvedVariables; + return this.executeInTerminal(task, trigger, new VariableResolver(lastTask.getVerifiedTask().workspaceFolder, lastTask.getVerifiedTask().systemInfo, lastTask.getVerifiedTask().resolvedVariables.variables, this.configurationResolverService), workspaceFolder!); } } @@ -923,7 +928,7 @@ export class TerminalTaskSystem implements ITaskSystem { args = resolvedResult.args; commandExecutable = CommandString.value(command); - this.currentTask.shellLaunchConfig = launchConfigs = this.isRerun ? this.lastTask.getVerifiedTask().shellLaunchConfig : await this.createShellLaunchConfig(task, workspaceFolder, resolver, platform, options, command, args, waitOnExit); + this.currentTask.shellLaunchConfig = launchConfigs = (this.isRerun && this.lastTask) ? this.lastTask.getVerifiedTask().shellLaunchConfig : await this.createShellLaunchConfig(task, workspaceFolder, resolver, platform, options, command, args, waitOnExit); if (launchConfigs === undefined) { return [undefined, undefined, new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive using cmd.exe.'), TaskErrors.UnknownError)]; } diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index eac610b4d4af0..9a4884346b061 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -44,7 +44,7 @@ export abstract class AbstractProblemCollector implements IDisposable { private bufferLength: number; private openModels: IStringDictionary; private readonly modelListeners = new DisposableStore(); - private tail: Promise; + private tail: Promise | undefined; // [owner] -> ApplyToKind private applyToByOwner: Map; @@ -344,8 +344,8 @@ export const enum ProblemHandlingStrategy { export class StartStopProblemCollector extends AbstractProblemCollector implements IProblemMatcher { private owners: string[]; - private currentOwner: string; - private currentResource: string; + private currentOwner: string | undefined; + private currentResource: string | undefined; constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService, _strategy: ProblemHandlingStrategy = ProblemHandlingStrategy.Clean, fileService?: IFileService) { super(problemMatchers, markerService, modelService, fileService); @@ -397,8 +397,8 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement private _activeBackgroundMatchers: Set; // Current State - private currentOwner: string | null; - private currentResource: string | null; + private currentOwner: string | undefined; + private currentResource: string | undefined; constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService, fileService?: IFileService) { super(problemMatchers, markerService, modelService, fileService); @@ -503,8 +503,8 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement private resetCurrentResource(): void { this.reportMarkersForCurrentResource(); - this.currentOwner = null; - this.currentResource = null; + this.currentOwner = undefined; + this.currentResource = undefined; } private reportMarkersForCurrentResource(): void { @@ -512,4 +512,4 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement this.deliverMarkersPerOwnerAndResource(this.currentOwner, this.currentResource); } } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/tasks/common/problemMatcher.ts b/src/vs/workbench/contrib/tasks/common/problemMatcher.ts index 18b98f751f0c8..ec7c7bcbb96aa 100644 --- a/src/vs/workbench/contrib/tasks/common/problemMatcher.ts +++ b/src/vs/workbench/contrib/tasks/common/problemMatcher.ts @@ -264,7 +264,7 @@ abstract class AbstractLineMatcher implements ILineMatcher { public abstract get matchLength(): number; - protected fillProblemData(data: ProblemData | null, pattern: ProblemPattern, matches: RegExpExecArray): data is ProblemData { + protected fillProblemData(data: ProblemData | undefined, pattern: ProblemPattern, matches: RegExpExecArray): data is ProblemData { if (data) { this.fillProperty(data, 'file', pattern, matches, true); this.appendProperty(data, 'message', pattern, matches, true); @@ -449,7 +449,7 @@ class SingleLineMatcher extends AbstractLineMatcher { class MultiLineMatcher extends AbstractLineMatcher { private patterns: ProblemPattern[]; - private data: ProblemData | null; + private data: ProblemData | undefined; constructor(matcher: ProblemMatcher, fileService?: IFileService) { super(matcher, fileService); @@ -480,7 +480,7 @@ class MultiLineMatcher extends AbstractLineMatcher { } let loop = !!this.patterns[this.patterns.length - 1].loop; if (!loop) { - this.data = null; + this.data = undefined; } const markerMatch = data ? this.getMarkerMatch(data) : null; return { match: markerMatch ? markerMatch : null, continue: loop }; @@ -491,7 +491,7 @@ class MultiLineMatcher extends AbstractLineMatcher { Assert.ok(pattern.loop === true && this.data !== null); let matches = pattern.regexp.exec(line); if (!matches) { - this.data = null; + this.data = undefined; return null; } let data = Objects.deepClone(this.data); @@ -794,7 +794,7 @@ export namespace Config { fileLocation?: string | string[]; /** - * The name of a predefined problem pattern, the inline definintion + * The name of a predefined problem pattern, the inline definition * of a problem pattern or an array of problem patterns to match * problems spread over multiple lines. */ diff --git a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index 4e856c3b65c2b..d0e83e2e000f6 100644 --- a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -681,7 +681,7 @@ export namespace RunOptions { } } -class ParseContext { +interface ParseContext { workspaceFolder: IWorkspaceFolder; problemReporter: IProblemReporter; namedProblemMatchers: IStringDictionary; diff --git a/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts b/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts index b52a02294869c..6a696c1666aa5 100644 --- a/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts +++ b/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts @@ -89,7 +89,7 @@ class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { private taskTypes: IStringDictionary; private readyPromise: Promise; - private _schema: IJSONSchema; + private _schema: IJSONSchema | undefined; constructor() { this.taskTypes = Object.create(null); diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 46593df22bee6..d1d4cc32eb494 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -518,7 +518,7 @@ export abstract class CommonTask { /** * The cached label. */ - _label: string; + _label: string = ''; type?: string; @@ -614,7 +614,7 @@ export abstract class CommonTask { export class CustomTask extends CommonTask { - type: '$customized'; // CUSTOMIZED_TASK_TYPE + type!: '$customized'; // CUSTOMIZED_TASK_TYPE /** * Indicated the source of the task (e.g. tasks.json or extension) @@ -626,7 +626,7 @@ export class CustomTask extends CommonTask { /** * The command configuration */ - command: CommandConfiguration; + command: CommandConfiguration = {}; public constructor(id: string, source: WorkspaceTaskSource, label: string, type: string, command: CommandConfiguration | undefined, hasDefinedMatchers: boolean, runOptions: RunOptions, configurationProperties: ConfigurationProperties) { @@ -754,8 +754,9 @@ export class ContributedTask extends CommonTask { /** * Indicated the source of the task (e.g. tasks.json or extension) + * Set in the super constructor */ - _source: ExtensionTaskSource; + _source!: ExtensionTaskSource; defines: KeyedTaskIdentifier; @@ -824,7 +825,7 @@ export class InMemoryTask extends CommonTask { */ _source: InMemoryTaskSource; - type: 'inMemory'; + type!: 'inMemory'; public constructor(id: string, source: InMemoryTaskSource, label: string, type: string, runOptions: RunOptions, configurationProperties: ConfigurationProperties) { From 5d22d7f74c36ef606347f647def0bad73b4877bc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 13 Aug 2019 11:36:40 +0200 Subject: [PATCH 103/613] create API instance per extension --- .../api/common/extHostExtensionService.ts | 4 +- .../api/worker/extHostExtensionService.ts | 91 ++++++++++++++----- 2 files changed, 69 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 3b59d2d692d49..99553033cd227 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -384,7 +384,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio try { activationTimesBuilder.activateCallStart(); logService.trace(`ExtensionService#_callActivateOptional ${extensionId.value}`); - const scope = typeof global === 'object' ? global : self; //todo@joh not so nice + const scope = typeof global === 'object' ? global : self; // `global` is nodejs while `self` is for workers const activateResult: Promise = extensionModule.activate.apply(scope, [context]); activationTimesBuilder.activateCallStop(); @@ -525,7 +525,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio } private async _doHandleExtensionTests(): Promise { - const { extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment; + const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment; if (!(extensionDevelopmentLocationURI && extensionTestsLocationURI && extensionTestsLocationURI.scheme === Schemas.file)) { return Promise.resolve(undefined); } diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index 30d15e39399b0..6f7f2b67febde 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -3,53 +3,96 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl'; +import { createApiFactoryAndRegisterActors, IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl'; import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator'; import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { endsWith } from 'vs/base/common/strings'; +import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; +import * as vscode from 'vscode'; +import { TernarySearchTree } from 'vs/base/common/map'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; + +class ApiInstances { + + private readonly _apiInstances = new Map(); + + constructor( + private readonly _apiFactory: IExtensionApiFactory, + private readonly _extensionPaths: TernarySearchTree, + private readonly _extensionRegistry: ExtensionDescriptionRegistry, + private readonly _configProvider: ExtHostConfigProvider, + ) { + // + } + + get(modulePath: string): typeof vscode { + const extension = this._extensionPaths.findSubstr(modulePath) || nullExtensionDescription; + const id = ExtensionIdentifier.toKey(extension.identifier); + + let apiInstance = this._apiInstances.get(id); + if (!apiInstance) { + apiInstance = this._apiFactory(extension, this._extensionRegistry, this._configProvider); + this._apiInstances.set(id, apiInstance); + } + return apiInstance; + } +} export class ExtHostExtensionService extends AbstractExtHostExtensionService { + private _apiInstances?: ApiInstances; + protected async _beforeAlmostReadyToRunExtensions(): Promise { // initialize API and register actors - const factory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors); - - // globally define the vscode module and share that for all extensions - // todo@joh have an instance per extension, not a shared one.... - const sharedApiInstance = factory(nullExtensionDescription, this._registry, await this._extHostConfiguration.getConfigProvider()); - define('vscode', sharedApiInstance); + const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors); + const configProvider = await this._extHostConfiguration.getConfigProvider(); + const extensionPath = await this.getExtensionPathIndex(); + this._apiInstances = new ApiInstances(apiFactory, extensionPath, this._registry, configProvider); } protected _loadCommonJSModule(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { - // fake commonjs world + + // make sure modulePath ends with `.js` + const suffix = '.js'; + modulePath = endsWith(modulePath, suffix) ? modulePath : modulePath + suffix; + + interface FakeCommonJSSelf { + module?: object; + exports?: object; + require?: (module: string) => any; + window?: object; + __dirname: never; + __filename: never; + } + + // FAKE commonjs world that only collects exports + const patchSelf: FakeCommonJSSelf = self; const module = { exports: {} }; - //@ts-ignore - self['module'] = module; - //@ts-ignore - self['exports'] = module.exports; - // that's improper but might help extensions that aren't author correctly - // @ts-ignore - self['window'] = self; + patchSelf.module = module; + patchSelf.exports = module.exports; + patchSelf.window = self; // <- that's improper but might help extensions that aren't authored correctly + + // FAKE require function that only works for the vscode-module + patchSelf.require = (module: string) => { + if (module !== 'vscode') { + throw new Error(`Cannot load module '${module}'`); + } + return this._apiInstances!.get(modulePath); + }; try { activationTimesBuilder.codeLoadingStart(); - // import the single (!) script, make sure it's a JS-file - const suffix = '.js'; - if (endsWith(modulePath, suffix)) { - importScripts(modulePath); - } else { - importScripts(modulePath + suffix); - } + importScripts(modulePath); } finally { activationTimesBuilder.codeLoadingStop(); } - // return what it exported return Promise.resolve(module.exports as T); } - public async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise { + async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise { throw new Error('Not supported'); } } From 65ab4f871fc92e8782170a3dd87ba539c74fa896 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 13 Aug 2019 12:33:16 +0200 Subject: [PATCH 104/613] #78992 --- src/vs/workbench/contrib/files/browser/fileActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 8a2c0fe652f85..9d87a9c16b18a 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -44,7 +44,7 @@ import { CLOSE_EDITORS_AND_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/e import { coalesce } from 'vs/base/common/arrays'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { ExplorerItem, NewExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { onUnexpectedError, getErrorMessage } from 'vs/base/common/errors'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); @@ -1108,7 +1108,7 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => { return await fileService.copy(fileToPaste, targetFile); } } catch (e) { - onError(notificationService, new Error(nls.localize('fileDeleted', "File to paste was deleted or moved meanwhile"))); + onError(notificationService, new Error(nls.localize('fileDeleted', "File to paste was deleted or moved meanwhile. {0}", getErrorMessage(e)))); return undefined; } })); From 0c044b6d5d32ae0cc5bb3078e95a8545c5e80288 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 13 Aug 2019 12:50:33 +0200 Subject: [PATCH 105/613] :lipstick: --- src/vs/workbench/workbench.desktop.main.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index eade62cbe0370..c2d6ea7a5bc7d 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -32,7 +32,6 @@ import 'vs/workbench/services/textMate/electron-browser/textMateService'; import 'vs/workbench/services/workspace/electron-browser/workspaceEditingService'; import 'vs/workbench/services/extensions/common/inactiveExtensionUrlHandler'; import 'vs/workbench/services/search/node/searchService'; -import 'vs/workbench/contrib/debug/electron-browser/extensionHostDebugService'; import 'vs/workbench/services/output/node/outputChannelModelService'; import 'vs/workbench/services/textfile/node/textFileService'; import 'vs/workbench/services/dialogs/electron-browser/dialogService'; @@ -48,7 +47,6 @@ import 'vs/workbench/services/configurationResolver/electron-browser/configurati import 'vs/workbench/services/extensionManagement/node/extensionManagementService'; import 'vs/workbench/services/accessibility/node/accessibilityService'; import 'vs/workbench/services/remote/node/tunnelService'; -import 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService'; import 'vs/workbench/services/backup/node/backupFileService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -104,6 +102,7 @@ import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; import 'vs/workbench/contrib/logs/electron-browser/logs.contribution'; // Stats +import 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService'; import 'vs/workbench/contrib/stats/electron-browser/stats.contribution'; // Rapid Render Splash @@ -111,6 +110,7 @@ import 'vs/workbench/contrib/splash/electron-browser/partsSplash.contribution'; // Debug import 'vs/workbench/contrib/debug/node/debugHelperService'; +import 'vs/workbench/contrib/debug/electron-browser/extensionHostDebugService'; // Webview import 'vs/workbench/contrib/webview/electron-browser/webview.contribution'; From 053fe57597db6e87589a12b45358f982491e6640 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 13 Aug 2019 13:02:00 +0200 Subject: [PATCH 106/613] Watching problem matchers should clear problems on task exit Fixes #78485 --- .../workbench/contrib/tasks/common/problemCollectors.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index 9a4884346b061..422c2e47e1b94 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -47,7 +47,7 @@ export abstract class AbstractProblemCollector implements IDisposable { private tail: Promise | undefined; // [owner] -> ApplyToKind - private applyToByOwner: Map; + protected applyToByOwner: Map; // [owner] -> [resource] -> URI private resourcesToClean: Map>; // [owner] -> [resource] -> [markerkey] -> markerData @@ -512,4 +512,11 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement this.deliverMarkersPerOwnerAndResource(this.currentOwner, this.currentResource); } } + + public done(): void { + [...this.applyToByOwner.keys()].forEach(owner => { + this.recordResourcesToClean(owner); + }); + super.done(); + } } From aa3a41995a691b9dd18a203e38e719dc5e56edce Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 13 Aug 2019 13:59:37 +0200 Subject: [PATCH 107/613] web - remove .yarnrc file --- remote/web/.yarnrc | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 remote/web/.yarnrc diff --git a/remote/web/.yarnrc b/remote/web/.yarnrc deleted file mode 100644 index b28191e6bae4c..0000000000000 --- a/remote/web/.yarnrc +++ /dev/null @@ -1,3 +0,0 @@ -disturl "http://nodejs.org/dist" -target "10.11.0" -runtime "node" From 9de099ea8c4c3ee1514777e83b204afe52d44e33 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 13 Aug 2019 15:35:12 +0200 Subject: [PATCH 108/613] web - support vscode.env.openExternal Leverage DOM.windowOpenNoOpener() for this purpose. --- src/vs/workbench/browser/web.simpleservices.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index e31a873b221a0..2dae61b2abf04 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -24,7 +24,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common import { ITunnelService } from 'vs/platform/remote/common/tunnel'; // tslint:disable-next-line: import-patterns import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { addDisposableListener, EventType } from 'vs/base/browser/dom'; +import { addDisposableListener, EventType, windowOpenNoOpener } from 'vs/base/browser/dom'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; import { pathsToEditors } from 'vs/workbench/common/editor'; import { IFileService } from 'vs/platform/files/common/files'; @@ -774,6 +774,8 @@ export class SimpleWindowsService implements IWindowsService { // This needs to be handled from browser process to prevent // foreground ordering issues on Windows openExternal(_url: string): Promise { + windowOpenNoOpener(_url); + return Promise.resolve(true); } From 7c2805ec2ca2548f83748ac687271361d5c15ef4 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 13 Aug 2019 15:42:06 +0200 Subject: [PATCH 109/613] add IllusionMH and gjsjohnmurray for bot commands --- .github/commands.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/commands.yml b/.github/commands.yml index c92792325293c..21ebd49ec3c29 100644 --- a/.github/commands.yml +++ b/.github/commands.yml @@ -4,7 +4,7 @@ { type: 'comment', name: 'question', - allowUsers: ['cleidigh', 'usernamehw'], + allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], action: 'updateLabels', addLabel: '*question' }, @@ -60,7 +60,7 @@ { type: 'comment', name: 'duplicate', - allowUsers: ['cleidigh', 'usernamehw'], + allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], action: 'updateLabels', addLabel: '*duplicate' }, @@ -74,7 +74,7 @@ { type: 'comment', name: 'confirm', - allowUsers: ['cleidigh', 'usernamehw'], + allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], action: 'updateLabels', addLabel: 'confirmed', removeLabel: 'confirmation-pending' @@ -90,14 +90,14 @@ { type: 'comment', name: 'findDuplicates', - allowUsers: ['cleidigh', 'usernamehw'], + allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], action: 'comment', comment: "Potential duplicates:\n${potentialDuplicates}" }, { type: 'comment', name: 'needsMoreInfo', - allowUsers: ['cleidigh', 'usernamehw'], + allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'], action: 'updateLabels', addLabel: 'needs more info', comment: "Thanks for creating this issue! We figured it's missing some basic information or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!" From a6d41c14243821d3e36bc8aab196744669795c18 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 13 Aug 2019 11:58:48 +0200 Subject: [PATCH 110/613] doc :lipstick: --- src/vs/base/browser/ui/grid/gridview.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 38d1131affe90..9b326ffbc2bb5 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -213,6 +213,7 @@ class BranchNode implements ISplitView, IDisposable { throw new Error('Invalid state'); } + // branch nodes should flip the normal/orthogonal directions this._size = orthogonalSize; this._orthogonalSize = size; From fc524344ff5df0aff04da3e087c6ee5e06387c83 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 13 Aug 2019 15:22:28 +0200 Subject: [PATCH 111/613] grid test :lipstick: --- src/vs/base/test/browser/ui/grid/grid.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/test/browser/ui/grid/grid.test.ts b/src/vs/base/test/browser/ui/grid/grid.test.ts index 25468a06bb9ae..a201e8b84effb 100644 --- a/src/vs/base/test/browser/ui/grid/grid.test.ts +++ b/src/vs/base/test/browser/ui/grid/grid.test.ts @@ -511,7 +511,8 @@ suite('SerializableGrid', function () { container.appendChild(grid.element); grid.layout(800, 600); - assert.deepEqual(grid.serialize(), { + const actual = grid.serialize(); + assert.deepEqual(actual, { orientation: 0, width: 800, height: 600, From cd6edb7410bfbcb7f9d38ac2bd8464170728353b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 13 Aug 2019 15:22:39 +0200 Subject: [PATCH 112/613] reduce layout calls related to #77856 --- src/vs/base/browser/ui/grid/gridview.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 9b326ffbc2bb5..fa61ade982e77 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -356,6 +356,10 @@ class BranchNode implements ISplitView, IDisposable { throw new Error('Invalid index'); } + if (this.splitview.isViewVisible(index) === visible) { + return; + } + this.splitview.setViewVisible(index, visible); this._onDidChange.fire(undefined); } From a329c30c3187ced49188aaf7979f606b443ba278 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 13 Aug 2019 15:27:51 +0200 Subject: [PATCH 113/613] :lipstick: --- src/vs/base/browser/ui/grid/gridview.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index fa61ade982e77..934601cf17c02 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -646,8 +646,6 @@ export class GridView implements IDisposable { const { size, orthogonalSize } = this._root; this.root = flipNode(this._root, orthogonalSize, size); this.root.layout(size, orthogonalSize); - // this.root.layout(size); - // this.root.orthogonalLayout(orthogonalSize); } get width(): number { return this.root.width; } From d841937ff9293d082b09fc811da9633c71ee62f0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 13 Aug 2019 16:17:24 +0200 Subject: [PATCH 114/613] web - delete unused SimpleExtensionManagementService --- .../workbench/browser/web.simpleservices.ts | 60 +------------------ 1 file changed, 1 insertion(+), 59 deletions(-) diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 2dae61b2abf04..95d2533c4c6ed 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -8,9 +8,8 @@ import * as browser from 'vs/base/browser/browser'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IGalleryExtension, IExtensionIdentifier, IReportedExtension, IExtensionManagementService, ILocalExtension, IGalleryMetadata } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionTipsService, ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { ExtensionType, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IURLHandler, IURLService } from 'vs/platform/url/common/url'; import { ConsoleLogService, ILogService } from 'vs/platform/log/common/log'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; @@ -80,63 +79,6 @@ registerSingleton(IExtensionTipsService, SimpleExtensionTipsService, true); //#endregion -export class SimpleExtensionManagementService implements IExtensionManagementService { - - _serviceBrand: any; - - onInstallExtension = Event.None; - onDidInstallExtension = Event.None; - onUninstallExtension = Event.None; - onDidUninstallExtension = Event.None; - - zip(extension: ILocalExtension): Promise { - // @ts-ignore - return Promise.resolve(undefined); - } - - unzip(zipLocation: URI, type: ExtensionType): Promise { - // @ts-ignore - return Promise.resolve(undefined); - } - - install(vsix: URI): Promise { - // @ts-ignore - return Promise.resolve(undefined); - } - - installFromGallery(extension: IGalleryExtension): Promise { - // @ts-ignore - return Promise.resolve(undefined); - } - - uninstall(extension: ILocalExtension, force?: boolean): Promise { - return Promise.resolve(undefined); - } - - reinstallFromGallery(extension: ILocalExtension): Promise { - return Promise.resolve(undefined); - } - - getInstalled(type?: ExtensionType): Promise { - // @ts-ignore - return Promise.resolve([]); - } - - getExtensionsReport(): Promise { - // @ts-ignore - return Promise.resolve([]); - } - - updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { - // @ts-ignore - return Promise.resolve(local); - } -} - -registerSingleton(IExtensionManagementService, SimpleExtensionManagementService); - -//#endregion - //#region Extension URL Handler export const IExtensionUrlHandler = createDecorator('inactiveExtensionUrlHandler'); From 6371cad57381ead9cb744393501ae749f1a8ac40 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Tue, 13 Aug 2019 16:46:00 +0200 Subject: [PATCH 115/613] Work around minifier bug (#79044) --- .../services/extensions/node/proxyResolver.ts | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 64c2e0a526e59..12a6e3ea66a37 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -469,24 +469,26 @@ async function readCaCertificates() { } async function readWindowsCaCertificates() { - const winCA = await import('vscode-windows-ca-certs'); - - let ders: any[] = []; - const store = winCA(); - try { - let der: any; - while (der = store.next()) { - ders.push(der); - } - } finally { - store.done(); - } + // Not using await to work around minifier bug (https://github.com/microsoft/vscode/issues/79044). + return import('vscode-windows-ca-certs') + .then(winCA => { + let ders: any[] = []; + const store = winCA(); + try { + let der: any; + while (der = store.next()) { + ders.push(der); + } + } finally { + store.done(); + } - const certs = new Set(ders.map(derToPem)); - return { - certs: Array.from(certs), - append: true - }; + const certs = new Set(ders.map(derToPem)); + return { + certs: Array.from(certs), + append: true + }; + }); } async function readMacCaCertificates() { From 2da1710b0864bb53eee52752fdb42ce135b12585 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 13 Aug 2019 16:53:11 +0200 Subject: [PATCH 116/613] fixes #77841 --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 93a7a5ed2bd0b..a366a0555e348 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -607,11 +607,9 @@ class CallStackDataSource implements IAsyncDataSourcethreads[0]) : Promise.resolve(threads); } else if (isDebugSession(element)) { const childSessions = this.debugService.getModel().getSessions().filter(s => s.parentSession === element); - if (childSessions.length) { - return Promise.resolve(childSessions); - } + const threads: CallStackItem[] = element.getAllThreads(); - return Promise.resolve(element.getAllThreads()); + return Promise.resolve(threads.concat(childSessions)); } else { return this.getThreadChildren(element); } From 1364caff336a417eb5f3bf728b60cb7ae16437f5 Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Tue, 13 Aug 2019 16:00:32 +0100 Subject: [PATCH 117/613] Fix microsoft/vscode#79047 --- src/vs/workbench/contrib/files/common/explorerService.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/contrib/files/common/explorerService.ts b/src/vs/workbench/contrib/files/common/explorerService.ts index 9d0f177a77a94..8196703202889 100644 --- a/src/vs/workbench/contrib/files/common/explorerService.ts +++ b/src/vs/workbench/contrib/files/common/explorerService.ts @@ -159,6 +159,12 @@ export class ExplorerService implements IExplorerService { const options: IResolveFileOptions = { resolveTo: [resource], resolveMetadata: this.sortOrder === 'modified' }; const workspaceFolder = this.contextService.getWorkspaceFolder(resource); const rootUri = workspaceFolder ? workspaceFolder.uri : this.roots[0].resource; + + // Do not waste time looking in a different scheme + if (resource.scheme !== rootUri.scheme) { + return Promise.resolve(undefined); + } + const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop()!; try { From a321c9ea3cd2a71ba82a408be816cb3fbef940dd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 13 Aug 2019 17:43:54 +0200 Subject: [PATCH 118/613] debt - remove unused services from ctor --- src/vs/workbench/contrib/output/browser/outputServices.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index 13d736f2db0a3..6e7141a041b7f 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -14,14 +14,11 @@ import { EditorOptions } from 'vs/workbench/common/editor'; import { IOutputChannelDescriptor, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, LOG_SCHEME, CONTEXT_ACTIVE_LOG_OUTPUT, LOG_MIME, OUTPUT_MIME } from 'vs/workbench/contrib/output/common/output'; import { OutputPanel } from 'vs/workbench/contrib/output/browser/outputPanel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { OutputLinkProvider } from 'vs/workbench/contrib/output/common/outputLinkProvider'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { ITextModel } from 'vs/editor/common/model'; import { IPanel } from 'vs/workbench/common/panel'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IWindowService } from 'vs/platform/windows/common/windows'; import { ILogService } from 'vs/platform/log/common/log'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -77,10 +74,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo @IStorageService private readonly storageService: IStorageService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IPanelService private readonly panelService: IPanelService, - @IWorkspaceContextService contextService: IWorkspaceContextService, @ITextModelService textModelResolverService: ITextModelService, - @IEnvironmentService environmentService: IEnvironmentService, - @IWindowService windowService: IWindowService, @ILogService private readonly logService: ILogService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @IContextKeyService private readonly contextKeyService: IContextKeyService, From bbb1be82bfa933c1777290b376919bc85637723d Mon Sep 17 00:00:00 2001 From: kieferrm Date: Tue, 13 Aug 2019 15:48:26 +0000 Subject: [PATCH 119/613] allow attached-container --- src/vs/platform/telemetry/browser/workbenchCommonProperties.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts b/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts index 3e6c88bd45103..582ace3280996 100644 --- a/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts @@ -76,7 +76,7 @@ function cleanRemoteAuthority(remoteAuthority?: string): string { let ret = 'other'; // Whitelisted remote authorities - ['ssh-remote', 'dev-container', 'wsl'].forEach((res: string) => { + ['ssh-remote', 'dev-container', 'attached-container', 'wsl'].forEach((res: string) => { if (remoteAuthority!.indexOf(`${res}+`) === 0) { ret = res; } From fe4729e2e33d3d6bed23c158c128a528a56322b2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 08:51:00 -0700 Subject: [PATCH 120/613] xterm@3.15.0-beta99 Diff: https://github.com/xtermjs/xterm.js/compare/f3b9dc0...95ff154 Changes: - Fix alt click following links Fixes #78828 --- package.json | 2 +- remote/package.json | 2 +- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index a371ab6f68433..495aaf1e2e435 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "vscode-ripgrep": "^1.5.6", "vscode-sqlite3": "4.0.8", "vscode-textmate": "^4.2.2", - "xterm": "3.15.0-beta98", + "xterm": "3.15.0-beta99", "xterm-addon-search": "0.2.0-beta3", "xterm-addon-web-links": "0.1.0-beta10", "yauzl": "^2.9.2", diff --git a/remote/package.json b/remote/package.json index d3ad3afaed721..ae754c1ae64a8 100644 --- a/remote/package.json +++ b/remote/package.json @@ -21,7 +21,7 @@ "vscode-proxy-agent": "0.4.0", "vscode-ripgrep": "^1.5.6", "vscode-textmate": "^4.2.2", - "xterm": "3.15.0-beta98", + "xterm": "3.15.0-beta99", "xterm-addon-search": "0.2.0-beta3", "xterm-addon-web-links": "0.1.0-beta10", "yauzl": "^2.9.2", diff --git a/remote/yarn.lock b/remote/yarn.lock index b271a9fd5cc14..5d578d3130bfc 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -1227,10 +1227,10 @@ xterm-addon-web-links@0.1.0-beta10: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.1.0-beta10.tgz#610fa9773a2a5ccd41c1c83ba0e2dd2c9eb66a23" integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg== -xterm@3.15.0-beta98: - version "3.15.0-beta98" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta98.tgz#37f37c35577422880e7ef673cc37f9d2a45dd40c" - integrity sha512-vZbg2LcRvoiJOgr1MyeLFM9mF4uib3BWUWDHyFc+vZ58CTuK0iczOvFXgk/ySo23ZLqwmHQSigLgmWvZ8J5G0Q== +xterm@3.15.0-beta99: + version "3.15.0-beta99" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta99.tgz#0010a7ea5d56cbb08a1e3a525b353c96a158e7a0" + integrity sha512-Vm0ZWToWwO4uk/28Kqvqt9L92h5EU2z4WR9I6xcQaPIBmkJPINIARU4LWQnvaOfgFhRbpwBMveTfh8/jM97lPg== yauzl@^2.9.2: version "2.10.0" diff --git a/yarn.lock b/yarn.lock index 62a825ad5948f..0a6b395f80dd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9956,10 +9956,10 @@ xterm-addon-web-links@0.1.0-beta10: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.1.0-beta10.tgz#610fa9773a2a5ccd41c1c83ba0e2dd2c9eb66a23" integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg== -xterm@3.15.0-beta98: - version "3.15.0-beta98" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta98.tgz#37f37c35577422880e7ef673cc37f9d2a45dd40c" - integrity sha512-vZbg2LcRvoiJOgr1MyeLFM9mF4uib3BWUWDHyFc+vZ58CTuK0iczOvFXgk/ySo23ZLqwmHQSigLgmWvZ8J5G0Q== +xterm@3.15.0-beta99: + version "3.15.0-beta99" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta99.tgz#0010a7ea5d56cbb08a1e3a525b353c96a158e7a0" + integrity sha512-Vm0ZWToWwO4uk/28Kqvqt9L92h5EU2z4WR9I6xcQaPIBmkJPINIARU4LWQnvaOfgFhRbpwBMveTfh8/jM97lPg== y18n@^3.2.1: version "3.2.1" From 9d34c8207a99242d2cda7050b3121732a21f8ea9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 08:55:03 -0700 Subject: [PATCH 121/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 495aaf1e2e435..2ad68c6cc7d5c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "a7dba33608e7866342d550c5cd6ed038bba999b4", + "distro": "c70929f01c8688bf05a58fc514f649309e9d45a5", "author": { "name": "Microsoft Corporation" }, From ff0d0e3d6655df94a11e038e3afb1c07095b8c34 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 13 Aug 2019 18:06:18 +0200 Subject: [PATCH 122/613] Use heuristic to avoid scrambled duplicate problems in small terminal Fixes #77475 --- src/vs/platform/markers/common/markers.ts | 9 ++++++++- .../workbench/contrib/tasks/common/problemCollectors.ts | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/markers/common/markers.ts b/src/vs/platform/markers/common/markers.ts index 235c4b12f3640..26d05bc8c2e49 100644 --- a/src/vs/platform/markers/common/markers.ts +++ b/src/vs/platform/markers/common/markers.ts @@ -129,6 +129,10 @@ export interface MarkerStatistics { export namespace IMarkerData { const emptyString = ''; export function makeKey(markerData: IMarkerData): string { + return makeKeyOptionalMessage(markerData, true); + } + + export function makeKeyOptionalMessage(markerData: IMarkerData, useMessage: boolean): string { let result: string[] = [emptyString]; if (markerData.source) { result.push(markerData.source.replace('¦', '\¦')); @@ -145,7 +149,10 @@ export namespace IMarkerData { } else { result.push(emptyString); } - if (markerData.message) { + + // Modifed to not include the message as part of the marker key to work around + // https://github.com/microsoft/vscode/issues/77475 + if (markerData.message && useMessage) { result.push(markerData.message.replace('¦', '\¦')); } else { result.push(emptyString); diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index 422c2e47e1b94..30333ecdc3df5 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -278,9 +278,14 @@ export abstract class AbstractProblemCollector implements IDisposable { markersPerResource = new Map(); markersPerOwner.set(resourceAsString, markersPerResource); } - let key = IMarkerData.makeKey(marker); + let key = IMarkerData.makeKeyOptionalMessage(marker, false); + let existingMarker; if (!markersPerResource.has(key)) { markersPerResource.set(key, marker); + } else if (((existingMarker = markersPerResource.get(key)) !== undefined) && existingMarker.message.length < marker.message.length) { + // Most likely https://github.com/microsoft/vscode/issues/77475 + // Heuristic dictates that when the key is the same and message is smaller, we have hit this limitation. + markersPerResource.set(key, marker); } } From d0ed03f2e6ce71bfa5dcbeb6923ec3256b9d8ecc Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 09:31:09 -0700 Subject: [PATCH 123/613] Move driver enabling behind flag --- src/vs/workbench/browser/web.main.ts | 6 ++++++ src/vs/workbench/workbench.web.api.ts | 5 +++++ src/vs/workbench/workbench.web.main.ts | 4 ---- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 909f7578e4c0a..cf8d773184b56 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -41,6 +41,7 @@ import { BrowserStorageService } from 'vs/platform/storage/browser/storageServic import { IStorageService } from 'vs/platform/storage/common/storage'; import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/theme/common/themeService'; import { InMemoryUserDataProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider'; +import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; class CodeRendererMain extends Disposable { @@ -83,6 +84,11 @@ class CodeRendererMain extends Disposable { })); this._register(workbench.onShutdown(() => this.dispose())); + // Driver + if (this.configuration.driver) { + registerWindowDriver(); + } + // Startup workbench.startup(); } diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index bc4b9d1d7672a..15156252d69f7 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -48,6 +48,11 @@ export interface IWorkbenchConstructionOptions { * A factory for web sockets. */ webSocketFactory?: IWebSocketFactory; + + /** + * Experimental: Whether to enable the smoke test driver. + */ + driver?: boolean; } /** diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 5f9a88accf06d..1a41fc77ac4db 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -54,7 +54,6 @@ import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuS import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; import { ExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; -import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; registerSingleton(IRequestService, RequestService, true); registerSingleton(IExtensionManagementService, ExtensionManagementService); @@ -91,6 +90,3 @@ import 'vs/workbench/contrib/terminal/browser/terminalInstanceService'; import 'vs/workbench/contrib/tasks/browser/taskService'; //#endregion - -// TODO: This should only be registered in a particular launch setup -registerWindowDriver(); From e89abca44186a74bc6df2bb29ea9b82839b6cdda Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 09:31:55 -0700 Subject: [PATCH 124/613] Add driver option to IWorkbenchConstructionOptions --- src/vs/workbench/browser/web.main.ts | 5 +++++ src/vs/workbench/workbench.web.api.ts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 909f7578e4c0a..0dd0a93ef4656 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -83,6 +83,11 @@ class CodeRendererMain extends Disposable { })); this._register(workbench.onShutdown(() => this.dispose())); + // Driver + if (this.configuration.driver) { + registerWindowDriver(); + } + // Startup workbench.startup(); } diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index bc4b9d1d7672a..15156252d69f7 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -48,6 +48,11 @@ export interface IWorkbenchConstructionOptions { * A factory for web sockets. */ webSocketFactory?: IWebSocketFactory; + + /** + * Experimental: Whether to enable the smoke test driver. + */ + driver?: boolean; } /** From 9ee03331e8631e1e569f6ac1250c9e8aaf4c4d8f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 09:38:08 -0700 Subject: [PATCH 125/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ad68c6cc7d5c..cf5ff6a7afb9b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "c70929f01c8688bf05a58fc514f649309e9d45a5", + "distro": "6ce1c040c0d555b998dba62dd333f437a7b2d44f", "author": { "name": "Microsoft Corporation" }, From 96be6fd2120a74fcd60b89bce9a1b7fb031dec74 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 09:46:21 -0700 Subject: [PATCH 126/613] Make monaco includes consistent --- src/tsconfig.monaco.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 32983503724dd..aa0af48b342b8 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -14,7 +14,7 @@ }, "include": [ "typings/require.d.ts", - "./typings/require-monaco.d.ts", + "typings/require-monaco.d.ts", "typings/thenable.d.ts", "typings/es6-promise.d.ts", "typings/lib.es2018.promise.d.ts", From 8f1c14f737988c06c30dd83936da488e8a55711d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 09:48:58 -0700 Subject: [PATCH 127/613] Fix naming of puppeteer driver --- test/smoke/src/vscode/code.ts | 2 +- .../src/vscode/{puppeteer-driver.ts => puppeteerDriver.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename test/smoke/src/vscode/{puppeteer-driver.ts => puppeteerDriver.ts} (100%) diff --git a/test/smoke/src/vscode/code.ts b/test/smoke/src/vscode/code.ts index f381ed002e97a..364e44acf3c7e 100644 --- a/test/smoke/src/vscode/code.ts +++ b/test/smoke/src/vscode/code.ts @@ -10,7 +10,7 @@ import * as fs from 'fs'; import * as mkdirp from 'mkdirp'; import { tmpName } from 'tmp'; import { IDriver, connect as connectElectronDriver, IDisposable, IElement, Thenable } from './driver'; -import { connect as connectPuppeteerDriver, launch } from './puppeteer-driver'; +import { connect as connectPuppeteerDriver, launch } from './puppeteerDriver'; import { Logger } from '../logger'; import { ncp } from 'ncp'; import { URI } from 'vscode-uri'; diff --git a/test/smoke/src/vscode/puppeteer-driver.ts b/test/smoke/src/vscode/puppeteerDriver.ts similarity index 100% rename from test/smoke/src/vscode/puppeteer-driver.ts rename to test/smoke/src/vscode/puppeteerDriver.ts From 933dc05cf986f9c13d72e6c38f49c8bf008df1ed Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 10:21:33 -0700 Subject: [PATCH 128/613] Fix compile --- src/vs/workbench/browser/web.main.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 0dd0a93ef4656..909f7578e4c0a 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -83,11 +83,6 @@ class CodeRendererMain extends Disposable { })); this._register(workbench.onShutdown(() => this.dispose())); - // Driver - if (this.configuration.driver) { - registerWindowDriver(); - } - // Startup workbench.startup(); } From ca2eb3be062bb5d6f15788667b523557b9f33dae Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 13 Aug 2019 10:33:48 -0700 Subject: [PATCH 129/613] Skip using vscode's encoding for port forwarded uris Fixes https://github.com/microsoft/vscode-remote-release/issues/1132 --- src/vs/workbench/contrib/webview/common/portMapping.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/webview/common/portMapping.ts b/src/vs/workbench/contrib/webview/common/portMapping.ts index 964c0ac6da057..e47390dcf0547 100644 --- a/src/vs/workbench/contrib/webview/common/portMapping.ts +++ b/src/vs/workbench/contrib/webview/common/portMapping.ts @@ -47,16 +47,16 @@ export class WebviewPortMappingManager extends Disposable { if (this.extensionLocation && this.extensionLocation.scheme === REMOTE_HOST_SCHEME) { const tunnel = await this.getOrCreateTunnel(mapping.extensionHostPort); if (tunnel) { - return uri.with({ + return encodeURI(uri.with({ authority: `127.0.0.1:${tunnel.tunnelLocalPort}`, - }).toString(); + }).toString(true)); } } if (mapping.webviewPort !== mapping.extensionHostPort) { - return uri.with({ + return encodeURI(uri.with({ authority: `${requestLocalHostInfo.address}:${mapping.extensionHostPort}` - }).toString(); + }).toString(true)); } } } @@ -84,4 +84,4 @@ export class WebviewPortMappingManager extends Disposable { } return tunnel; } -} \ No newline at end of file +} From 16d5e2650f7f7d0e21b5a1c9a4602c354b3ec719 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 13 Aug 2019 19:49:21 +0200 Subject: [PATCH 130/613] grid tests :lipstick: --- src/vs/base/test/browser/ui/grid/grid.test.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/base/test/browser/ui/grid/grid.test.ts b/src/vs/base/test/browser/ui/grid/grid.test.ts index a201e8b84effb..d8dbad25721bd 100644 --- a/src/vs/base/test/browser/ui/grid/grid.test.ts +++ b/src/vs/base/test/browser/ui/grid/grid.test.ts @@ -9,6 +9,7 @@ import { TestView, nodesToArrays } from './util'; import { deepClone } from 'vs/base/common/objects'; // Simple example: +// // +-----+---------------+ // | 4 | 2 | // +-----+---------+-----+ @@ -16,6 +17,16 @@ import { deepClone } from 'vs/base/common/objects'; // +---------------+ 3 | // | 5 | | // +---------------+-----+ +// +// V +// +-H +// | +-4 +// | +-2 +// +-H +// | +-V +// | +-1 +// | +-5 +// +-3 suite('Grid', function () { let container: HTMLElement; From 064e134c6c931603b0664f8ed758c47992ec1bf6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 11:19:14 -0700 Subject: [PATCH 131/613] Launch server from tests --- test/smoke/src/vscode/code.ts | 2 +- test/smoke/src/vscode/puppeteerDriver.ts | 29 ++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/test/smoke/src/vscode/code.ts b/test/smoke/src/vscode/code.ts index 364e44acf3c7e..8a7ffc3d7b169 100644 --- a/test/smoke/src/vscode/code.ts +++ b/test/smoke/src/vscode/code.ts @@ -173,7 +173,7 @@ export async function spawn(options: SpawnOptions): Promise { let connectDriver: typeof connectElectronDriver; if (options.web) { - launch(args); + await launch(args); connectDriver = connectPuppeteerDriver.bind(connectPuppeteerDriver, !!options.headless); } else { const spawnOptions: cp.SpawnOptions = { env }; diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 0397c31343634..85621a74cbf24 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -4,6 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as puppeteer from 'puppeteer'; +import { ChildProcess, spawn } from 'child_process'; +import { join } from 'path'; +import { Readable } from 'stream'; const width = 1200; const height = 800; @@ -177,10 +180,23 @@ function timeout(ms: number): Promise { // function runInDriver(call: string, args: (string | boolean)[]): Promise {} let args; +let server: ChildProcess; +let endpoint: string | undefined; -export function launch(_args): void { +export async function launch(_args): Promise { args = _args; - // TODO: Move puppeteer launch here + console.log('launch args', args); + + // TODO: --web-user-data-dir (tmpdir) + server = spawn(join(args[0], '/resources/server/web.sh'), ['--driver', 'web']); + endpoint = await new Promise(r => { + server.stdout.on('data', d => { + const matches = d.toString('ascii').match(/Web UI available at (.+)/); + if (matches !== null) { + r(matches[1]); + } + }); + }); } export function connect(headless: boolean, outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }> { @@ -194,9 +210,14 @@ export function connect(headless: boolean, outPath: string, handle: string): Pro }); const page = (await browser.pages())[0]; await page.setViewport({ width, height }); - await page.goto(`http://127.0.0.1:9888?folder=${args[1]}`); + const endpointSplit = endpoint!.split('#'); + await page.goto(`${endpointSplit[0]}?folder=${args[1]}#${endpointSplit[1]}`); const result = { - client: { dispose: () => { } }, + client: { + dispose: () => { + server.kill(); + } + }, driver: buildDriver(browser, page) }; c(result); From f4deaa16c1e21e275b2a0925e60120d2774844f6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 11:19:55 -0700 Subject: [PATCH 132/613] Remove unused import --- test/smoke/src/vscode/puppeteerDriver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 85621a74cbf24..fe305d7394f52 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -6,7 +6,6 @@ import * as puppeteer from 'puppeteer'; import { ChildProcess, spawn } from 'child_process'; import { join } from 'path'; -import { Readable } from 'stream'; const width = 1200; const height = 800; From 68f94dd8b4310301a213f618d077433ce58a0fbf Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 11:28:29 -0700 Subject: [PATCH 133/613] Use temp user data dir --- test/smoke/src/vscode/puppeteerDriver.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index fe305d7394f52..99ea0443e997b 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -6,6 +6,9 @@ import * as puppeteer from 'puppeteer'; import { ChildProcess, spawn } from 'child_process'; import { join } from 'path'; +import { tmpdir } from 'os'; +import { mkdir } from 'fs'; +import { promisify } from 'util'; const width = 1200; const height = 800; @@ -184,11 +187,18 @@ let endpoint: string | undefined; export async function launch(_args): Promise { args = _args; - console.log('launch args', args); - // TODO: --web-user-data-dir (tmpdir) - server = spawn(join(args[0], '/resources/server/web.sh'), ['--driver', 'web']); - endpoint = await new Promise(r => { + // TODO: Don't open up the system browser + const webUserDataDir = join(tmpdir(), `smoketest-${Math.random() * 10000000000}`); + await promisify(mkdir)(webUserDataDir); + server = spawn(join(args[0], 'resources/server/web.sh'), ['--driver', 'web', '--web-user-data-dir', webUserDataDir]); + server.stderr.on('data', e => console.log('Server error: ' + e)); + endpoint = await waitForEndpoint(); + await timeout(2000); +} + +function waitForEndpoint(): Promise { + return new Promise(r => { server.stdout.on('data', d => { const matches = d.toString('ascii').match(/Web UI available at (.+)/); if (matches !== null) { From 27f5a327c922d4e68004a84b3f6a9e114eb765de Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 11:34:02 -0700 Subject: [PATCH 134/613] Kill process when process exits --- test/smoke/src/vscode/puppeteerDriver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 99ea0443e997b..81b59e3ece62e 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -193,8 +193,8 @@ export async function launch(_args): Promise { await promisify(mkdir)(webUserDataDir); server = spawn(join(args[0], 'resources/server/web.sh'), ['--driver', 'web', '--web-user-data-dir', webUserDataDir]); server.stderr.on('data', e => console.log('Server error: ' + e)); + process.on('exit', () => server.kill()); endpoint = await waitForEndpoint(); - await timeout(2000); } function waitForEndpoint(): Promise { From 3db202b4308b9f92e72dfc6f646f3a41ba4483a7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 11:35:44 -0700 Subject: [PATCH 135/613] Clean up teardown code --- test/smoke/src/vscode/puppeteerDriver.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 81b59e3ece62e..ee6db8b0e5e8d 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -182,7 +182,7 @@ function timeout(ms: number): Promise { // function runInDriver(call: string, args: (string | boolean)[]): Promise {} let args; -let server: ChildProcess; +let server: ChildProcess | undefined; let endpoint: string | undefined; export async function launch(_args): Promise { @@ -193,13 +193,20 @@ export async function launch(_args): Promise { await promisify(mkdir)(webUserDataDir); server = spawn(join(args[0], 'resources/server/web.sh'), ['--driver', 'web', '--web-user-data-dir', webUserDataDir]); server.stderr.on('data', e => console.log('Server error: ' + e)); - process.on('exit', () => server.kill()); + process.on('exit', teardown); endpoint = await waitForEndpoint(); } +function teardown(): void { + if (server) { + server.kill(); + server = undefined; + } +} + function waitForEndpoint(): Promise { return new Promise(r => { - server.stdout.on('data', d => { + server!.stdout.on('data', d => { const matches = d.toString('ascii').match(/Web UI available at (.+)/); if (matches !== null) { r(matches[1]); @@ -222,11 +229,7 @@ export function connect(headless: boolean, outPath: string, handle: string): Pro const endpointSplit = endpoint!.split('#'); await page.goto(`${endpointSplit[0]}?folder=${args[1]}#${endpointSplit[1]}`); const result = { - client: { - dispose: () => { - server.kill(); - } - }, + client: { dispose: () => teardown }, driver: buildDriver(browser, page) }; c(result); From d5840463a32164129dcfa94c0ebbbaca0b74c437 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 11:37:26 -0700 Subject: [PATCH 136/613] Improve logging name --- test/smoke/src/vscode/puppeteerDriver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index ee6db8b0e5e8d..7bf04dc0296dd 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -192,7 +192,7 @@ export async function launch(_args): Promise { const webUserDataDir = join(tmpdir(), `smoketest-${Math.random() * 10000000000}`); await promisify(mkdir)(webUserDataDir); server = spawn(join(args[0], 'resources/server/web.sh'), ['--driver', 'web', '--web-user-data-dir', webUserDataDir]); - server.stderr.on('data', e => console.log('Server error: ' + e)); + server.stderr.on('data', e => console.log('Server stderr: ' + e)); process.on('exit', teardown); endpoint = await waitForEndpoint(); } From 687d759cb6ba1a94f9cc8f09c3db3d3d96b5bc27 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 11:39:17 -0700 Subject: [PATCH 137/613] Try add smoke tests to build --- build/azure-pipelines/web/product-build-web.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index fe5231dacc1b8..c36ee9da2d92b 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -88,6 +88,14 @@ steps: yarn gulp vscode-web-min-ci displayName: Build +- script: | + set -e + cd test/smoke + yarn compile + cd - + yarn smoketest --web --headless + displayName: Smoke tests + # upload only the workbench.web.api.js source maps because # we just compiled these bits in the previous step and the # general task to upload source maps has already been run From 6059f324dd0a11e9e35cc9bb9e46542c5dbd5fe6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 11:45:57 -0700 Subject: [PATCH 138/613] Add continueeOnError to smoke tests --- build/azure-pipelines/web/product-build-web.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index c36ee9da2d92b..8dde071b48572 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -94,6 +94,7 @@ steps: yarn compile cd - yarn smoketest --web --headless + continueOnError: true displayName: Smoke tests # upload only the workbench.web.api.js source maps because From 4af418a01f50f03b4a6756c045abaa6241e6e0df Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 13 Aug 2019 12:35:22 -0700 Subject: [PATCH 139/613] Use bat on Windows --- test/smoke/src/vscode/puppeteerDriver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 7bf04dc0296dd..5d7b163b71c2c 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -191,7 +191,7 @@ export async function launch(_args): Promise { // TODO: Don't open up the system browser const webUserDataDir = join(tmpdir(), `smoketest-${Math.random() * 10000000000}`); await promisify(mkdir)(webUserDataDir); - server = spawn(join(args[0], 'resources/server/web.sh'), ['--driver', 'web', '--web-user-data-dir', webUserDataDir]); + server = spawn(join(args[0], `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`), ['--driver', 'web', '--web-user-data-dir', webUserDataDir]); server.stderr.on('data', e => console.log('Server stderr: ' + e)); process.on('exit', teardown); endpoint = await waitForEndpoint(); From bb256ef71eda218cc327363d05da014729dd79b0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 13 Aug 2019 15:08:58 +0200 Subject: [PATCH 140/613] debt: use action bar from the panel view --- .../parts/views/media/panelviewlet.css | 4 ++ .../browser/parts/views/panelViewlet.ts | 4 ++ .../extensions/browser/extensionsViews.ts | 43 +++++++------------ .../extensions/browser/media/extensions.css | 5 --- .../browser/media/extensionsViewlet.css | 15 ++++--- 5 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/media/panelviewlet.css b/src/vs/workbench/browser/parts/views/media/panelviewlet.css index e1ce2ee1b6067..e022f97beb884 100644 --- a/src/vs/workbench/browser/parts/views/media/panelviewlet.css +++ b/src/vs/workbench/browser/parts/views/media/panelviewlet.css @@ -7,6 +7,10 @@ border-top: none !important; /* less clutter: do not show any border for first views in a panel */ } +.monaco-panel-view .panel > .panel-header > .actions.show { + display: initial; +} + .monaco-panel-view .panel > .panel-header h3.title { white-space: nowrap; text-overflow: ellipsis; diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index ef9dbe0d75ff2..4a5a2a0cbc335 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -41,6 +41,7 @@ export interface IViewletPanelOptions extends IPanelOptions { actionRunner?: IActionRunner; id: string; title: string; + showActionsAlways?: boolean; } export abstract class ViewletPanel extends Panel implements IView { @@ -67,6 +68,7 @@ export abstract class ViewletPanel extends Panel implements IView { protected actionRunner?: IActionRunner; protected toolbar: ToolBar; + private readonly showActionsAlways: boolean = false; private headerContainer: HTMLElement; private titleContainer: HTMLElement; @@ -82,6 +84,7 @@ export abstract class ViewletPanel extends Panel implements IView { this.id = options.id; this.title = options.title; this.actionRunner = options.actionRunner; + this.showActionsAlways = !!options.showActionsAlways; this.focusedViewContextKey = FocusedViewContext.bindTo(contextKeyService); } @@ -133,6 +136,7 @@ export abstract class ViewletPanel extends Panel implements IView { this.renderHeaderTitle(container, this.title); const actions = append(container, $('.actions')); + toggleClass(actions, 'show', this.showActionsAlways); this.toolbar = new ToolBar(actions, this.contextMenuService, { orientation: ActionsOrientation.HORIZONTAL, actionViewItemProvider: action => this.getActionViewItem(action), diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index ae585737ab747..51c49a2202b9e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -14,7 +14,7 @@ import { IExtensionManagementServer, IExtensionManagementServerService, IExtensi import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { append, $, toggleClass } from 'vs/base/browser/dom'; +import { append, $, toggleClass, addClass } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Delegate, Renderer, IExtensionsViewState } from 'vs/workbench/contrib/extensions/browser/extensionsList'; import { IExtension, IExtensionsWorkbenchService, ExtensionState } from 'vs/workbench/contrib/extensions/common/extensions'; @@ -27,7 +27,7 @@ import { OpenGlobalSettingsAction } from 'vs/workbench/contrib/preferences/brows import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; -import { ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ManageExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { WorkbenchPagedList } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -103,16 +103,13 @@ export class ExtensionsListView extends ViewletPanel { @IProductService protected readonly productService: IProductService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title, showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService); this.server = options.server; } protected renderHeader(container: HTMLElement): void { - this.renderHeaderTitle(container); - } - - renderHeaderTitle(container: HTMLElement): void { - super.renderHeaderTitle(container, this.title); + addClass(container, 'extension-view-header'); + super.renderHeader(container); this.badgeContainer = append(container, $('.count-badge-wrapper')); this.badge = new CountBadge(this.badgeContainer); @@ -953,25 +950,15 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { this._register(this.contextService.onDidChangeWorkbenchState(() => this.update())); } - renderHeader(container: HTMLElement): void { - super.renderHeader(container); - - const listActionBar = $('.list-actionbar-container'); - container.insertBefore(listActionBar, this.badgeContainer); - - const actionbar = this._register(new ActionBar(listActionBar, { - animated: false - })); - actionbar.onDidRun(({ error }) => error && this.notificationService.error(error)); + getActions(): IAction[] { + if (!this.installAllAction) { + this.installAllAction = this._register(this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, InstallWorkspaceRecommendedExtensionsAction.LABEL, [])); + this.installAllAction.class = 'octicon octicon-cloud-download'; + } - this.installAllAction = this._register(this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, InstallWorkspaceRecommendedExtensionsAction.LABEL, [])); const configureWorkspaceFolderAction = this._register(this.instantiationService.createInstance(ConfigureWorkspaceFolderRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction.ID, ConfigureWorkspaceFolderRecommendedExtensionsAction.LABEL)); - - this.installAllAction.class = 'octicon octicon-cloud-download'; configureWorkspaceFolderAction.class = 'octicon octicon-pencil'; - - actionbar.push([this.installAllAction], { icon: true, label: false }); - actionbar.push([configureWorkspaceFolderAction], { icon: true, label: false }); + return [this.installAllAction, configureWorkspaceFolderAction]; } async show(query: string): Promise> { @@ -986,9 +973,11 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { this.setRecommendationsToInstall(); } - private setRecommendationsToInstall(): Promise { - return this.getRecommendationsToInstall() - .then(recommendations => { this.installAllAction.recommendations = recommendations; }); + private async setRecommendationsToInstall(): Promise { + const recommendations = await this.getRecommendationsToInstall(); + if (this.installAllAction) { + this.installAllAction.recommendations = recommendations; + } } private getRecommendationsToInstall(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensions.css b/src/vs/workbench/contrib/extensions/browser/media/extensions.css index 4a74b3ce38dea..ad1858f6f0567 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensions.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensions.css @@ -6,8 +6,3 @@ .monaco-workbench .activitybar > .content .monaco-action-bar .action-label.extensions { -webkit-mask: url('extensions-activity-bar.svg') no-repeat 50% 50%; } - -.extensions .split-view-view .panel-header .count-badge-wrapper { - position: absolute; - right: 12px; -} \ No newline at end of file diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css b/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css index ede6bff2a6445..57be7e4e1d49c 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css @@ -27,13 +27,16 @@ height: calc(100% - 38px); } -.extensions-viewlet > .extensions .list-actionbar-container .monaco-action-bar .action-item > .octicon { - font-size: 12px; - line-height: 1; - margin-right: 10px; +.extensions-viewlet > .extensions .extension-view-header .monaco-action-bar { + margin-right: 4px; +} + +.extensions-viewlet > .extensions .extension-view-header .monaco-action-bar .action-item > .action-label.icon.octicon { + vertical-align: middle; + line-height: 22px; } -.extensions-viewlet > .extensions .list-actionbar-container .monaco-action-bar .action-item.disabled { +.extensions-viewlet > .extensions .extension-view-header .monaco-action-bar .action-item.disabled { display: none; } @@ -44,7 +47,7 @@ } .extensions-viewlet > .extensions .panel-header { - padding-right: 28px; + padding-right: 6px; } .extensions-viewlet > .extensions .panel-header > .title { From c954a8ab52fd3ecc5379a6c97be0a5552be0780d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 13 Aug 2019 23:08:25 +0200 Subject: [PATCH 141/613] Fix microsoft/vscode-remote-release/issues/1066 --- .../extensions/browser/extensionsActions.ts | 44 ++++++++++++++----- .../extensions/browser/extensionsViewlet.ts | 4 +- .../extensions/browser/extensionsViews.ts | 13 +++++- .../browser/remoteExtensionsInstaller.ts | 2 +- 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index b598b94ea4bff..4b5d61946543e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -3022,7 +3022,10 @@ interface IExtensionPickItem extends IQuickPickItem { export class InstallLocalExtensionsInRemoteAction extends Action { + private extensions: IExtension[] | undefined = undefined; + constructor( + private readonly selectAndInstall: boolean, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @@ -3034,7 +3037,12 @@ export class InstallLocalExtensionsInRemoteAction extends Action { ) { super('workbench.extensions.actions.installLocalExtensionsInRemote'); this.update(); - this._register(this.extensionsWorkbenchService.onChange(() => this.update())); + this.extensionsWorkbenchService.queryLocal().then(() => this.updateExtensions()); + this._register(this.extensionsWorkbenchService.onChange(() => { + if (this.extensions) { + this.updateExtensions(); + } + })); } get label(): string { @@ -3042,24 +3050,38 @@ export class InstallLocalExtensionsInRemoteAction extends Action { localize('install local extensions', "Install Local Extensions in {0}...", this.extensionManagementServerService.remoteExtensionManagementServer.label) : ''; } + private updateExtensions(): void { + this.extensions = this.extensionsWorkbenchService.local; + this.update(); + } + private update(): void { - this.enabled = this.getLocalExtensionsToInstall().length > 0; + this.enabled = !!this.extensions && this.getExtensionsToInstall(this.extensions).length > 0; + } + + async run(): Promise { + if (this.selectAndInstall) { + return this.selectAndInstallLocalExtensions(); + } else { + const extensionsToInstall = await this.queryExtensionsToInstall(); + return this.installLocalExtensions(extensionsToInstall); + } + } + + private async queryExtensionsToInstall(): Promise { + const local = await this.extensionsWorkbenchService.queryLocal(); + return this.getExtensionsToInstall(local); } - private getLocalExtensionsToInstall(): IExtension[] { - return this.extensionsWorkbenchService.local.filter(extension => { + private getExtensionsToInstall(local: IExtension[]): IExtension[] { + return local.filter(extension => { const action = this.instantiationService.createInstance(RemoteInstallAction); action.extension = extension; return action.enabled; }); } - async run(): Promise { - this.selectAndInstallLocalExtensions(); - return Promise.resolve(); - } - - private selectAndInstallLocalExtensions(): void { + private async selectAndInstallLocalExtensions(): Promise { const quickPick = this.quickInputService.createQuickPick(); quickPick.busy = true; const disposable = quickPick.onDidAccept(() => { @@ -3069,7 +3091,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action { this.onDidAccept(quickPick.selectedItems); }); quickPick.show(); - const localExtensionsToInstall = this.getLocalExtensionsToInstall(); + const localExtensionsToInstall = await this.queryExtensionsToInstall(); quickPick.busy = false; if (localExtensionsToInstall.length) { quickPick.title = localize('install local extensions title', "Install Local Extensions in {0}", this.extensionManagementServerService.remoteExtensionManagementServer!.label); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 50e67a2a8880d..8396d202e4fba 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -22,7 +22,7 @@ import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, AutoUpdate import { ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowDisabledExtensionsAction, ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, - EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, InstallLocalExtensionsInRemoteAction + EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -348,7 +348,6 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio @IInstantiationService instantiationService: IInstantiationService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @INotificationService private readonly notificationService: INotificationService, @IViewletService private readonly viewletService: IViewletService, @IThemeService themeService: IThemeService, @@ -478,7 +477,6 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL), ...(this.configurationService.getValue(AutoUpdateConfigurationKey) ? [this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)] : [this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)]), this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL), - ...(this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer ? [this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction)] : []), new Separator(), this.instantiationService.createInstance(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL), this.instantiationService.createInstance(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 51c49a2202b9e..41731a7311cbb 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -28,7 +28,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ManageExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, ManageExtensionAction, InstallLocalExtensionsInRemoteAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { WorkbenchPagedList } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -73,7 +73,7 @@ class ExtensionListViewWarning extends Error { } export class ExtensionsListView extends ViewletPanel { - private readonly server: IExtensionManagementServer | undefined; + protected readonly server: IExtensionManagementServer | undefined; private messageContainer: HTMLElement; private messageSeverityIcon: HTMLElement; private messageBox: HTMLElement; @@ -861,6 +861,15 @@ export class ServerExtensionsView extends ExtensionsListView { } return super.show(query.trim()); } + + getActions(): IAction[] { + if (this.extensionManagementServerService.localExtensionManagementServer === this.server) { + const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, false)); + installLocalExtensionsInRemoteAction.class = 'octicon octicon-cloud-download'; + return [installLocalExtensionsInRemoteAction]; + } + return []; + } } export class EnabledExtensionsView extends ExtensionsListView { diff --git a/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts b/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts index 84c47be30646f..b140853a8207e 100644 --- a/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts +++ b/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts @@ -22,7 +22,7 @@ export class RemoteExtensionsInstaller extends Disposable implements IWorkbenchC ) { super(); if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { - const installLocalExtensionsInRemoteAction = instantiationService.createInstance(InstallLocalExtensionsInRemoteAction); + const installLocalExtensionsInRemoteAction = instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, true); CommandsRegistry.registerCommand('workbench.extensions.installLocalExtensions', () => installLocalExtensionsInRemoteAction.run()); let disposable = Disposable.None; const appendMenuItem = () => { From 5d49ab27dbca1aaeb78a91dd15543950aa1e0daf Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 13 Aug 2019 23:17:49 +0200 Subject: [PATCH 142/613] fix label --- .../contrib/extensions/browser/extensionsActions.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 4b5d61946543e..ec19e774ab1f6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -3046,8 +3046,12 @@ export class InstallLocalExtensionsInRemoteAction extends Action { } get label(): string { - return this.extensionManagementServerService.remoteExtensionManagementServer ? - localize('install local extensions', "Install Local Extensions in {0}...", this.extensionManagementServerService.remoteExtensionManagementServer.label) : ''; + if (this.extensionManagementServerService.remoteExtensionManagementServer) { + return this.selectAndInstall ? + localize('select and install local extensions', "Install Local Extensions in {0}...", this.extensionManagementServerService.remoteExtensionManagementServer.label) + : localize('install local extensions', "Install Local Extensions in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label); + } + return ''; } private updateExtensions(): void { @@ -3057,6 +3061,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action { private update(): void { this.enabled = !!this.extensions && this.getExtensionsToInstall(this.extensions).length > 0; + this.tooltip = this.label; } async run(): Promise { From 8da78b6b50f49fd5fd70516ee3b17655f6bd5c34 Mon Sep 17 00:00:00 2001 From: Utkarsh Gupta Date: Wed, 14 Aug 2019 02:52:46 +0530 Subject: [PATCH 143/613] fix: event's jsdoc typo --- src/vs/editor/browser/editorBrowser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 496e9cae7ab45..705b6def023dd 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -399,7 +399,7 @@ export interface ICodeEditor extends editorCommon.IEditor { */ onWillType(listener: (text: string) => void): IDisposable; /** - * An event emitted before interpreting typed characters (on the keyboard). + * An event emitted after interpreting typed characters (on the keyboard). * @event * @internal */ From c08cfdf128f3727de2c0a82548d84eb47d97d4ba Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Tue, 13 Aug 2019 14:34:14 -0700 Subject: [PATCH 144/613] Move cleanRemoteAuthority function to common location --- .../browser/workbenchCommonProperties.ts | 16 +--------------- .../platform/telemetry/common/telemetryUtils.ts | 16 ++++++++++++++++ .../telemetry/node/workbenchCommonProperties.ts | 17 +---------------- 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts b/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts index 582ace3280996..8f2d241306144 100644 --- a/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts @@ -12,6 +12,7 @@ export const lastSessionDateStorageKey = 'telemetry.lastSessionDate'; import * as Platform from 'vs/base/common/platform'; import * as uuid from 'vs/base/common/uuid'; +import { cleanRemoteAuthority } from 'vs/platform/telemetry/common/telemetryUtils'; export async function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string | undefined, version: string | undefined, machineId: string, remoteAuthority?: string): Promise<{ [name: string]: string | undefined }> { const result: { [name: string]: string | undefined; } = Object.create(null); @@ -69,18 +70,3 @@ export async function resolveWorkbenchCommonProperties(storageService: IStorageS return result; } -function cleanRemoteAuthority(remoteAuthority?: string): string { - if (!remoteAuthority) { - return 'none'; - } - - let ret = 'other'; - // Whitelisted remote authorities - ['ssh-remote', 'dev-container', 'attached-container', 'wsl'].forEach((res: string) => { - if (remoteAuthority!.indexOf(`${res}+`) === 0) { - ret = res; - } - }); - - return ret; -} diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index f8990d64a32ae..d98abe9e15684 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -288,6 +288,22 @@ export function validateTelemetryData(data?: any): { properties: Properties, mea }; } +export function cleanRemoteAuthority(remoteAuthority?: string): string { + if (!remoteAuthority) { + return 'none'; + } + + let ret = 'other'; + // Whitelisted remote authorities + ['ssh-remote', 'dev-container', 'attached-container', 'wsl'].forEach((res: string) => { + if (remoteAuthority!.indexOf(`${res}+`) === 0) { + ret = res; + } + }); + + return ret; +} + function flatten(obj: any, result: { [key: string]: any }, order: number = 0, prefix?: string): void { if (!obj) { return; diff --git a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts index d1b83a1414611..81d6a4d3d2289 100644 --- a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts @@ -6,6 +6,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; import { instanceStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { cleanRemoteAuthority } from 'vs/platform/telemetry/common/telemetryUtils'; export async function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string | undefined, version: string | undefined, machineId: string, installSourcePath: string, remoteAuthority?: string): Promise<{ [name: string]: string | undefined }> { const result = await resolveCommonProperties(commit, version, machineId, installSourcePath); @@ -30,19 +31,3 @@ export async function resolveWorkbenchCommonProperties(storageService: IStorageS return result; } - -function cleanRemoteAuthority(remoteAuthority?: string): string { - if (!remoteAuthority) { - return 'none'; - } - - let ret = 'other'; - // Whitelisted remote authorities - ['ssh-remote', 'dev-container', 'wsl'].forEach((res: string) => { - if (remoteAuthority!.indexOf(`${res}+`) === 0) { - ret = res; - } - }); - - return ret; -} From 8e28611ac3caf211cdbeba58840ed33c60eb13f3 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 13 Aug 2019 23:55:45 +0200 Subject: [PATCH 145/613] #78168 strict init --- .../extensions/browser/extensionsViewer.ts | 13 ++-- .../extensions/browser/extensionsViews.ts | 76 +++++++++++-------- .../extensions/browser/extensionsWidgets.ts | 10 +-- .../browser/extensionsWorkbenchService.ts | 6 +- 4 files changed, 58 insertions(+), 47 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts index 8747ad8ca519b..aeb50c75c645d 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewer.ts @@ -156,7 +156,7 @@ export class UnknownExtensionRenderer implements IListRenderer { - return this.extensionsWorkdbenchService.open(this.extensionData.extension, sideByside); + if (this._extensionData) { + return this.extensionsWorkdbenchService.open(this._extensionData.extension, sideByside); + } + return Promise.resolve(); } } @@ -263,4 +262,4 @@ export class ExtensionData implements IExtensionData { } return null; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 41731a7311cbb..ab7e8ee8fb195 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -74,14 +74,15 @@ class ExtensionListViewWarning extends Error { } export class ExtensionsListView extends ViewletPanel { protected readonly server: IExtensionManagementServer | undefined; - private messageContainer: HTMLElement; - private messageSeverityIcon: HTMLElement; - private messageBox: HTMLElement; - private extensionsList: HTMLElement; - private badge: CountBadge; - protected badgeContainer: HTMLElement; - private list: WorkbenchPagedList | null; - private queryRequest: { query: string, request: CancelablePromise> } | null; + private bodyTemplate: { + messageContainer: HTMLElement; + messageSeverityIcon: HTMLElement; + messageBox: HTMLElement; + extensionsList: HTMLElement; + } | undefined; + private badge: CountBadge | undefined; + private list: WorkbenchPagedList | null = null; + private queryRequest: { query: string, request: CancelablePromise> } | null = null; constructor( options: ExtensionsListViewOptions, @@ -111,20 +112,19 @@ export class ExtensionsListView extends ViewletPanel { addClass(container, 'extension-view-header'); super.renderHeader(container); - this.badgeContainer = append(container, $('.count-badge-wrapper')); - this.badge = new CountBadge(this.badgeContainer); + this.badge = new CountBadge(append(container, $('.count-badge-wrapper'))); this._register(attachBadgeStyler(this.badge, this.themeService)); } renderBody(container: HTMLElement): void { - this.extensionsList = append(container, $('.extensions-list')); - this.messageContainer = append(container, $('.message-container')); - this.messageSeverityIcon = append(this.messageContainer, $('')); - this.messageBox = append(this.messageContainer, $('.message')); + const extensionsList = append(container, $('.extensions-list')); + const messageContainer = append(container, $('.message-container')); + const messageSeverityIcon = append(messageContainer, $('')); + const messageBox = append(messageContainer, $('.message')); const delegate = new Delegate(); const extensionsViewState = new ExtensionsViewState(); const renderer = this.instantiationService.createInstance(Renderer, extensionsViewState); - this.list = this.instantiationService.createInstance(WorkbenchPagedList, this.extensionsList, delegate, [renderer], { + this.list = this.instantiationService.createInstance(WorkbenchPagedList, extensionsList, delegate, [renderer], { ariaLabel: localize('extensions', "Extensions"), multipleSelectionSupport: false, setRowLineHeight: false, @@ -144,10 +144,19 @@ export class ExtensionsListView extends ViewletPanel { .map(e => e.elements[0]) .filter(e => !!e) .on(this.pin, this)); + + this.bodyTemplate = { + extensionsList, + messageBox, + messageContainer, + messageSeverityIcon + }; } protected layoutBody(height: number, width: number): void { - this.extensionsList.style.height = height + 'px'; + if (this.bodyTemplate) { + this.bodyTemplate.extensionsList.style.height = height + 'px'; + } if (this.list) { this.list.layout(height, width); } @@ -479,7 +488,7 @@ export class ExtensionsListView extends ViewletPanel { } - private _searchExperiments: Promise; + private _searchExperiments: Promise | undefined; private getSearchExperiments(): Promise { if (!this._searchExperiments) { this._searchExperiments = this.experimentService.getExperimentsByType(ExperimentActionType.ExtensionSearchResults); @@ -690,24 +699,27 @@ export class ExtensionsListView extends ViewletPanel { this.list.scrollTop = 0; const count = this.count(); - toggleClass(this.extensionsList, 'hidden', count === 0); - toggleClass(this.messageContainer, 'hidden', count > 0); - this.badge.setCount(count); + if (this.bodyTemplate && this.badge) { - if (count === 0 && this.isBodyVisible()) { - if (error) { - if (error instanceof ExtensionListViewWarning) { - this.messageSeverityIcon.className = SeverityIcon.className(Severity.Warning); - this.messageBox.textContent = getErrorMessage(error); + toggleClass(this.bodyTemplate.extensionsList, 'hidden', count === 0); + toggleClass(this.bodyTemplate.messageContainer, 'hidden', count > 0); + this.badge.setCount(count); + + if (count === 0 && this.isBodyVisible()) { + if (error) { + if (error instanceof ExtensionListViewWarning) { + this.bodyTemplate.messageSeverityIcon.className = SeverityIcon.className(Severity.Warning); + this.bodyTemplate.messageBox.textContent = getErrorMessage(error); + } else { + this.bodyTemplate.messageSeverityIcon.className = SeverityIcon.className(Severity.Error); + this.bodyTemplate.messageBox.textContent = localize('error', "Error while loading extensions. {0}", getErrorMessage(error)); + } } else { - this.messageSeverityIcon.className = SeverityIcon.className(Severity.Error); - this.messageBox.textContent = localize('error', "Error while loading extensions. {0}", getErrorMessage(error)); + this.bodyTemplate.messageSeverityIcon.className = ''; + this.bodyTemplate.messageBox.textContent = localize('no extensions found', "No extensions found."); } - } else { - this.messageSeverityIcon.className = ''; - this.messageBox.textContent = localize('no extensions found', "No extensions found."); + alert(this.bodyTemplate.messageBox.textContent); } - alert(this.messageBox.textContent); } } } @@ -949,7 +961,7 @@ export class RecommendedExtensionsView extends ExtensionsListView { export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { private readonly recommendedExtensionsQuery = '@recommended:workspace'; - private installAllAction: InstallWorkspaceRecommendedExtensionsAction; + private installAllAction: InstallWorkspaceRecommendedExtensionsAction | undefined; renderBody(container: HTMLElement): void { super.renderBody(container); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index e27cc72e7d173..05741f72a6216 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -18,9 +18,9 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } + private _extension: IExtension | null = null; + get extension(): IExtension | null { return this._extension; } + set extension(extension: IExtension | null) { this._extension = extension; this.update(); } update(): void { this.render(); } abstract render(): void; } @@ -183,7 +183,7 @@ export class RecommendationWidget extends ExtensionWidget { private element?: HTMLElement; private readonly disposables = this._register(new DisposableStore()); - private _tooltip: string; + private _tooltip: string = ''; get tooltip(): string { return this._tooltip; } set tooltip(tooltip: string) { if (this._tooltip !== tooltip) { @@ -314,4 +314,4 @@ class RemoteBadge extends Disposable { updateTitle(); } } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 7ddf7c7fa7016..b64689e02c6c9 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -484,7 +484,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension private readonly _onChange: Emitter = new Emitter(); get onChange(): Event { return this._onChange.event; } - private _extensionAllowedBadgeProviders: string[]; + private _extensionAllowedBadgeProviders: string[] | undefined; private installing: IExtension[] = []; constructor( @@ -1096,12 +1096,12 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } - private _ignoredAutoUpdateExtensions: string[]; + private _ignoredAutoUpdateExtensions: string[] | undefined; private get ignoredAutoUpdateExtensions(): string[] { if (!this._ignoredAutoUpdateExtensions) { this._ignoredAutoUpdateExtensions = JSON.parse(this.storageService.get('extensions.ignoredAutoUpdateExtension', StorageScope.GLOBAL, '[]') || '[]'); } - return this._ignoredAutoUpdateExtensions; + return this._ignoredAutoUpdateExtensions!; } private set ignoredAutoUpdateExtensions(extensionIds: string[]) { From e80c80fd207657914539c3bed749b50352239ce7 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 14 Aug 2019 00:29:04 +0200 Subject: [PATCH 146/613] #78168 strict init --- .../extensions/browser/extensionsViewlet.ts | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 8396d202e4fba..d9aef35dae5be 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -319,7 +319,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensionsViewlet { - private onSearchChange: EventOf; + private readonly _onSearchChange: Emitter = this._register(new Emitter()); + private readonly onSearchChange: EventOf = this._onSearchChange.event; private nonEmptyWorkspaceContextKey: IContextKey; private defaultViewsContextKey: IContextKey; private searchMarketplaceExtensionsContextKey: IContextKey; @@ -333,12 +334,10 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio private defaultRecommendedExtensionsContextKey: IContextKey; private searchDelayer: Delayer; - private root: HTMLElement; - - private searchBox: SuggestEnabledInput; - private extensionsBox: HTMLElement; - private primaryActions: IAction[]; - private secondaryActions: IAction[] | null; + private root: HTMLElement | undefined; + private searchBox: SuggestEnabledInput | undefined; + private primaryActions: IAction[] | undefined; + private secondaryActions: IAction[] | null = null; private readonly searchViewletState: MementoObject; constructor( @@ -417,32 +416,35 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this._register(attachSuggestEnabledInputBoxStyler(this.searchBox, this.themeService)); - const _searchChange = new Emitter(); - this.onSearchChange = _searchChange.event; this._register(this.searchBox.onInputDidChange(() => { this.triggerSearch(); - _searchChange.fire(this.searchBox.getValue()); + this._onSearchChange.fire(this.searchBox!.getValue()); }, this)); this._register(this.searchBox.onShouldFocusResults(() => this.focusListView(), this)); this._register(this.onDidChangeVisibility(visible => { if (visible) { - this.searchBox.focus(); + this.searchBox!.focus(); } })); - this.extensionsBox = append(this.root, $('.extensions')); - super.create(this.extensionsBox); + super.create(append(this.root, $('.extensions'))); } focus(): void { - this.searchBox.focus(); + if (this.searchBox) { + this.searchBox.focus(); + } } layout(dimension: Dimension): void { - toggleClass(this.root, 'narrow', dimension.width <= 300); - this.searchBox.layout({ height: 20, width: dimension.width - 34 }); + if (this.root) { + toggleClass(this.root, 'narrow', dimension.width <= 300); + } + if (this.searchBox) { + this.searchBox.layout({ height: 20, width: dimension.width - 34 }); + } super.layout(new Dimension(dimension.width, dimension.height - 38)); } @@ -453,7 +455,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio getActions(): IAction[] { if (!this.primaryActions) { this.primaryActions = [ - this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox.getValue()) + this.instantiationService.createInstance(ClearExtensionsInputAction, ClearExtensionsInputAction.ID, ClearExtensionsInputAction.LABEL, this.onSearchChange, this.searchBox ? this.searchBox.getValue() : '') ]; } return this.primaryActions; @@ -487,22 +489,24 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio } search(value: string): void { - const event = new Event('input', { bubbles: true }) as SearchInputEvent; - event.immediate = true; + if (this.searchBox) { + const event = new Event('input', { bubbles: true }) as SearchInputEvent; + event.immediate = true; - this.searchBox.setValue(value); + this.searchBox.setValue(value); + } } - private triggerSearch(immediate = false): void { - this.searchDelayer.trigger(() => this.doSearch(), immediate || !this.searchBox.getValue() ? 0 : 500).then(undefined, err => this.onError(err)); + private triggerSearch(): void { + this.searchDelayer.trigger(() => this.doSearch(), this.searchBox && this.searchBox.getValue() ? 500 : 0).then(undefined, err => this.onError(err)); } private normalizedQuery(): string { - return this.searchBox.getValue().replace(/@category/g, 'category').replace(/@tag:/g, 'tag:').replace(/@ext:/g, 'ext:'); + return this.searchBox ? this.searchBox.getValue().replace(/@category/g, 'category').replace(/@tag:/g, 'tag:').replace(/@ext:/g, 'ext:') : ''; } protected saveState(): void { - const value = this.searchBox.getValue(); + const value = this.searchBox ? this.searchBox.getValue() : ''; if (ExtensionsListView.isLocalExtensionsQuery(value)) { this.searchViewletState['query.value'] = value; } else { From 5a87b682d8cafe712d675f628c4c2742fc7b50f6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 14 Aug 2019 01:09:36 +0200 Subject: [PATCH 147/613] #78168 strict init --- .../extensions/browser/extensionEditor.ts | 320 ++++++++++-------- 1 file changed, 175 insertions(+), 145 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index e7cc0836f23a9..2a19adea3df60 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -133,29 +133,33 @@ interface IActiveElement { focus(): void; } +interface IExtensionEditorTemplate { + iconContainer: HTMLElement; + icon: HTMLImageElement; + name: HTMLElement; + identifier: HTMLElement; + preview: HTMLElement; + builtin: HTMLElement; + license: HTMLElement; + publisher: HTMLElement; + installCount: HTMLElement; + rating: HTMLElement; + repository: HTMLElement; + description: HTMLElement; + extensionActionBar: ActionBar; + navbar: NavBar; + content: HTMLElement; + subtextContainer: HTMLElement; + subtext: HTMLElement; + ignoreActionbar: ActionBar; + header: HTMLElement; +} + export class ExtensionEditor extends BaseEditor { static readonly ID: string = 'workbench.editor.extension'; - private iconContainer: HTMLElement; - private icon: HTMLImageElement; - private name: HTMLElement; - private identifier: HTMLElement; - private preview: HTMLElement; - private builtin: HTMLElement; - private license: HTMLElement; - private publisher: HTMLElement; - private installCount: HTMLElement; - private rating: HTMLElement; - private repository: HTMLElement; - private description: HTMLElement; - private extensionActionBar: ActionBar; - private navbar: NavBar; - private content: HTMLElement; - private subtextContainer: HTMLElement; - private subtext: HTMLElement; - private ignoreActionbar: ActionBar; - private header: HTMLElement; + private template: IExtensionEditorTemplate | undefined; private extensionReadme: Cache | null; private extensionChangelog: Cache | null; @@ -164,7 +168,7 @@ export class ExtensionEditor extends BaseEditor { private layoutParticipants: ILayoutParticipant[] = []; private readonly contentDisposables = this._register(new DisposableStore()); private readonly transientDisposables = this._register(new DisposableStore()); - private activeElement: IActiveElement | null; + private activeElement: IActiveElement | null = null; private editorLoadComplete: boolean = false; constructor( @@ -192,43 +196,43 @@ export class ExtensionEditor extends BaseEditor { const root = append(parent, $('.extension-editor')); root.tabIndex = 0; // this is required for the focus tracker on the editor root.style.outline = 'none'; - this.header = append(root, $('.header')); + const header = append(root, $('.header')); - this.iconContainer = append(this.header, $('.icon-container')); - this.icon = append(this.iconContainer, $('img.icon', { draggable: false })); + const iconContainer = append(header, $('.icon-container')); + const icon = append(iconContainer, $('img.icon', { draggable: false })); - const details = append(this.header, $('.details')); + const details = append(header, $('.details')); const title = append(details, $('.title')); - this.name = append(title, $('span.name.clickable', { title: localize('name', "Extension name") })); - this.identifier = append(title, $('span.identifier', { title: localize('extension id', "Extension identifier") })); + const name = append(title, $('span.name.clickable', { title: localize('name', "Extension name") })); + const identifier = append(title, $('span.identifier', { title: localize('extension id', "Extension identifier") })); - this.preview = append(title, $('span.preview', { title: localize('preview', "Preview") })); - this.preview.textContent = localize('preview', "Preview"); + const preview = append(title, $('span.preview', { title: localize('preview', "Preview") })); + preview.textContent = localize('preview', "Preview"); - this.builtin = append(title, $('span.builtin')); - this.builtin.textContent = localize('builtin', "Built-in"); + const builtin = append(title, $('span.builtin')); + builtin.textContent = localize('builtin', "Built-in"); const subtitle = append(details, $('.subtitle')); - this.publisher = append(subtitle, $('span.publisher.clickable', { title: localize('publisher', "Publisher name"), tabIndex: 0 })); + const publisher = append(subtitle, $('span.publisher.clickable', { title: localize('publisher', "Publisher name"), tabIndex: 0 })); - this.installCount = append(subtitle, $('span.install', { title: localize('install count', "Install count"), tabIndex: 0 })); + const installCount = append(subtitle, $('span.install', { title: localize('install count', "Install count"), tabIndex: 0 })); - this.rating = append(subtitle, $('span.rating.clickable', { title: localize('rating', "Rating"), tabIndex: 0 })); + const rating = append(subtitle, $('span.rating.clickable', { title: localize('rating', "Rating"), tabIndex: 0 })); - this.repository = append(subtitle, $('span.repository.clickable')); - this.repository.textContent = localize('repository', 'Repository'); - this.repository.style.display = 'none'; - this.repository.tabIndex = 0; + const repository = append(subtitle, $('span.repository.clickable')); + repository.textContent = localize('repository', 'Repository'); + repository.style.display = 'none'; + repository.tabIndex = 0; - this.license = append(subtitle, $('span.license.clickable')); - this.license.textContent = localize('license', 'License'); - this.license.style.display = 'none'; - this.license.tabIndex = 0; + const license = append(subtitle, $('span.license.clickable')); + license.textContent = localize('license', 'License'); + license.style.display = 'none'; + license.tabIndex = 0; - this.description = append(details, $('.description')); + const description = append(details, $('.description')); const extensionActions = append(details, $('.actions')); - this.extensionActionBar = new ActionBar(extensionActions, { + const extensionActionBar = this._register(new ActionBar(extensionActions, { animated: false, actionViewItemProvider: (action: Action) => { if (action instanceof ExtensionEditorDropDownAction) { @@ -236,29 +240,48 @@ export class ExtensionEditor extends BaseEditor { } return undefined; } - }); - - this.subtextContainer = append(details, $('.subtext-container')); - this.subtext = append(this.subtextContainer, $('.subtext')); - this.ignoreActionbar = new ActionBar(this.subtextContainer, { animated: false }); + })); - this._register(this.extensionActionBar); - this._register(this.ignoreActionbar); + const subtextContainer = append(details, $('.subtext-container')); + const subtext = append(subtextContainer, $('.subtext')); + const ignoreActionbar = this._register(new ActionBar(subtextContainer, { animated: false })); - this._register(Event.chain(this.extensionActionBar.onDidRun) + this._register(Event.chain(extensionActionBar.onDidRun) .map(({ error }) => error) .filter(error => !!error) .on(this.onError, this)); - this._register(Event.chain(this.ignoreActionbar.onDidRun) + this._register(Event.chain(ignoreActionbar.onDidRun) .map(({ error }) => error) .filter(error => !!error) .on(this.onError, this)); const body = append(root, $('.body')); - this.navbar = new NavBar(body); - - this.content = append(body, $('.content')); + const navbar = new NavBar(body); + + const content = append(body, $('.content')); + + this.template = { + builtin, + content, + description, + extensionActionBar, + header, + icon, + iconContainer, + identifier, + ignoreActionbar, + installCount, + license, + name, + navbar, + preview, + publisher, + rating, + repository, + subtext, + subtextContainer + }; } private onClick(element: HTMLElement, callback: () => void): IDisposable { @@ -276,6 +299,13 @@ export class ExtensionEditor extends BaseEditor { } async setInput(input: ExtensionsInput, options: EditorOptions, token: CancellationToken): Promise { + if (this.template) { + await this.updateTemplate(input, this.template); + } + return super.setInput(input, options, token); + } + + private async updateTemplate(input: ExtensionsInput, template: IExtensionEditorTemplate): Promise { const runningExtensions = await this.extensionService.getExtensions(); const colorThemes = await this.workbenchThemeService.getColorThemes(); const fileIconThemes = await this.workbenchThemeService.getFileIconThemes(); @@ -290,18 +320,18 @@ export class ExtensionEditor extends BaseEditor { this.extensionChangelog = new Cache(() => createCancelablePromise(token => extension.getChangelog(token))); this.extensionManifest = new Cache(() => createCancelablePromise(token => extension.getManifest(token))); - const remoteBadge = this.instantiationService.createInstance(RemoteBadgeWidget, this.iconContainer, true); - const onError = Event.once(domEvent(this.icon, 'error')); - onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables); - this.icon.src = extension.iconUrl; + const remoteBadge = this.instantiationService.createInstance(RemoteBadgeWidget, template.iconContainer, true); + const onError = Event.once(domEvent(template.icon, 'error')); + onError(() => template.icon.src = extension.iconUrlFallback, null, this.transientDisposables); + template.icon.src = extension.iconUrl; - this.name.textContent = extension.displayName; - this.identifier.textContent = extension.identifier.id; - this.preview.style.display = extension.preview ? 'inherit' : 'none'; - this.builtin.style.display = extension.type === ExtensionType.System ? 'inherit' : 'none'; + template.name.textContent = extension.displayName; + template.identifier.textContent = extension.identifier.id; + template.preview.style.display = extension.preview ? 'inherit' : 'none'; + template.builtin.style.display = extension.type === ExtensionType.System ? 'inherit' : 'none'; - this.publisher.textContent = extension.publisherDisplayName; - this.description.textContent = extension.description; + template.publisher.textContent = extension.publisherDisplayName; + template.description.textContent = extension.description; const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); let recommendationsData = {}; @@ -319,40 +349,40 @@ export class ExtensionEditor extends BaseEditor { */ this.telemetryService.publicLog('extensionGallery:openExtension', assign(extension.telemetryData, recommendationsData)); - toggleClass(this.name, 'clickable', !!extension.url); - toggleClass(this.publisher, 'clickable', !!extension.url); - toggleClass(this.rating, 'clickable', !!extension.url); + toggleClass(template.name, 'clickable', !!extension.url); + toggleClass(template.publisher, 'clickable', !!extension.url); + toggleClass(template.rating, 'clickable', !!extension.url); if (extension.url) { - this.transientDisposables.add(this.onClick(this.name, () => window.open(extension.url))); - this.transientDisposables.add(this.onClick(this.rating, () => window.open(`${extension.url}#review-details`))); - this.transientDisposables.add(this.onClick(this.publisher, () => { + this.transientDisposables.add(this.onClick(template.name, () => window.open(extension.url))); + this.transientDisposables.add(this.onClick(template.rating, () => window.open(`${extension.url}#review-details`))); + this.transientDisposables.add(this.onClick(template.publisher, () => { this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => viewlet.search(`publisher:"${extension.publisherDisplayName}"`)); })); if (extension.licenseUrl) { - this.transientDisposables.add(this.onClick(this.license, () => window.open(extension.licenseUrl))); - this.license.style.display = 'initial'; + this.transientDisposables.add(this.onClick(template.license, () => window.open(extension.licenseUrl))); + template.license.style.display = 'initial'; } else { - this.license.style.display = 'none'; + template.license.style.display = 'none'; } } else { - this.license.style.display = 'none'; + template.license.style.display = 'none'; } if (extension.repository) { - this.transientDisposables.add(this.onClick(this.repository, () => window.open(extension.repository))); - this.repository.style.display = 'initial'; + this.transientDisposables.add(this.onClick(template.repository, () => window.open(extension.repository))); + template.repository.style.display = 'initial'; } else { - this.repository.style.display = 'none'; + template.repository.style.display = 'none'; } const widgets = [ remoteBadge, - this.instantiationService.createInstance(InstallCountWidget, this.installCount, false), - this.instantiationService.createInstance(RatingsWidget, this.rating, false) + this.instantiationService.createInstance(InstallCountWidget, template.installCount, false), + this.instantiationService.createInstance(RatingsWidget, template.rating, false) ]; const reloadAction = this.instantiationService.createInstance(ReloadAction); const combinedInstallAction = this.instantiationService.createInstance(CombinedInstallAction); @@ -375,20 +405,20 @@ export class ExtensionEditor extends BaseEditor { const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets]); extensionContainers.extension = extension; - this.extensionActionBar.clear(); - this.extensionActionBar.push(actions, { icon: true, label: true }); + template.extensionActionBar.clear(); + template.extensionActionBar.push(actions, { icon: true, label: true }); for (const disposable of [...actions, ...widgets, extensionContainers]) { this.transientDisposables.add(disposable); } - this.setSubText(extension, reloadAction); - this.content.innerHTML = ''; // Clear content before setting navbar actions. + this.setSubText(extension, reloadAction, template); + template.content.innerHTML = ''; // Clear content before setting navbar actions. - this.navbar.clear(); - this.navbar.onChange(this.onNavbarChange.bind(this, extension), this, this.transientDisposables); + template.navbar.clear(); + template.navbar.onChange(e => this.onNavbarChange(extension, e, template), this, this.transientDisposables); if (extension.hasReadme()) { - this.navbar.push(NavbarSection.Readme, localize('details', "Details"), localize('detailstooltip', "Extension details, rendered from the extension's 'README.md' file")); + template.navbar.push(NavbarSection.Readme, localize('details', "Details"), localize('detailstooltip', "Extension details, rendered from the extension's 'README.md' file")); } this.extensionManifest.get() .promise @@ -397,25 +427,23 @@ export class ExtensionEditor extends BaseEditor { combinedInstallAction.manifest = manifest; } if (extension.extensionPack.length) { - this.navbar.push(NavbarSection.ExtensionPack, localize('extensionPack', "Extension Pack"), localize('extensionsPack', "Set of extensions that can be installed together")); + template.navbar.push(NavbarSection.ExtensionPack, localize('extensionPack', "Extension Pack"), localize('extensionsPack', "Set of extensions that can be installed together")); } if (manifest && manifest.contributes) { - this.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions"), localize('contributionstooltip', "Lists contributions to VS Code by this extension")); + template.navbar.push(NavbarSection.Contributions, localize('contributions', "Contributions"), localize('contributionstooltip', "Lists contributions to VS Code by this extension")); } if (extension.hasChangelog()) { - this.navbar.push(NavbarSection.Changelog, localize('changelog', "Changelog"), localize('changelogtooltip', "Extension update history, rendered from the extension's 'CHANGELOG.md' file")); + template.navbar.push(NavbarSection.Changelog, localize('changelog', "Changelog"), localize('changelogtooltip', "Extension update history, rendered from the extension's 'CHANGELOG.md' file")); } if (extension.dependencies.length) { - this.navbar.push(NavbarSection.Dependencies, localize('dependencies', "Dependencies"), localize('dependenciestooltip', "Lists extensions this extension depends on")); + template.navbar.push(NavbarSection.Dependencies, localize('dependencies', "Dependencies"), localize('dependenciestooltip', "Lists extensions this extension depends on")); } this.editorLoadComplete = true; }); - - return super.setInput(input, options, token); } - private setSubText(extension: IExtension, reloadAction: ReloadAction): void { - hide(this.subtextContainer); + private setSubText(extension: IExtension, reloadAction: ReloadAction, template: IExtensionEditorTemplate): void { + hide(template.subtextContainer); const ignoreAction = this.instantiationService.createInstance(IgnoreExtensionRecommendationAction); const undoIgnoreAction = this.instantiationService.createInstance(UndoIgnoreExtensionRecommendationAction); @@ -424,23 +452,23 @@ export class ExtensionEditor extends BaseEditor { ignoreAction.enabled = false; undoIgnoreAction.enabled = false; - this.ignoreActionbar.clear(); - this.ignoreActionbar.push([ignoreAction, undoIgnoreAction], { icon: true, label: true }); + template.ignoreActionbar.clear(); + template.ignoreActionbar.push([ignoreAction, undoIgnoreAction], { icon: true, label: true }); this.transientDisposables.add(ignoreAction); this.transientDisposables.add(undoIgnoreAction); const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); if (extRecommendations[extension.identifier.id.toLowerCase()]) { ignoreAction.enabled = true; - this.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; - show(this.subtextContainer); + template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; + show(template.subtextContainer); } else if (this.extensionTipsService.getAllIgnoredRecommendations().global.indexOf(extension.identifier.id.toLowerCase()) !== -1) { undoIgnoreAction.enabled = true; - this.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); - show(this.subtextContainer); + template.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); + show(template.subtextContainer); } else { - this.subtext.textContent = ''; + template.subtext.textContent = ''; } this.extensionTipsService.onRecommendationChange(change => { @@ -450,28 +478,28 @@ export class ExtensionEditor extends BaseEditor { const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); if (extRecommendations[extension.identifier.id.toLowerCase()]) { ignoreAction.enabled = true; - this.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; + template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; } } else { undoIgnoreAction.enabled = true; ignoreAction.enabled = false; - this.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); + template.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); } } }); this.transientDisposables.add(reloadAction.onDidChange(e => { if (e.tooltip) { - this.subtext.textContent = reloadAction.tooltip; - show(this.subtextContainer); + template.subtext.textContent = reloadAction.tooltip; + show(template.subtextContainer); ignoreAction.enabled = false; undoIgnoreAction.enabled = false; } if (e.enabled === true) { - show(this.subtextContainer); + show(template.subtextContainer); } if (e.enabled === false) { - hide(this.subtextContainer); + hide(template.subtextContainer); } })); } @@ -495,7 +523,7 @@ export class ExtensionEditor extends BaseEditor { } } - private onNavbarChange(extension: IExtension, { id, focus }: { id: string, focus: boolean }): void { + private onNavbarChange(extension: IExtension, { id, focus }: { id: string | null, focus: boolean }, template: IExtensionEditorTemplate): void { if (this.editorLoadComplete) { /* __GDPR__ "extensionEditor:navbarChange" : { @@ -509,30 +537,32 @@ export class ExtensionEditor extends BaseEditor { } this.contentDisposables.clear(); - this.content.innerHTML = ''; + template.content.innerHTML = ''; this.activeElement = null; - this.open(id, extension) - .then(activeElement => { - this.activeElement = activeElement; - if (focus) { - this.focus(); - } - }); + if (id) { + this.open(id, extension, template) + .then(activeElement => { + this.activeElement = activeElement; + if (focus) { + this.focus(); + } + }); + } } - private open(id: string, extension: IExtension): Promise { + private open(id: string, extension: IExtension, template: IExtensionEditorTemplate): Promise { switch (id) { - case NavbarSection.Readme: return this.openReadme(); - case NavbarSection.Contributions: return this.openContributions(); - case NavbarSection.Changelog: return this.openChangelog(); - case NavbarSection.Dependencies: return this.openDependencies(extension); - case NavbarSection.ExtensionPack: return this.openExtensionPack(extension); + case NavbarSection.Readme: return this.openReadme(template); + case NavbarSection.Contributions: return this.openContributions(template); + case NavbarSection.Changelog: return this.openChangelog(template); + case NavbarSection.Dependencies: return this.openDependencies(extension, template); + case NavbarSection.ExtensionPack: return this.openExtensionPack(extension, template); } return Promise.resolve(null); } - private openMarkdown(cacheResult: CacheResult, noContentCopy: string): Promise { - return this.loadContents(() => cacheResult) + private openMarkdown(cacheResult: CacheResult, noContentCopy: string, template: IExtensionEditorTemplate): Promise { + return this.loadContents(() => cacheResult, template) .then(marked.parse) .then(content => this.renderBody(content)) .then(removeEmbeddedSVGs) @@ -544,7 +574,7 @@ export class ExtensionEditor extends BaseEditor { { svgWhiteList: this.extensionsWorkbenchService.allowedBadgeProviders, }); - webviewElement.mountTo(this.content); + webviewElement.mountTo(template.content); this.contentDisposables.add(webviewElement.onDidFocus(() => this.fireOnDidFocus())); const removeLayoutParticipant = arrays.insert(this.layoutParticipants, webviewElement); this.contentDisposables.add(toDisposable(removeLayoutParticipant)); @@ -563,7 +593,7 @@ export class ExtensionEditor extends BaseEditor { return webviewElement; }) .then(undefined, () => { - const p = append(this.content, $('p.nocontent')); + const p = append(template.content, $('p.nocontent')); p.textContent = noContentCopy; return p; }); @@ -757,17 +787,17 @@ export class ExtensionEditor extends BaseEditor { `; } - private openReadme(): Promise { - return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available.")); + private openReadme(template: IExtensionEditorTemplate): Promise { + return this.openMarkdown(this.extensionReadme!.get(), localize('noReadme', "No README available."), template); } - private openChangelog(): Promise { - return this.openMarkdown(this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available.")); + private openChangelog(template: IExtensionEditorTemplate): Promise { + return this.openMarkdown(this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available."), template); } - private openContributions(): Promise { + private openContributions(template: IExtensionEditorTemplate): Promise { const content = $('div', { class: 'subcontent', tabindex: '0' }); - return this.loadContents(() => this.extensionManifest!.get()) + return this.loadContents(() => this.extensionManifest!.get(), template) .then(manifest => { if (!manifest) { return content; @@ -798,28 +828,28 @@ export class ExtensionEditor extends BaseEditor { const isEmpty = !renders.some(x => x); if (isEmpty) { append(content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions"); - append(this.content, content); + append(template.content, content); } else { - append(this.content, scrollableContent.getDomNode()); + append(template.content, scrollableContent.getDomNode()); this.contentDisposables.add(scrollableContent); } return content; }, () => { append(content, $('p.nocontent')).textContent = localize('noContributions', "No Contributions"); - append(this.content, content); + append(template.content, content); return content; }); } - private openDependencies(extension: IExtension): Promise { + private openDependencies(extension: IExtension, template: IExtensionEditorTemplate): Promise { if (arrays.isFalsyOrEmpty(extension.dependencies)) { - append(this.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies"); - return Promise.resolve(this.content); + append(template.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies"); + return Promise.resolve(template.content); } const content = $('div', { class: 'subcontent' }); const scrollableContent = new DomScrollableElement(content, {}); - append(this.content, scrollableContent.getDomNode()); + append(template.content, scrollableContent.getDomNode()); this.contentDisposables.add(scrollableContent); const dependenciesTree = this.instantiationService.createInstance(ExtensionsTree, new ExtensionData(extension, null, extension => extension.dependencies || [], this.extensionsWorkbenchService), content); @@ -836,10 +866,10 @@ export class ExtensionEditor extends BaseEditor { return Promise.resolve({ focus() { dependenciesTree.domFocus(); } }); } - private openExtensionPack(extension: IExtension): Promise { + private openExtensionPack(extension: IExtension, template: IExtensionEditorTemplate): Promise { const content = $('div', { class: 'subcontent' }); const scrollableContent = new DomScrollableElement(content, {}); - append(this.content, scrollableContent.getDomNode()); + append(template.content, scrollableContent.getDomNode()); this.contentDisposables.add(scrollableContent); const extensionsPackTree = this.instantiationService.createInstance(ExtensionsTree, new ExtensionData(extension, null, extension => extension.extensionPack || [], this.extensionsWorkbenchService), content); @@ -1259,11 +1289,11 @@ export class ExtensionEditor extends BaseEditor { return null; } - private loadContents(loadingTask: () => CacheResult): Promise { - addClass(this.content, 'loading'); + private loadContents(loadingTask: () => CacheResult, template: IExtensionEditorTemplate): Promise { + addClass(template.content, 'loading'); const result = loadingTask(); - const onDone = () => removeClass(this.content, 'loading'); + const onDone = () => removeClass(template.content, 'loading'); result.promise.then(onDone, onDone); this.contentDisposables.add(toDisposable(() => result.dispose())); From ec07311dab2556c9d66a4cb3eecdc21c524202e1 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 13 Aug 2019 16:23:31 -0700 Subject: [PATCH 148/613] fixes #77797 --- src/vs/workbench/browser/parts/sidebar/sidebarPart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 66650e527f7cb..9e17bf39cc4bb 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -63,7 +63,7 @@ export class SidebarPart extends CompositePart implements IViewletServi return; } - return width; + return Math.max(width, 300); } //#endregion From 82601dcaa4b7aaea6c71d7e375484454321dcceb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 13 Aug 2019 11:54:50 -0700 Subject: [PATCH 149/613] Split formatted text renderer into own file --- src/vs/base/browser/formattedTextRenderer.ts | 199 +++++++++++++++++ src/vs/base/browser/htmlContentRenderer.ts | 201 +----------------- src/vs/base/browser/ui/inputbox/inputBox.ts | 3 +- .../browser/formattedTextRenderer.test.ts | 104 +++++++++ src/vs/base/test/browser/htmlContent.test.ts | 101 +-------- .../accessibilityHelp/accessibilityHelp.ts | 2 +- .../browser/accessibility/accessibility.ts | 2 +- 7 files changed, 314 insertions(+), 298 deletions(-) create mode 100644 src/vs/base/browser/formattedTextRenderer.ts create mode 100644 src/vs/base/test/browser/formattedTextRenderer.test.ts diff --git a/src/vs/base/browser/formattedTextRenderer.ts b/src/vs/base/browser/formattedTextRenderer.ts new file mode 100644 index 0000000000000..6d33caf8acc61 --- /dev/null +++ b/src/vs/base/browser/formattedTextRenderer.ts @@ -0,0 +1,199 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { createElement, IContentActionHandler, RenderOptions } from 'vs/base/browser/htmlContentRenderer'; + +export function renderText(text: string, options: RenderOptions = {}): HTMLElement { + const element = createElement(options); + element.textContent = text; + return element; +} + +export function renderFormattedText(formattedText: string, options: RenderOptions = {}): HTMLElement { + const element = createElement(options); + _renderFormattedText(element, parseFormattedText(formattedText), options.actionHandler); + return element; +} + +class StringStream { + private source: string; + private index: number; + + constructor(source: string) { + this.source = source; + this.index = 0; + } + + public eos(): boolean { + return this.index >= this.source.length; + } + + public next(): string { + const next = this.peek(); + this.advance(); + return next; + } + + public peek(): string { + return this.source[this.index]; + } + + public advance(): void { + this.index++; + } +} + +const enum FormatType { + Invalid, + Root, + Text, + Bold, + Italics, + Action, + ActionClose, + NewLine +} + +interface IFormatParseTree { + type: FormatType; + content?: string; + index?: number; + children?: IFormatParseTree[]; +} + +export function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionHandler?: IContentActionHandler) { + let child: Node | undefined; + + if (treeNode.type === FormatType.Text) { + child = document.createTextNode(treeNode.content || ''); + } else if (treeNode.type === FormatType.Bold) { + child = document.createElement('b'); + } else if (treeNode.type === FormatType.Italics) { + child = document.createElement('i'); + } else if (treeNode.type === FormatType.Action && actionHandler) { + const a = document.createElement('a'); + a.href = '#'; + actionHandler.disposeables.add(DOM.addStandardDisposableListener(a, 'click', (event) => { + actionHandler.callback(String(treeNode.index), event); + })); + + child = a; + } else if (treeNode.type === FormatType.NewLine) { + child = document.createElement('br'); + } else if (treeNode.type === FormatType.Root) { + child = element; + } + + if (child && element !== child) { + element.appendChild(child); + } + + if (child && Array.isArray(treeNode.children)) { + treeNode.children.forEach((nodeChild) => { + _renderFormattedText(child!, nodeChild, actionHandler); + }); + } +} + +export function parseFormattedText(content: string): IFormatParseTree { + + const root: IFormatParseTree = { + type: FormatType.Root, + children: [] + }; + + let actionViewItemIndex = 0; + let current = root; + const stack: IFormatParseTree[] = []; + const stream = new StringStream(content); + + while (!stream.eos()) { + let next = stream.next(); + + const isEscapedFormatType = (next === '\\' && formatTagType(stream.peek()) !== FormatType.Invalid); + if (isEscapedFormatType) { + next = stream.next(); // unread the backslash if it escapes a format tag type + } + + if (!isEscapedFormatType && isFormatTag(next) && next === stream.peek()) { + stream.advance(); + + if (current.type === FormatType.Text) { + current = stack.pop()!; + } + + const type = formatTagType(next); + if (current.type === type || (current.type === FormatType.Action && type === FormatType.ActionClose)) { + current = stack.pop()!; + } else { + const newCurrent: IFormatParseTree = { + type: type, + children: [] + }; + + if (type === FormatType.Action) { + newCurrent.index = actionViewItemIndex; + actionViewItemIndex++; + } + + current.children!.push(newCurrent); + stack.push(current); + current = newCurrent; + } + } else if (next === '\n') { + if (current.type === FormatType.Text) { + current = stack.pop()!; + } + + current.children!.push({ + type: FormatType.NewLine + }); + + } else { + if (current.type !== FormatType.Text) { + const textCurrent: IFormatParseTree = { + type: FormatType.Text, + content: next + }; + current.children!.push(textCurrent); + stack.push(current); + current = textCurrent; + + } else { + current.content += next; + } + } + } + + if (current.type === FormatType.Text) { + current = stack.pop()!; + } + + if (stack.length) { + // incorrectly formatted string literal + } + + return root; +} + +function isFormatTag(char: string): boolean { + return formatTagType(char) !== FormatType.Invalid; +} + +function formatTagType(char: string): FormatType { + switch (char) { + case '*': + return FormatType.Bold; + case '_': + return FormatType.Italics; + case '[': + return FormatType.Action; + case ']': + return FormatType.ActionClose; + default: + return FormatType.Invalid; + } +} diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/htmlContentRenderer.ts index 4506912e178e9..80f956bf1381a 100644 --- a/src/vs/base/browser/htmlContentRenderer.ts +++ b/src/vs/base/browser/htmlContentRenderer.ts @@ -28,7 +28,7 @@ export interface RenderOptions { codeBlockRenderCallback?: () => void; } -function createElement(options: RenderOptions): HTMLElement { +export function createElement(options: RenderOptions): HTMLElement { const tagName = options.inline ? 'span' : 'div'; const element = document.createElement(tagName); if (options.className) { @@ -37,18 +37,6 @@ function createElement(options: RenderOptions): HTMLElement { return element; } -export function renderText(text: string, options: RenderOptions = {}): HTMLElement { - const element = createElement(options); - element.textContent = text; - return element; -} - -export function renderFormattedText(formattedText: string, options: RenderOptions = {}): HTMLElement { - const element = createElement(options); - _renderFormattedText(element, parseFormattedText(formattedText), options.actionHandler); - return element; -} - /** * Create html nodes for the given content element. */ @@ -205,190 +193,3 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions return element; } - -// --- formatted string parsing - -class StringStream { - private source: string; - private index: number; - - constructor(source: string) { - this.source = source; - this.index = 0; - } - - public eos(): boolean { - return this.index >= this.source.length; - } - - public next(): string { - const next = this.peek(); - this.advance(); - return next; - } - - public peek(): string { - return this.source[this.index]; - } - - public advance(): void { - this.index++; - } -} - -const enum FormatType { - Invalid, - Root, - Text, - Bold, - Italics, - Action, - ActionClose, - NewLine -} - -interface IFormatParseTree { - type: FormatType; - content?: string; - index?: number; - children?: IFormatParseTree[]; -} - -function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionHandler?: IContentActionHandler) { - let child: Node | undefined; - - if (treeNode.type === FormatType.Text) { - child = document.createTextNode(treeNode.content || ''); - } - else if (treeNode.type === FormatType.Bold) { - child = document.createElement('b'); - } - else if (treeNode.type === FormatType.Italics) { - child = document.createElement('i'); - } - else if (treeNode.type === FormatType.Action && actionHandler) { - const a = document.createElement('a'); - a.href = '#'; - actionHandler.disposeables.add(DOM.addStandardDisposableListener(a, 'click', (event) => { - actionHandler.callback(String(treeNode.index), event); - })); - - child = a; - } - else if (treeNode.type === FormatType.NewLine) { - child = document.createElement('br'); - } - else if (treeNode.type === FormatType.Root) { - child = element; - } - - if (child && element !== child) { - element.appendChild(child); - } - - if (child && Array.isArray(treeNode.children)) { - treeNode.children.forEach((nodeChild) => { - _renderFormattedText(child!, nodeChild, actionHandler); - }); - } -} - -function parseFormattedText(content: string): IFormatParseTree { - - const root: IFormatParseTree = { - type: FormatType.Root, - children: [] - }; - - let actionViewItemIndex = 0; - let current = root; - const stack: IFormatParseTree[] = []; - const stream = new StringStream(content); - - while (!stream.eos()) { - let next = stream.next(); - - const isEscapedFormatType = (next === '\\' && formatTagType(stream.peek()) !== FormatType.Invalid); - if (isEscapedFormatType) { - next = stream.next(); // unread the backslash if it escapes a format tag type - } - - if (!isEscapedFormatType && isFormatTag(next) && next === stream.peek()) { - stream.advance(); - - if (current.type === FormatType.Text) { - current = stack.pop()!; - } - - const type = formatTagType(next); - if (current.type === type || (current.type === FormatType.Action && type === FormatType.ActionClose)) { - current = stack.pop()!; - } else { - const newCurrent: IFormatParseTree = { - type: type, - children: [] - }; - - if (type === FormatType.Action) { - newCurrent.index = actionViewItemIndex; - actionViewItemIndex++; - } - - current.children!.push(newCurrent); - stack.push(current); - current = newCurrent; - } - } else if (next === '\n') { - if (current.type === FormatType.Text) { - current = stack.pop()!; - } - - current.children!.push({ - type: FormatType.NewLine - }); - - } else { - if (current.type !== FormatType.Text) { - const textCurrent: IFormatParseTree = { - type: FormatType.Text, - content: next - }; - current.children!.push(textCurrent); - stack.push(current); - current = textCurrent; - - } else { - current.content += next; - } - } - } - - if (current.type === FormatType.Text) { - current = stack.pop()!; - } - - if (stack.length) { - // incorrectly formatted string literal - } - - return root; -} - -function isFormatTag(char: string): boolean { - return formatTagType(char) !== FormatType.Invalid; -} - -function formatTagType(char: string): FormatType { - switch (char) { - case '*': - return FormatType.Bold; - case '_': - return FormatType.Italics; - case '[': - return FormatType.Action; - case ']': - return FormatType.ActionClose; - default: - return FormatType.Invalid; - } -} diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 59d0c9900b9e3..fb8658883c30c 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -8,7 +8,8 @@ import 'vs/css!./inputBox'; import * as nls from 'vs/nls'; import * as Bal from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; -import { RenderOptions, renderFormattedText, renderText } from 'vs/base/browser/htmlContentRenderer'; +import { RenderOptions } from 'vs/base/browser/htmlContentRenderer'; +import { renderFormattedText, renderText } from 'vs/base/browser/formattedTextRenderer'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IAction } from 'vs/base/common/actions'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; diff --git a/src/vs/base/test/browser/formattedTextRenderer.test.ts b/src/vs/base/test/browser/formattedTextRenderer.test.ts new file mode 100644 index 0000000000000..0be9747852205 --- /dev/null +++ b/src/vs/base/test/browser/formattedTextRenderer.test.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { renderText, renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; +import { DisposableStore } from 'vs/base/common/lifecycle'; + +suite('FormattedTextRenderer', () => { + const store = new DisposableStore(); + + setup(() => { + store.clear(); + }); + + teardown(() => { + store.clear(); + }); + + test('render simple element', () => { + let result: HTMLElement = renderText('testing'); + + assert.strictEqual(result.nodeType, document.ELEMENT_NODE); + assert.strictEqual(result.textContent, 'testing'); + assert.strictEqual(result.tagName, 'DIV'); + }); + + test('render element with class', () => { + let result: HTMLElement = renderText('testing', { + className: 'testClass' + }); + assert.strictEqual(result.nodeType, document.ELEMENT_NODE); + assert.strictEqual(result.className, 'testClass'); + }); + + test('simple formatting', () => { + let result: HTMLElement = renderFormattedText('**bold**'); + assert.strictEqual(result.children.length, 1); + assert.strictEqual(result.firstChild!.textContent, 'bold'); + assert.strictEqual((result.firstChild).tagName, 'B'); + assert.strictEqual(result.innerHTML, 'bold'); + + result = renderFormattedText('__italics__'); + assert.strictEqual(result.innerHTML, 'italics'); + + result = renderFormattedText('this string has **bold** and __italics__'); + assert.strictEqual(result.innerHTML, 'this string has bold and italics'); + }); + + test('no formatting', () => { + let result: HTMLElement = renderFormattedText('this is just a string'); + assert.strictEqual(result.innerHTML, 'this is just a string'); + }); + + test('preserve newlines', () => { + let result: HTMLElement = renderFormattedText('line one\nline two'); + assert.strictEqual(result.innerHTML, 'line one
line two'); + }); + + test('action', () => { + let callbackCalled = false; + let result: HTMLElement = renderFormattedText('[[action]]', { + actionHandler: { + callback(content) { + assert.strictEqual(content, '0'); + callbackCalled = true; + }, + disposeables: store + } + }); + assert.strictEqual(result.innerHTML, 'action'); + + let event: MouseEvent = document.createEvent('MouseEvent'); + event.initEvent('click', true, true); + result.firstChild!.dispatchEvent(event); + assert.strictEqual(callbackCalled, true); + }); + + test('fancy action', () => { + let callbackCalled = false; + let result: HTMLElement = renderFormattedText('__**[[action]]**__', { + actionHandler: { + callback(content) { + assert.strictEqual(content, '0'); + callbackCalled = true; + }, + disposeables: store + } + }); + assert.strictEqual(result.innerHTML, 'action'); + + let event: MouseEvent = document.createEvent('MouseEvent'); + event.initEvent('click', true, true); + result.firstChild!.firstChild!.firstChild!.dispatchEvent(event); + assert.strictEqual(callbackCalled, true); + }); + + test('escaped formatting', () => { + let result: HTMLElement = renderFormattedText('\\*\\*bold\\*\\*'); + assert.strictEqual(result.children.length, 0); + assert.strictEqual(result.innerHTML, '**bold**'); + }); +}); diff --git a/src/vs/base/test/browser/htmlContent.test.ts b/src/vs/base/test/browser/htmlContent.test.ts index d5dec0729d9f1..c52e73056b9b1 100644 --- a/src/vs/base/test/browser/htmlContent.test.ts +++ b/src/vs/base/test/browser/htmlContent.test.ts @@ -2,105 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import * as marked from 'vs/base/common/marked/marked'; -import { renderMarkdown, renderText, renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; suite('HtmlContent', () => { - const store = new DisposableStore(); - - setup(() => { - store.clear(); - }); - - teardown(() => { - store.clear(); - }); - - test('render simple element', () => { - let result: HTMLElement = renderText('testing'); - - assert.strictEqual(result.nodeType, document.ELEMENT_NODE); - assert.strictEqual(result.textContent, 'testing'); - assert.strictEqual(result.tagName, 'DIV'); - }); - - test('render element with class', () => { - let result: HTMLElement = renderText('testing', { - className: 'testClass' - }); - assert.strictEqual(result.nodeType, document.ELEMENT_NODE); - assert.strictEqual(result.className, 'testClass'); - }); - - test('simple formatting', () => { - let result: HTMLElement = renderFormattedText('**bold**'); - assert.strictEqual(result.children.length, 1); - assert.strictEqual(result.firstChild!.textContent, 'bold'); - assert.strictEqual((result.firstChild).tagName, 'B'); - assert.strictEqual(result.innerHTML, 'bold'); - - result = renderFormattedText('__italics__'); - assert.strictEqual(result.innerHTML, 'italics'); - - result = renderFormattedText('this string has **bold** and __italics__'); - assert.strictEqual(result.innerHTML, 'this string has bold and italics'); - }); - - test('no formatting', () => { - let result: HTMLElement = renderFormattedText('this is just a string'); - assert.strictEqual(result.innerHTML, 'this is just a string'); - }); - - test('preserve newlines', () => { - let result: HTMLElement = renderFormattedText('line one\nline two'); - assert.strictEqual(result.innerHTML, 'line one
line two'); - }); - - test('action', () => { - let callbackCalled = false; - let result: HTMLElement = renderFormattedText('[[action]]', { - actionHandler: { - callback(content) { - assert.strictEqual(content, '0'); - callbackCalled = true; - }, - disposeables: store - } - }); - assert.strictEqual(result.innerHTML, 'action'); - - let event: MouseEvent = document.createEvent('MouseEvent'); - event.initEvent('click', true, true); - result.firstChild!.dispatchEvent(event); - assert.strictEqual(callbackCalled, true); - }); - - test('fancy action', () => { - let callbackCalled = false; - let result: HTMLElement = renderFormattedText('__**[[action]]**__', { - actionHandler: { - callback(content) { - assert.strictEqual(content, '0'); - callbackCalled = true; - }, - disposeables: store - } - }); - assert.strictEqual(result.innerHTML, 'action'); - - let event: MouseEvent = document.createEvent('MouseEvent'); - event.initEvent('click', true, true); - result.firstChild!.firstChild!.firstChild!.dispatchEvent(event); - assert.strictEqual(callbackCalled, true); - }); - - test('escaped formatting', () => { - let result: HTMLElement = renderFormattedText('\\*\\*bold\\*\\*'); - assert.strictEqual(result.children.length, 0); - assert.strictEqual(result.innerHTML, '**bold**'); - }); test('image rendering conforms to default', () => { const markdown = { value: `![image](someimageurl 'caption')` }; const result: HTMLElement = renderMarkdown(markdown); @@ -111,6 +18,7 @@ suite('HtmlContent', () => { }).trim(); assert.strictEqual(result.innerHTML, imageFromMarked); }); + test('image rendering conforms to default without title', () => { const markdown = { value: `![image](someimageurl)` }; const result: HTMLElement = renderMarkdown(markdown); @@ -121,14 +29,17 @@ suite('HtmlContent', () => { }).trim(); assert.strictEqual(result.innerHTML, imageFromMarked); }); + test('image width from title params', () => { let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` }); assert.strictEqual(result.innerHTML, `

image

`); }); + test('image height from title params', () => { let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` }); assert.strictEqual(result.innerHTML, `

image

`); }); + test('image width and height from title params', () => { let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` }); assert.strictEqual(result.innerHTML, `

image

`); diff --git a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts index 22e860f8eb0f7..1981153c0c858 100644 --- a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts +++ b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts @@ -7,7 +7,7 @@ import 'vs/css!./accessibilityHelp'; import * as browser from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; +import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { Widget } from 'vs/base/browser/ui/widget'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts index 9d6b5c95ba262..a45ab6500316c 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts @@ -7,7 +7,7 @@ import 'vs/css!./accessibility'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { renderFormattedText } from 'vs/base/browser/htmlContentRenderer'; +import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { Widget } from 'vs/base/browser/ui/widget'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; From ae85999e600984b521468465b1501f2408962af2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 13 Aug 2019 11:59:04 -0700 Subject: [PATCH 150/613] Split MarkdownRenderOptions from FormattedTextRenderOptions --- src/vs/base/browser/formattedTextRenderer.ts | 31 +++++++++++++++--- src/vs/base/browser/htmlContentRenderer.ts | 32 ++++--------------- src/vs/base/browser/ui/inputbox/inputBox.ts | 4 +-- .../contrib/markdown/markdownRenderer.ts | 4 +-- 4 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/vs/base/browser/formattedTextRenderer.ts b/src/vs/base/browser/formattedTextRenderer.ts index 6d33caf8acc61..0e9b0c9571cd2 100644 --- a/src/vs/base/browser/formattedTextRenderer.ts +++ b/src/vs/base/browser/formattedTextRenderer.ts @@ -4,20 +4,41 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { createElement, IContentActionHandler, RenderOptions } from 'vs/base/browser/htmlContentRenderer'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { DisposableStore } from 'vs/base/common/lifecycle'; -export function renderText(text: string, options: RenderOptions = {}): HTMLElement { +export interface IContentActionHandler { + callback: (content: string, event?: IMouseEvent) => void; + readonly disposeables: DisposableStore; +} + +export interface FormattedTextRenderOptions { + readonly className?: string; + readonly inline?: boolean; + readonly actionHandler?: IContentActionHandler; +} + +export function renderText(text: string, options: FormattedTextRenderOptions = {}): HTMLElement { const element = createElement(options); element.textContent = text; return element; } -export function renderFormattedText(formattedText: string, options: RenderOptions = {}): HTMLElement { +export function renderFormattedText(formattedText: string, options: FormattedTextRenderOptions = {}): HTMLElement { const element = createElement(options); _renderFormattedText(element, parseFormattedText(formattedText), options.actionHandler); return element; } +export function createElement(options: FormattedTextRenderOptions): HTMLElement { + const tagName = options.inline ? 'span' : 'div'; + const element = document.createElement(tagName); + if (options.className) { + element.className = options.className; + } + return element; +} + class StringStream { private source: string; private index: number; @@ -64,7 +85,7 @@ interface IFormatParseTree { children?: IFormatParseTree[]; } -export function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionHandler?: IContentActionHandler) { +function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionHandler?: IContentActionHandler) { let child: Node | undefined; if (treeNode.type === FormatType.Text) { @@ -98,7 +119,7 @@ export function _renderFormattedText(element: Node, treeNode: IFormatParseTree, } } -export function parseFormattedText(content: string): IFormatParseTree { +function parseFormattedText(content: string): IFormatParseTree { const root: IFormatParseTree = { type: FormatType.Root, diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/htmlContentRenderer.ts index 80f956bf1381a..7fd9f63b75a4d 100644 --- a/src/vs/base/browser/htmlContentRenderer.ts +++ b/src/vs/base/browser/htmlContentRenderer.ts @@ -4,43 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; +import { createElement, FormattedTextRenderOptions } from 'vs/base/browser/formattedTextRenderer'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IMarkdownString, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent'; import { defaultGenerator } from 'vs/base/common/idGenerator'; -import { escape } from 'vs/base/common/strings'; -import { removeMarkdownEscapes, IMarkdownString, parseHrefAndDimensions } from 'vs/base/common/htmlContent'; import * as marked from 'vs/base/common/marked/marked'; -import { IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { DisposableStore } from 'vs/base/common/lifecycle'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { URI } from 'vs/base/common/uri'; import { parse } from 'vs/base/common/marshalling'; import { cloneAndChange } from 'vs/base/common/objects'; +import { escape } from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; -export interface IContentActionHandler { - callback: (content: string, event?: IMouseEvent) => void; - readonly disposeables: DisposableStore; -} - -export interface RenderOptions { - className?: string; - inline?: boolean; - actionHandler?: IContentActionHandler; +export interface MarkdownRenderOptions extends FormattedTextRenderOptions { codeBlockRenderer?: (modeId: string, value: string) => Promise; codeBlockRenderCallback?: () => void; } -export function createElement(options: RenderOptions): HTMLElement { - const tagName = options.inline ? 'span' : 'div'; - const element = document.createElement(tagName); - if (options.className) { - element.className = options.className; - } - return element; -} - /** * Create html nodes for the given content element. */ -export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): HTMLElement { +export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRenderOptions = {}): HTMLElement { const element = createElement(options); const _uriMassage = function (part: string): string { diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index fb8658883c30c..454ce9c87af4c 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -8,7 +8,7 @@ import 'vs/css!./inputBox'; import * as nls from 'vs/nls'; import * as Bal from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; -import { RenderOptions } from 'vs/base/browser/htmlContentRenderer'; +import { MarkdownRenderOptions } from 'vs/base/browser/htmlContentRenderer'; import { renderFormattedText, renderText } from 'vs/base/browser/formattedTextRenderer'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IAction } from 'vs/base/common/actions'; @@ -439,7 +439,7 @@ export class InputBox extends Widget { div = dom.append(container, $('.monaco-inputbox-container')); layout(); - const renderOptions: RenderOptions = { + const renderOptions: MarkdownRenderOptions = { inline: true, className: 'monaco-inputbox-message' }; diff --git a/src/vs/editor/contrib/markdown/markdownRenderer.ts b/src/vs/editor/contrib/markdown/markdownRenderer.ts index 5825a919bf5a8..34a713d0b1750 100644 --- a/src/vs/editor/contrib/markdown/markdownRenderer.ts +++ b/src/vs/editor/contrib/markdown/markdownRenderer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { renderMarkdown, RenderOptions } from 'vs/base/browser/htmlContentRenderer'; +import { renderMarkdown, MarkdownRenderOptions } from 'vs/base/browser/htmlContentRenderer'; import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener'; import { IModeService } from 'vs/editor/common/services/modeService'; import { URI } from 'vs/base/common/uri'; @@ -33,7 +33,7 @@ export class MarkdownRenderer extends Disposable { super(); } - private getOptions(disposeables: DisposableStore): RenderOptions { + private getOptions(disposeables: DisposableStore): MarkdownRenderOptions { return { codeBlockRenderer: (languageAlias, value) => { // In markdown, From 12792640f5976311352ef701579a77325b79aa03 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 13 Aug 2019 12:02:32 -0700 Subject: [PATCH 151/613] Rename htmlContentRenderer -> markdownRenderer --- .../browser/{htmlContentRenderer.ts => markdownRenderer.ts} | 0 src/vs/base/browser/ui/inputbox/inputBox.ts | 2 +- src/vs/base/browser/ui/selectBox/selectBoxCustom.ts | 2 +- .../browser/{htmlContent.test.ts => markdownRenderer.test.ts} | 4 ++-- src/vs/editor/contrib/markdown/markdownRenderer.ts | 2 +- .../workbench/contrib/comments/browser/commentsTreeViewer.ts | 2 +- src/vs/workbench/contrib/preferences/browser/settingsTree.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename src/vs/base/browser/{htmlContentRenderer.ts => markdownRenderer.ts} (100%) rename src/vs/base/test/browser/{htmlContent.test.ts => markdownRenderer.test.ts} (95%) diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/markdownRenderer.ts similarity index 100% rename from src/vs/base/browser/htmlContentRenderer.ts rename to src/vs/base/browser/markdownRenderer.ts diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 454ce9c87af4c..7af2a58f46d2b 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -8,7 +8,7 @@ import 'vs/css!./inputBox'; import * as nls from 'vs/nls'; import * as Bal from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; -import { MarkdownRenderOptions } from 'vs/base/browser/htmlContentRenderer'; +import { MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer'; import { renderFormattedText, renderText } from 'vs/base/browser/formattedTextRenderer'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IAction } from 'vs/base/common/actions'; diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 134c64bbef43c..d144eb6305038 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -18,7 +18,7 @@ import { domEvent } from 'vs/base/browser/event'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox'; import { isMacintosh } from 'vs/base/common/platform'; -import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; +import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; const $ = dom.$; diff --git a/src/vs/base/test/browser/htmlContent.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts similarity index 95% rename from src/vs/base/test/browser/htmlContent.test.ts rename to src/vs/base/test/browser/markdownRenderer.test.ts index c52e73056b9b1..525eb86e741b8 100644 --- a/src/vs/base/test/browser/htmlContent.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -5,9 +5,9 @@ import * as assert from 'assert'; import * as marked from 'vs/base/common/marked/marked'; -import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; +import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; -suite('HtmlContent', () => { +suite('MarkdownRenderer', () => { test('image rendering conforms to default', () => { const markdown = { value: `![image](someimageurl 'caption')` }; const result: HTMLElement = renderMarkdown(markdown); diff --git a/src/vs/editor/contrib/markdown/markdownRenderer.ts b/src/vs/editor/contrib/markdown/markdownRenderer.ts index 34a713d0b1750..81f0b2ccc8714 100644 --- a/src/vs/editor/contrib/markdown/markdownRenderer.ts +++ b/src/vs/editor/contrib/markdown/markdownRenderer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { renderMarkdown, MarkdownRenderOptions } from 'vs/base/browser/htmlContentRenderer'; +import { renderMarkdown, MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer'; import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener'; import { IModeService } from 'vs/editor/common/services/modeService'; import { URI } from 'vs/base/common/uri'; diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 7ab435e96ca24..5baf982dd9383 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import * as nls from 'vs/nls'; -import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; +import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 7106918e292a0..82465a2262467 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; +import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; From 0921202c2cb04a1b3755ab347ee0a5e509b1e4e3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 14 Aug 2019 07:59:53 +0200 Subject: [PATCH 152/613] web - do not loose state on unload --- src/vs/platform/storage/browser/storageService.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 392ca58f2b88b..a11505e32acde 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -144,8 +144,10 @@ export class BrowserStorageService extends Disposable implements IStorageService // Signal as event so that clients can still store data this._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); - // Close DBs - this.globalStorage.close(); - this.workspaceStorage.close(); + // We explicitly do not close our DBs because writing data onBeforeUnload() + // can result in unexpected results. Namely, it seems that - even though this + // operation is async - sometimes it is being triggered on unload and + // succeeds. Often though, the DBs turn out to be empty because the write + // never had a chance to complete. } } From ba69d8dcccaebbfaa94906503728eb7c3e45b9c1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 14 Aug 2019 08:33:50 +0200 Subject: [PATCH 153/613] open exter - move into opener service --- .../editor/browser/services/openerService.ts | 9 ++++- src/vs/platform/opener/common/opener.ts | 14 ++++++- .../workbench/api/browser/mainThreadWindow.ts | 7 ++-- .../files/browser/editors/binaryFileEditor.ts | 14 ++----- .../opener/electron-browser/openerService.ts | 40 +++++++++++++++++++ src/vs/workbench/workbench.common.main.ts | 3 -- src/vs/workbench/workbench.desktop.main.ts | 1 + src/vs/workbench/workbench.web.main.ts | 3 ++ 8 files changed, 70 insertions(+), 21 deletions(-) create mode 100644 src/vs/workbench/services/opener/electron-browser/openerService.ts diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index c175034f9699f..608d6e3b5d9c2 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -55,8 +55,7 @@ export class OpenerService implements IOpenerService { if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https) || equalsIgnoreCase(scheme, Schemas.mailto)) { // open http or default mail application - dom.windowOpenNoOpener(encodeURI(resource.toString(true))); - return Promise.resolve(true); + return this.openExternal(resource); } else if (equalsIgnoreCase(scheme, Schemas.command)) { // run command or bail out if command isn't known @@ -100,4 +99,10 @@ export class OpenerService implements IOpenerService { ).then(() => true); } } + + openExternal(resource: URI): Promise { + dom.windowOpenNoOpener(encodeURI(resource.toString(true))); + + return Promise.resolve(true); + } } diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 8c0f9fee5df42..c8336fc712cf7 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -9,7 +9,6 @@ import { IDisposable } from 'vs/base/common/lifecycle'; export const IOpenerService = createDecorator('openerService'); - export interface IOpener { open(resource: URI, options?: { openToSide?: boolean }): Promise; } @@ -18,6 +17,9 @@ export interface IOpenerService { _serviceBrand: any; + /** + * Register a participant that can handle the open() call. + */ registerOpener(opener: IOpener): IDisposable; /** @@ -27,10 +29,18 @@ export interface IOpenerService { * @return A promise that resolves when the opening is done. */ open(resource: URI, options?: { openToSide?: boolean }): Promise; + + /** + * Opens a URL externally. + * + * @param url A resource to open externally. + */ + openExternal(resource: URI): Promise; } export const NullOpenerService: IOpenerService = Object.freeze({ _serviceBrand: undefined, registerOpener() { return { dispose() { } }; }, - open() { return Promise.resolve(false); } + open() { return Promise.resolve(false); }, + openExternal() { return Promise.resolve(false); } }); diff --git a/src/vs/workbench/api/browser/mainThreadWindow.ts b/src/vs/workbench/api/browser/mainThreadWindow.ts index 410bb4ec66d30..acbb5c6f30688 100644 --- a/src/vs/workbench/api/browser/mainThreadWindow.ts +++ b/src/vs/workbench/api/browser/mainThreadWindow.ts @@ -6,12 +6,13 @@ import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ExtHostContext, ExtHostWindowShape, IExtHostContext, MainContext, MainThreadWindowShape, IOpenUriOptions } from '../common/extHost.protocol'; import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { extractLocalHostUriMetaDataForPortMapping } from 'vs/workbench/contrib/webview/common/portMapping'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; @extHostNamedCustomer(MainContext.MainThreadWindow) export class MainThreadWindow implements MainThreadWindowShape { @@ -23,7 +24,7 @@ export class MainThreadWindow implements MainThreadWindowShape { constructor( extHostContext: IExtHostContext, @IWindowService private readonly windowService: IWindowService, - @IWindowsService private readonly windowsService: IWindowsService, + @IOpenerService private readonly openerService: IOpenerService, @ITunnelService private readonly tunnelService: ITunnelService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService ) { @@ -58,7 +59,7 @@ export class MainThreadWindow implements MainThreadWindowShape { } } - return this.windowsService.openExternal(encodeURI(uri.toString(true))); + return this.openerService.openExternal(uri); } private getOrCreateTunnel(remotePort: number): Promise | undefined { diff --git a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts index 580a2990ac4f9..d6ce1130e866e 100644 --- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts @@ -7,16 +7,15 @@ import * as nls from 'vs/nls'; import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; -import { URI } from 'vs/base/common/uri'; import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; /** * An implementation of editor for binary files like images. @@ -28,7 +27,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, - @IWindowsService private readonly windowsService: IWindowsService, + @IOpenerService private readonly openerService: IOpenerService, @IEditorService private readonly editorService: IEditorService, @IStorageService storageService: IStorageService, @IFileService fileService: IFileService, @@ -39,7 +38,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { BinaryFileEditor.ID, { openInternal: (input, options) => this.openInternal(input, options), - openExternal: resource => this.openExternal(resource) + openExternal: resource => this.openerService.openExternal(resource) }, telemetryService, themeService, @@ -58,13 +57,6 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { } } - private async openExternal(resource: URI): Promise { - const didOpen = await this.windowsService.openExternal(resource.toString()); - if (!didOpen) { - return this.windowsService.showItemInFolder(resource); - } - } - getTitle(): string | null { return this.input ? this.input.getName() : nls.localize('binaryFileEditor', "Binary File Viewer"); } diff --git a/src/vs/workbench/services/opener/electron-browser/openerService.ts b/src/vs/workbench/services/opener/electron-browser/openerService.ts new file mode 100644 index 0000000000000..3085bd78a1402 --- /dev/null +++ b/src/vs/workbench/services/opener/electron-browser/openerService.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { OpenerService as BaseOpenerService } from 'vs/editor/browser/services/openerService'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; + +export class OpenerService extends BaseOpenerService { + + _serviceBrand!: ServiceIdentifier; + + constructor( + @ICodeEditorService codeEditorService: ICodeEditorService, + @ICommandService commandService: ICommandService, + @IWindowsService private readonly windowsService: IWindowsService + ) { + super(codeEditorService, commandService); + } + + async openExternal(resource: URI): Promise { + const success = this.windowsService.openExternal(encodeURI(resource.toString(true))); + if (!success && resource.scheme === Schemas.file) { + await this.windowsService.showItemInFolder(resource); + + return true; + } + + return success; + } +} + +registerSingleton(IOpenerService, OpenerService, true); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 3558c2ffef3be..72765299a3129 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -80,8 +80,6 @@ import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IListService, ListService } from 'vs/platform/list/browser/listService'; -import { OpenerService } from 'vs/editor/browser/services/openerService'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl'; import { MarkerDecorationsService } from 'vs/editor/common/services/markerDecorationsServiceImpl'; @@ -102,7 +100,6 @@ import { DownloadService } from 'vs/platform/download/common/downloadService'; registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true); registerSingleton(IContextViewService, ContextViewService, true); registerSingleton(IListService, ListService, true); -registerSingleton(IOpenerService, OpenerService, true); registerSingleton(IEditorWorkerService, EditorWorkerServiceImpl); registerSingleton(IMarkerDecorationsService, MarkerDecorationsService); registerSingleton(IMarkerService, MarkerService, true); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index c2d6ea7a5bc7d..4d2996a0ed657 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -48,6 +48,7 @@ import 'vs/workbench/services/extensionManagement/node/extensionManagementServic import 'vs/workbench/services/accessibility/node/accessibilityService'; import 'vs/workbench/services/remote/node/tunnelService'; import 'vs/workbench/services/backup/node/backupFileService'; +import 'vs/workbench/services/opener/electron-browser/openerService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 1a41fc77ac4db..169b3c07ef6cf 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -54,6 +54,8 @@ import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuS import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; import { ExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; +import { OpenerService } from 'vs/editor/browser/services/openerService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; registerSingleton(IRequestService, RequestService, true); registerSingleton(IExtensionManagementService, ExtensionManagementService); @@ -63,6 +65,7 @@ registerSingleton(IClipboardService, BrowserClipboardService, true); registerSingleton(IAccessibilityService, BrowserAccessibilityService, true); registerSingleton(ILifecycleService, BrowserLifecycleService); registerSingleton(IContextMenuService, ContextMenuService); +registerSingleton(IOpenerService, OpenerService, true); //#endregion From 1a5ac941e78b860633c1b7c1d922419878379bc8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 14 Aug 2019 10:15:40 +0200 Subject: [PATCH 154/613] add extension kind web --- src/vs/platform/extensions/common/extensions.ts | 4 ++-- .../services/extensions/browser/extensionService.ts | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 93a52746e6bba..a043fdfcaa3fa 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -106,7 +106,7 @@ export interface IExtensionContributions { localizations?: ILocalization[]; } -export type ExtensionKind = 'ui' | 'workspace'; +export type ExtensionKind = 'ui' | 'workspace' | 'web'; export function isIExtensionIdentifier(thing: any): thing is IExtensionIdentifier { return thing @@ -221,4 +221,4 @@ export interface IExtensionDescription extends IExtensionManifest { export function isLanguagePackExtension(manifest: IExtensionManifest): boolean { return manifest.contributes && manifest.contributes.localizations ? manifest.contributes.localizations.length > 0 : false; -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index a32024e35aca2..7d624f12bf81e 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -65,11 +65,14 @@ export class ExtensionService extends AbstractExtensionService implements IExten const remoteAgentConnection = this._remoteAgentService.getConnection()!; - const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, Promise.resolve([]), URI.parse('empty:value')); //todo@joh + const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => ext.extensionKind === 'web')); + const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => ext.extensionKind !== 'web')); + + const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.parse('empty:value')); //todo@joh const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); result.push(webHostProcessManager); - const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, this.getExtensions(), this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); + const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); result.push(remoteExtHostProcessManager); From 6883d5533b0dd150298d18953713f69e41678a74 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 14 Aug 2019 10:31:43 +0200 Subject: [PATCH 155/613] add action only in remote --- .../workbench/contrib/extensions/browser/extensionsViews.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index ab7e8ee8fb195..733aa5fa5b1a8 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -859,7 +859,7 @@ export class ServerExtensionsView extends ExtensionsListView { @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionManagementServerService extensionManagementServerService: IExtensionManagementServerService, @IProductService productService: IProductService, - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService ) { options.server = server; super(options, notificationService, keybindingService, contextMenuService, instantiationService, themeService, extensionService, extensionsWorkbenchService, editorService, tipsService, telemetryService, configurationService, contextService, experimentService, workbenchThemeService, extensionManagementServerService, productService, contextKeyService); @@ -875,7 +875,7 @@ export class ServerExtensionsView extends ExtensionsListView { } getActions(): IAction[] { - if (this.extensionManagementServerService.localExtensionManagementServer === this.server) { + if (this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === this.server) { const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, false)); installLocalExtensionsInRemoteAction.class = 'octicon octicon-cloud-download'; return [installLocalExtensionsInRemoteAction]; From fa8c6e6b77a206f8d65bc53a83b100e253ae9658 Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Wed, 14 Aug 2019 10:42:59 +0100 Subject: [PATCH 156/613] Improve #79047 fix after feedback --- src/vs/workbench/contrib/files/common/explorerService.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/files/common/explorerService.ts b/src/vs/workbench/contrib/files/common/explorerService.ts index 8196703202889..aff4390570c49 100644 --- a/src/vs/workbench/contrib/files/common/explorerService.ts +++ b/src/vs/workbench/contrib/files/common/explorerService.ts @@ -158,12 +158,10 @@ export class ExplorerService implements IExplorerService { // Stat needs to be resolved first and then revealed const options: IResolveFileOptions = { resolveTo: [resource], resolveMetadata: this.sortOrder === 'modified' }; const workspaceFolder = this.contextService.getWorkspaceFolder(resource); - const rootUri = workspaceFolder ? workspaceFolder.uri : this.roots[0].resource; - - // Do not waste time looking in a different scheme - if (resource.scheme !== rootUri.scheme) { + if (workspaceFolder === null) { return Promise.resolve(undefined); } + const rootUri = workspaceFolder.uri; const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop()!; From b2b0c86dda9fe74c6e76d56fb61fe545859726b2 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 14 Aug 2019 11:58:58 +0200 Subject: [PATCH 157/613] use `vscode-remote`-endpoint when importing scripts --- src/vs/workbench/api/worker/extHostExtensionService.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index 6f7f2b67febde..be34dbe2d6421 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -13,6 +13,8 @@ import * as vscode from 'vscode'; import { TernarySearchTree } from 'vs/base/common/map'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; +import { isWeb } from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; class ApiInstances { @@ -83,8 +85,14 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { }; try { + // todo@joh this is a copy of `dom.ts#asDomUri` + // build url of the things we resolve + const url = isWeb + ? URI.parse(window.location.href).with({ path: '/vscode-remote', query: JSON.stringify(URI.file(modulePath)) }).toString(true) + : modulePath; + activationTimesBuilder.codeLoadingStart(); - importScripts(modulePath); + importScripts(url); } finally { activationTimesBuilder.codeLoadingStop(); } From 158f859217d08b50830dcf451375d83790a48333 Mon Sep 17 00:00:00 2001 From: skprabhanjan Date: Wed, 14 Aug 2019 17:50:50 +0530 Subject: [PATCH 158/613] Added preserveCase in search side bar --- .../search/browser/media/searchview.css | 14 +++++++++ .../contrib/search/browser/searchView.ts | 12 +++++++- .../contrib/search/browser/searchWidget.ts | 29 ++++++++++++++++++- .../contrib/search/common/searchModel.ts | 15 ++++++++-- .../services/search/common/replace.ts | 22 ++++++++++++-- 5 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/media/searchview.css b/src/vs/workbench/contrib/search/browser/media/searchview.css index 3181a10d8d1ec..f530bce968ae0 100644 --- a/src/vs/workbench/contrib/search/browser/media/searchview.css +++ b/src/vs/workbench/contrib/search/browser/media/searchview.css @@ -60,6 +60,20 @@ display: inline-flex; } +.search-view .search-widget .replace-input { + position: relative; + display: flex; + display: -webkit-flex; + vertical-align: middle; + width: auto !important; +} + +.search-view .search-widget .replace-input > .controls { + position: absolute; + top: 3px; + right: 2px; +} + .search-view .search-widget .replace-container.disabled { display: none; } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 2daad95ca686e..e42ff0dd0e5e5 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -364,6 +364,7 @@ export class SearchView extends ViewletPanel { const searchHistory = history.search || this.viewletState['query.searchHistory'] || []; const replaceHistory = history.replace || this.viewletState['query.replaceHistory'] || []; const showReplace = typeof this.viewletState['view.showReplace'] === 'boolean' ? this.viewletState['view.showReplace'] : true; + const preserveCase = this.viewletState['query.preserveCase'] === true; this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, container, { value: contentPattern, @@ -372,7 +373,8 @@ export class SearchView extends ViewletPanel { isCaseSensitive: isCaseSensitive, isWholeWords: isWholeWords, searchHistory: searchHistory, - replaceHistory: replaceHistory + replaceHistory: replaceHistory, + preserveCase: preserveCase })); if (showReplace) { @@ -390,6 +392,12 @@ export class SearchView extends ViewletPanel { this.viewModel.replaceActive = state; this.refreshTree(); })); + + this._register(this.searchWidget.onPreserveCaseChange((state) => { + this.viewModel.preserveCase = state; + this.refreshTree(); + })); + this._register(this.searchWidget.onReplaceValueChanged((value) => { this.viewModel.replaceString = this.searchWidget.getReplaceValue(); this.delayedRefresh.trigger(() => this.refreshTree()); @@ -1641,6 +1649,7 @@ export class SearchView extends ViewletPanel { const patternExcludes = this.inputPatternExcludes.getValue().trim(); const patternIncludes = this.inputPatternIncludes.getValue().trim(); const useExcludesAndIgnoreFiles = this.inputPatternExcludes.useExcludesAndIgnoreFiles(); + const preserveCase = this.viewModel.preserveCase; this.viewletState['query.contentPattern'] = contentPattern; this.viewletState['query.regex'] = isRegex; @@ -1649,6 +1658,7 @@ export class SearchView extends ViewletPanel { this.viewletState['query.folderExclusions'] = patternExcludes; this.viewletState['query.folderIncludes'] = patternIncludes; this.viewletState['query.useExcludesAndIgnoreFiles'] = useExcludesAndIgnoreFiles; + this.viewletState['query.preserveCase'] = preserveCase; const isReplaceShown = this.searchAndReplaceWidget.isReplaceShown(); this.viewletState['view.showReplace'] = isReplaceShown; diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 6cfbb4d554209..26b29b280c182 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -33,6 +33,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; export interface ISearchWidgetOptions { value?: string; @@ -42,6 +43,7 @@ export interface ISearchWidgetOptions { isWholeWords?: boolean; searchHistory?: string[]; replaceHistory?: string[]; + preserveCase?: boolean; } class ReplaceAllAction extends Action { @@ -97,6 +99,7 @@ export class SearchWidget extends Widget { replaceInputFocusTracker: dom.IFocusTracker; private replaceInputBoxFocused: IContextKey; private _replaceHistoryDelayer: Delayer; + private _preserveCase: Checkbox; private ignoreGlobalFindBufferOnNextFocus = false; private previousGlobalFindBufferValue: string; @@ -113,6 +116,9 @@ export class SearchWidget extends Widget { private _onReplaceStateChange = this._register(new Emitter()); readonly onReplaceStateChange: Event = this._onReplaceStateChange.event; + private _onPreserveCaseChange = this._register(new Emitter()); + readonly onPreserveCaseChange: Event = this._onPreserveCaseChange.event; + private _onReplaceValueChanged = this._register(new Emitter()); readonly onReplaceValueChanged: Event = this._onReplaceValueChanged.event; @@ -333,13 +339,34 @@ export class SearchWidget extends Widget { private renderReplaceInput(parent: HTMLElement, options: ISearchWidgetOptions): void { this.replaceContainer = dom.append(parent, dom.$('.replace-container.disabled')); - const replaceBox = dom.append(this.replaceContainer, dom.$('.input-box')); + const replaceBox = dom.append(this.replaceContainer, dom.$('.replace-input')); + this.replaceInput = this._register(new ContextScopedHistoryInputBox(replaceBox, this.contextViewService, { ariaLabel: nls.localize('label.Replace', 'Replace: Type replace term and press Enter to preview or Escape to cancel'), placeholder: nls.localize('search.replace.placeHolder', "Replace"), history: options.replaceHistory || [], flexibleHeight: true }, this.contextKeyService)); + + this._preserveCase = this._register(new Checkbox({ + actionClassName: 'monaco-preserve-case', + title: nls.localize('label.preserveCaseCheckbox', "Preserve Case"), + isChecked: !!options.preserveCase, + })); + + this._register(this._preserveCase.onChange(viaKeyboard => { + if (!viaKeyboard) { + this.replaceInput.focus(); + this._onPreserveCaseChange.fire(this._preserveCase.checked); + } + })); + + let controls = document.createElement('div'); + controls.className = 'controls'; + controls.style.display = 'block'; + controls.appendChild(this._preserveCase.domNode); + replaceBox.appendChild(controls); + this._register(attachInputBoxStyler(this.replaceInput, this.themeService)); this.onkeydown(this.replaceInput.inputElement, (keyboardEvent) => this.onReplaceInputKeyDown(keyboardEvent)); this.replaceInput.value = options.replaceValue || ''; diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index 1c93ff5221ca2..1962709126f90 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -103,17 +103,17 @@ export class Match { } const fullMatchText = this.fullMatchText(); - let replaceString = searchModel.replacePattern.getReplaceString(fullMatchText); + let replaceString = searchModel.replacePattern.getReplaceString(fullMatchText, searchModel.preserveCase); // If match string is not matching then regex pattern has a lookahead expression if (replaceString === null) { const fullMatchTextWithTrailingContent = this.fullMatchText(true); - replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithTrailingContent); + replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithTrailingContent, searchModel.preserveCase); // Search/find normalize line endings - check whether \r prevents regex from matching if (replaceString === null) { const fullMatchTextWithoutCR = fullMatchTextWithTrailingContent.replace(/\r\n/g, '\n'); - replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithoutCR); + replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithoutCR, searchModel.preserveCase); } } @@ -895,6 +895,7 @@ export class SearchModel extends Disposable { private _replaceActive: boolean = false; private _replaceString: string | null = null; private _replacePattern: ReplacePattern | null = null; + private _preserveCase: boolean = false; private readonly _onReplaceTermChanged: Emitter = this._register(new Emitter()); readonly onReplaceTermChanged: Event = this._onReplaceTermChanged.event; @@ -926,6 +927,14 @@ export class SearchModel extends Disposable { return this._replaceString || ''; } + set preserveCase(value: boolean) { + this._preserveCase = value; + } + + get preserveCase(): boolean { + return this._preserveCase; + } + set replaceString(replaceString: string) { this._replaceString = replaceString; if (this._searchQuery) { diff --git a/src/vs/workbench/services/search/common/replace.ts b/src/vs/workbench/services/search/common/replace.ts index a6caab8223e4d..3f58ea89fe0e1 100644 --- a/src/vs/workbench/services/search/common/replace.ts +++ b/src/vs/workbench/services/search/common/replace.ts @@ -54,7 +54,7 @@ export class ReplacePattern { * Returns the replace string for the first match in the given text. * If text has no matches then returns null. */ - getReplaceString(text: string): string | null { + getReplaceString(text: string, preserveCase?: boolean): string | null { this._regExp.lastIndex = 0; let match = this._regExp.exec(text); if (match) { @@ -65,12 +65,30 @@ export class ReplacePattern { let replaceString = text.replace(this._regExp, this.pattern); return replaceString.substr(match.index, match[0].length - (text.length - replaceString.length)); } - return this.pattern; + return this.buildReplaceString(match, preserveCase); } return null; } + public buildReplaceString(matches: string[] | null, preserveCase?: boolean): string { + + if (preserveCase && matches && (matches[0] !== '')) { + if (matches[0].toUpperCase() === matches[0]) { + return this._replacePattern.toUpperCase(); + } else if (matches[0].toLowerCase() === matches[0]) { + return this._replacePattern.toLowerCase(); + } else if (strings.containsUppercaseCharacter(matches[0][0])) { + return this._replacePattern[0].toUpperCase() + this._replacePattern.substr(1); + } else { + // we don't understand its pattern yet. + return this._replacePattern; + } + } else { + return this._replacePattern; + } + } + /** * \n => LF * \t => TAB From 9d4ba37d4ca38ef3bad829db03256bfe7b0a32fc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 14 Aug 2019 15:15:57 +0200 Subject: [PATCH 159/613] test build schedule --- build/azure-pipelines/product-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index f38f431182644..f559126ecea70 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -143,8 +143,8 @@ trigger: none pr: none schedules: -- cron: "0 5 * * Mon-Fri" - displayName: Mon-Fri at 7:00 +- cron: "20 13 * * Mon-Fri" + displayName: Mon-Fri at 15:20 branches: include: - master From 5875c55136372aab0dcb5848cc2ff1cffb295e3d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 14 Aug 2019 15:21:29 +0200 Subject: [PATCH 160/613] Revert "test build schedule" This reverts commit 9d4ba37d4ca38ef3bad829db03256bfe7b0a32fc. --- build/azure-pipelines/product-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index f559126ecea70..f38f431182644 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -143,8 +143,8 @@ trigger: none pr: none schedules: -- cron: "20 13 * * Mon-Fri" - displayName: Mon-Fri at 15:20 +- cron: "0 5 * * Mon-Fri" + displayName: Mon-Fri at 7:00 branches: include: - master From 361dbb9fafada498101aef1c4786d1184634489d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 14 Aug 2019 12:37:36 +0200 Subject: [PATCH 161/613] better check for worker/web extension --- .../services/extensions/browser/extensionService.ts | 7 +++++-- .../workbench/services/extensions/common/extensionsUtil.ts | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 7d624f12bf81e..80e9fe37c3fda 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -20,6 +20,8 @@ import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEn import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { WebWorkerExtensionHostStarter } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter'; import { URI } from 'vs/base/common/uri'; +import { isWebExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -34,6 +36,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IFileService fileService: IFileService, @IProductService productService: IProductService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, + @IConfigurationService private readonly _configService: IConfigurationService, ) { super( instantiationService, @@ -65,8 +68,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten const remoteAgentConnection = this._remoteAgentService.getConnection()!; - const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => ext.extensionKind === 'web')); - const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => ext.extensionKind !== 'web')); + const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => isWebExtension(ext, this._configService))); + const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !isWebExtension(ext, this._configService))); const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.parse('empty:value')); //todo@joh const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts index e03ddb10239b0..a1496708db6d1 100644 --- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -10,6 +10,11 @@ import { getGalleryExtensionId, areSameExtensions } from 'vs/platform/extensionM import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IProductService } from 'vs/platform/product/common/product'; +export function isWebExtension(manifest: IExtensionManifest, configurationService: IConfigurationService): boolean { + const extensionKind = getExtensionKind(manifest, configurationService); + return extensionKind === 'web'; +} + export function isUIExtension(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { const uiContributions = ExtensionsRegistry.getExtensionPoints().filter(e => e.defaultExtensionKind !== 'workspace').map(e => e.name); const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name); From 8f3b7dec4a02dcc4a2d45d96669e01cdb8a21d6c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 14 Aug 2019 16:02:01 +0200 Subject: [PATCH 162/613] debt - IExtensionDescription#main should be relative like all other file references --- .../workbench/api/common/extHostExtensionService.ts | 8 ++++---- src/vs/workbench/api/node/extHostExtensionService.ts | 11 ++++++++--- .../workbench/api/worker/extHostExtensionService.ts | 12 ++++++------ .../services/extensions/node/extensionPoints.ts | 5 ----- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 99553033cd227..8c903a6027c03 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; -import { originalFSPath } from 'vs/base/common/resources'; +import { originalFSPath, joinPath } from 'vs/base/common/resources'; import { Barrier } from 'vs/base/common/async'; import { dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; @@ -332,14 +332,14 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup); return Promise.all([ - this._loadCommonJSModule(extensionDescription.main, activationTimesBuilder), + this._loadCommonJSModule(joinPath(extensionDescription.extensionLocation, extensionDescription.main), activationTimesBuilder), this._loadExtensionContext(extensionDescription) ]).then(values => { return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder); }); } - protected abstract _loadCommonJSModule(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise; + protected abstract _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise; private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { @@ -536,7 +536,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio let testRunner: ITestRunner | INewTestRunner | undefined; let requireError: Error | undefined; try { - testRunner = await this._loadCommonJSModule(extensionTestsPath, new ExtensionActivationTimesBuilder(false)); + testRunner = await this._loadCommonJSModule(URI.file(extensionTestsPath), new ExtensionActivationTimesBuilder(false)); } catch (error) { requireError = error; } diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 8f1f5d6ee75f0..5b03a318564da 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -11,6 +11,8 @@ import { connectProxyResolver } from 'vs/workbench/services/extensions/node/prox import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadService'; import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer'; +import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; export class ExtHostExtensionService extends AbstractExtHostExtensionService { @@ -55,12 +57,15 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { }; } - protected _loadCommonJSModule(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + protected _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + if (module.scheme !== Schemas.file) { + throw new Error(`Cannot load URI: '${module}', must be of file-scheme`); + } let r: T | null = null; activationTimesBuilder.codeLoadingStart(); - this._logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`); + this._logService.info(`ExtensionService#loadCommonJSModule ${module.toString(true)}`); try { - r = require.__$__nodeRequire(modulePath); + r = require.__$__nodeRequire(module.fsPath); } catch (e) { return Promise.reject(e); } finally { diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index be34dbe2d6421..19b44f7dc182c 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -54,11 +54,11 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { this._apiInstances = new ApiInstances(apiFactory, extensionPath, this._registry, configProvider); } - protected _loadCommonJSModule(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { + protected _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { // make sure modulePath ends with `.js` const suffix = '.js'; - modulePath = endsWith(modulePath, suffix) ? modulePath : modulePath + suffix; + let modulePath = endsWith(module.fsPath, suffix) ? module.fsPath : module.fsPath + suffix; interface FakeCommonJSSelf { module?: object; @@ -71,9 +71,9 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { // FAKE commonjs world that only collects exports const patchSelf: FakeCommonJSSelf = self; - const module = { exports: {} }; - patchSelf.module = module; - patchSelf.exports = module.exports; + const exports = Object.create(null); + patchSelf.module = { exports }; + patchSelf.exports = exports; patchSelf.window = self; // <- that's improper but might help extensions that aren't authored correctly // FAKE require function that only works for the vscode-module @@ -97,7 +97,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { activationTimesBuilder.codeLoadingStop(); } - return Promise.resolve(module.exports as T); + return Promise.resolve(exports); } async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise { diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index f20f824a2e2c9..8abe6aa7f52c9 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -309,11 +309,6 @@ class ExtensionManifestValidator extends ExtensionManifestHandler { extensionDescription.id = `${extensionDescription.publisher}.${extensionDescription.name}`; extensionDescription.identifier = new ExtensionIdentifier(extensionDescription.id); - // main := absolutePath(`main`) - if (extensionDescription.main) { - extensionDescription.main = path.join(this._absoluteFolderPath, extensionDescription.main); - } - extensionDescription.extensionLocation = URI.file(this._absoluteFolderPath); return extensionDescription; From 48b3d1c0619b33efb442c70af5d323af10660028 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 14 Aug 2019 16:29:28 +0200 Subject: [PATCH 163/613] move windowsIPC => common --- src/vs/code/electron-main/app.ts | 2 +- .../platform/windows/{node => common}/windowsIpc.ts | 0 .../windows/electron-browser/windowsService.ts | 11 ++++++----- 3 files changed, 7 insertions(+), 6 deletions(-) rename src/vs/platform/windows/{node => common}/windowsIpc.ts (100%) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index d71ddf83ea18a..15e9c438ddb26 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -7,7 +7,7 @@ import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, p import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform'; import { WindowsManager } from 'vs/code/electron-main/windows'; import { IWindowsService, OpenContext, ActiveWindowManager, IURIToOpen } from 'vs/platform/windows/common/windows'; -import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc'; +import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc'; import { WindowsService } from 'vs/platform/windows/electron-main/windowsService'; import { ILifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { getShellEnvironment } from 'vs/code/node/shellEnv'; diff --git a/src/vs/platform/windows/node/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts similarity index 100% rename from src/vs/platform/windows/node/windowsIpc.ts rename to src/vs/platform/windows/common/windowsIpc.ts diff --git a/src/vs/platform/windows/electron-browser/windowsService.ts b/src/vs/platform/windows/electron-browser/windowsService.ts index 228dfd98ba259..57bd96143af12 100644 --- a/src/vs/platform/windows/electron-browser/windowsService.ts +++ b/src/vs/platform/windows/electron-browser/windowsService.ts @@ -13,17 +13,14 @@ import { URI } from 'vs/base/common/uri'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { IProcessEnvironment } from 'vs/base/common/platform'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; export class WindowsService implements IWindowsService { - _serviceBrand: any; + _serviceBrand!: ServiceIdentifier; private channel: IChannel; - constructor(@IMainProcessService mainProcessService: IMainProcessService) { - this.channel = mainProcessService.getChannel('windows'); - } - get onWindowOpen(): Event { return this.channel.listen('onWindowOpen'); } get onWindowFocus(): Event { return this.channel.listen('onWindowFocus'); } get onWindowBlur(): Event { return this.channel.listen('onWindowBlur'); } @@ -31,6 +28,10 @@ export class WindowsService implements IWindowsService { get onWindowUnmaximize(): Event { return this.channel.listen('onWindowUnmaximize'); } get onRecentlyOpenedChange(): Event { return this.channel.listen('onRecentlyOpenedChange'); } + constructor(@IMainProcessService mainProcessService: IMainProcessService) { + this.channel = mainProcessService.getChannel('windows'); + } + pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise { return this.channel.call('pickFileFolderAndOpen', options); } From ec62819ffa298f8b57e6db285e10404a3e6c89d4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 14 Aug 2019 16:29:47 +0200 Subject: [PATCH 164/613] very basic support to load multiple files --- .../api/worker/extHostExtensionService.ts | 66 +++++++++++++------ 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index 19b44f7dc182c..0b49644e7ccdf 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -6,15 +6,16 @@ import { createApiFactoryAndRegisterActors, IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl'; import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator'; import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; -import { endsWith } from 'vs/base/common/strings'; +import { endsWith, startsWith } from 'vs/base/common/strings'; import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; import * as vscode from 'vscode'; import { TernarySearchTree } from 'vs/base/common/map'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; -import { isWeb } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import { joinPath } from 'vs/base/common/resources'; class ApiInstances { @@ -56,9 +57,6 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { protected _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { - // make sure modulePath ends with `.js` - const suffix = '.js'; - let modulePath = endsWith(module.fsPath, suffix) ? module.fsPath : module.fsPath + suffix; interface FakeCommonJSSelf { module?: object; @@ -71,28 +69,44 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { // FAKE commonjs world that only collects exports const patchSelf: FakeCommonJSSelf = self; - const exports = Object.create(null); - patchSelf.module = { exports }; - patchSelf.exports = exports; patchSelf.window = self; // <- that's improper but might help extensions that aren't authored correctly // FAKE require function that only works for the vscode-module - patchSelf.require = (module: string) => { - if (module !== 'vscode') { - throw new Error(`Cannot load module '${module}'`); + const moduleStack: URI[] = []; + patchSelf.require = (mod: string) => { + const parent = moduleStack[moduleStack.length - 1]; + if (mod === 'vscode') { + return this._apiInstances!.get(parent.fsPath); } - return this._apiInstances!.get(modulePath); + if (!startsWith(mod, '.')) { + throw new Error(`Cannot load module '${mod}'`); + } + + const exports = Object.create(null); + patchSelf.module = { exports }; + patchSelf.exports = exports; + + const next = joinPath(parent, '..', ensureSuffix(mod, '.js')); + moduleStack.push(next); + importScripts(asDomUri(next).toString(true)); + moduleStack.pop(); + + return exports; }; try { - // todo@joh this is a copy of `dom.ts#asDomUri` - // build url of the things we resolve - const url = isWeb - ? URI.parse(window.location.href).with({ path: '/vscode-remote', query: JSON.stringify(URI.file(modulePath)) }).toString(true) - : modulePath; - activationTimesBuilder.codeLoadingStart(); - importScripts(url); + + const exports = Object.create(null); + patchSelf.module = { exports }; + patchSelf.exports = exports; + + module = module.with({ path: ensureSuffix(module.path, '.js') }); + moduleStack.push(module); + + importScripts(asDomUri(module).toString(true)); + moduleStack.pop(); + } finally { activationTimesBuilder.codeLoadingStop(); } @@ -104,3 +118,17 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { throw new Error('Not supported'); } } + +// todo@joh this is a copy of `dom.ts#asDomUri` +function asDomUri(uri: URI): URI { + if (Schemas.vscodeRemote === uri.scheme) { + // rewrite vscode-remote-uris to uris of the window location + // so that they can be intercepted by the service worker + return URI.parse(window.location.href).with({ path: '/vscode-remote', query: JSON.stringify(uri) }); + } + return uri; +} + +function ensureSuffix(path: string, suffix: string): string { + return endsWith(path, suffix) ? path : path + suffix; +} From 73772a4ff1a5f4808eabd9cc0be74d91800c03b9 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 14 Aug 2019 16:21:20 +0200 Subject: [PATCH 165/613] #69108 Move IWorkspaceStatsService to common Introduce a simple service for web --- .../workbench/browser/web.simpleservices.ts | 20 +++++++++++++++- .../electron-browser/experimentService.ts | 2 +- .../contrib/stats/common/workspaceStats.ts | 23 +++++++++++++++++++ .../stats/electron-browser/workspaceStats.ts | 2 +- .../electron-browser/workspaceStatsService.ts | 18 +-------------- 5 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 src/vs/workbench/contrib/stats/common/workspaceStats.ts diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 95d2533c4c6ed..527823182a354 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -22,7 +22,7 @@ import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; // tslint:disable-next-line: import-patterns -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { addDisposableListener, EventType, windowOpenNoOpener } from 'vs/base/browser/dom'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; import { pathsToEditors } from 'vs/workbench/common/editor'; @@ -39,6 +39,8 @@ import { IProductService } from 'vs/platform/product/common/product'; import Severity from 'vs/base/common/severity'; import { localize } from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +// tslint:disable-next-line: import-patterns +import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats'; //#region Extension Tips @@ -847,6 +849,22 @@ registerSingleton(ITunnelService, SimpleTunnelService); //#region experiments +class WorkspaceStatsService implements IWorkspaceStatsService { + + _serviceBrand: any; + + getTags(): Promise { + return Promise.resolve({}); + } + + getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined { + return undefined; + } + +} + +registerSingleton(IWorkspaceStatsService, WorkspaceStatsService); + class ExperimentService implements IExperimentService { _serviceBrand: any; diff --git a/src/vs/workbench/contrib/experiments/electron-browser/experimentService.ts b/src/vs/workbench/contrib/experiments/electron-browser/experimentService.ts index 79dd609cb6076..0d880ac7fd7c2 100644 --- a/src/vs/workbench/contrib/experiments/electron-browser/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/electron-browser/experimentService.ts @@ -20,7 +20,7 @@ import { distinct } from 'vs/base/common/arrays'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ExperimentState, IExperimentAction, IExperimentService, IExperiment, ExperimentActionType, IExperimentActionPromptProperties } from 'vs/workbench/contrib/experiments/common/experimentService'; import { IProductService } from 'vs/platform/product/common/product'; -import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService'; +import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; interface IExperimentStorageState { enabled: boolean; diff --git a/src/vs/workbench/contrib/stats/common/workspaceStats.ts b/src/vs/workbench/contrib/stats/common/workspaceStats.ts new file mode 100644 index 0000000000000..177911447fcb3 --- /dev/null +++ b/src/vs/workbench/contrib/stats/common/workspaceStats.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export type Tags = { [index: string]: boolean | number | string | undefined }; + +export const IWorkspaceStatsService = createDecorator('workspaceStatsService'); + +export interface IWorkspaceStatsService { + _serviceBrand: any; + + getTags(): Promise; + + /** + * Returns an id for the workspace, different from the id returned by the context service. A hash based + * on the folder uri or workspace configuration, not time-based, and undefined for empty workspaces. + */ + getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined; +} diff --git a/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts b/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts index 1eac917c60cea..7577b5a9bdadf 100644 --- a/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts +++ b/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts @@ -14,7 +14,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { endsWith } from 'vs/base/common/strings'; import { ITextFileService, } from 'vs/workbench/services/textfile/common/textfiles'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService'; +import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats'; import { IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnosticsService'; const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/; diff --git a/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts b/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts index b56c4a821ca17..3cce4db8bfb74 100644 --- a/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts +++ b/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts @@ -18,10 +18,8 @@ import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspa import { localize } from 'vs/nls'; import Severity from 'vs/base/common/severity'; import { joinPath } from 'vs/base/common/resources'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; - -export type Tags = { [index: string]: boolean | number | string | undefined }; +import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats'; const DISABLE_WORKSPACE_PROMPT_KEY = 'workspaces.dontPromptToOpen'; @@ -93,20 +91,6 @@ const PyModulesToLookFor = [ 'botframework-connector' ]; -export const IWorkspaceStatsService = createDecorator('workspaceStatsService'); - -export interface IWorkspaceStatsService { - _serviceBrand: any; - getTags(): Promise; - - /** - * Returns an id for the workspace, different from the id returned by the context service. A hash based - * on the folder uri or workspace configuration, not time-based, and undefined for empty workspaces. - */ - getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined; -} - - export class WorkspaceStatsService implements IWorkspaceStatsService { _serviceBrand: any; private _tags: Tags; From 6f55bec945612318aa86d7e1deee5f6dc95fb8b8 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 14 Aug 2019 16:38:14 +0200 Subject: [PATCH 166/613] Fix #69108 --- .../workbench/browser/web.simpleservices.ts | 31 +- .../experimentalPrompt.ts | 0 .../experiments.contribution.ts | 5 +- .../experiments/common/experimentService.ts | 404 ++++++++++++++++- .../electron-browser/experimentService.ts | 407 ------------------ .../experimentService.test.ts | 3 +- .../experimentalPrompts.test.ts | 2 +- .../electron-browser/extensionsViews.test.ts | 3 +- src/vs/workbench/workbench.common.main.ts | 3 + src/vs/workbench/workbench.desktop.main.ts | 3 - 10 files changed, 411 insertions(+), 450 deletions(-) rename src/vs/workbench/contrib/experiments/{electron-browser => browser}/experimentalPrompt.ts (100%) rename src/vs/workbench/contrib/experiments/{electron-browser => browser}/experiments.contribution.ts (79%) delete mode 100644 src/vs/workbench/contrib/experiments/electron-browser/experimentService.ts diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 527823182a354..f9bfd69e4c9a6 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -31,8 +31,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { toStoreData, restoreRecentlyOpened } from 'vs/platform/history/common/historyStorage'; -// tslint:disable-next-line: import-patterns -import { IExperimentService, IExperiment, ExperimentActionType, ExperimentState } from 'vs/workbench/contrib/experiments/common/experimentService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProductService } from 'vs/platform/product/common/product'; @@ -847,7 +845,7 @@ registerSingleton(ITunnelService, SimpleTunnelService); //#endregion -//#region experiments +//#region workspace stats class WorkspaceStatsService implements IWorkspaceStatsService { @@ -865,31 +863,4 @@ class WorkspaceStatsService implements IWorkspaceStatsService { registerSingleton(IWorkspaceStatsService, WorkspaceStatsService); -class ExperimentService implements IExperimentService { - _serviceBrand: any; - - async getExperimentById(id: string): Promise { - return { - enabled: false, - id: '', - state: ExperimentState.NoRun - }; - } - - async getExperimentsByType(type: ExperimentActionType): Promise { - return []; - } - - async getCuratedExtensionsList(curatedExtensionsKey: string): Promise { - return []; - } - - markAsCompleted(experimentId: string): void { } - - onExperimentEnabled: Event = Event.None; - -} - -registerSingleton(IExperimentService, ExperimentService); - //#endregion diff --git a/src/vs/workbench/contrib/experiments/electron-browser/experimentalPrompt.ts b/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts similarity index 100% rename from src/vs/workbench/contrib/experiments/electron-browser/experimentalPrompt.ts rename to src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts diff --git a/src/vs/workbench/contrib/experiments/electron-browser/experiments.contribution.ts b/src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts similarity index 79% rename from src/vs/workbench/contrib/experiments/electron-browser/experiments.contribution.ts rename to src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts index ca28e9b6f67d3..67b6159734f82 100644 --- a/src/vs/workbench/contrib/experiments/electron-browser/experiments.contribution.ts +++ b/src/vs/workbench/contrib/experiments/browser/experiments.contribution.ts @@ -4,12 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; -import { ExperimentService } from 'vs/workbench/contrib/experiments/electron-browser/experimentService'; +import { IExperimentService, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/electron-browser/experimentalPrompt'; +import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/browser/experimentalPrompt'; registerSingleton(IExperimentService, ExperimentService, true); diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index 437afe18bd6e5..5707377ce4b1b 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -4,7 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { language } from 'vs/base/common/platform'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { match } from 'vs/base/common/glob'; +import { IRequestService, asJson } from 'vs/platform/request/common/request'; +import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { distinct } from 'vs/base/common/arrays'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { IProductService } from 'vs/platform/product/common/product'; +import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; export const enum ExperimentState { Evaluating, @@ -56,4 +72,388 @@ export interface IExperimentService { onExperimentEnabled: Event; } -export const IExperimentService = createDecorator('experimentService'); \ No newline at end of file +export const IExperimentService = createDecorator('experimentService'); + +interface IExperimentStorageState { + enabled: boolean; + state: ExperimentState; + editCount?: number; + lastEditedDate?: string; +} + +interface IRawExperiment { + id: string; + enabled?: boolean; + condition?: { + insidersOnly?: boolean; + newUser?: boolean; + displayLanguage?: string; + installedExtensions?: { + excludes?: string[]; + includes?: string[]; + }, + fileEdits?: { + filePathPattern?: string; + workspaceIncludes?: string[]; + workspaceExcludes?: string[]; + minEditCount: number; + }, + experimentsPreviouslyRun?: { + excludes?: string[]; + includes?: string[]; + } + userProbability?: number; + }; + action?: IExperimentAction; +} + +export class ExperimentService extends Disposable implements IExperimentService { + _serviceBrand: any; + private _experiments: IExperiment[] = []; + private _loadExperimentsPromise: Promise; + private _curatedMapping = Object.create(null); + + private readonly _onExperimentEnabled = this._register(new Emitter()); + onExperimentEnabled: Event = this._onExperimentEnabled.event; + + constructor( + @IStorageService private readonly storageService: IStorageService, + @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @ITextFileService private readonly textFileService: ITextFileService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IRequestService private readonly requestService: IRequestService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IProductService private readonly productService: IProductService, + @IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService + ) { + super(); + + this._loadExperimentsPromise = Promise.resolve(this.lifecycleService.when(LifecyclePhase.Eventually)).then(() => this.loadExperiments()); + } + + public getExperimentById(id: string): Promise { + return this._loadExperimentsPromise.then(() => { + return this._experiments.filter(x => x.id === id)[0]; + }); + } + + public getExperimentsByType(type: ExperimentActionType): Promise { + return this._loadExperimentsPromise.then(() => { + if (type === ExperimentActionType.Custom) { + return this._experiments.filter(x => x.enabled && (!x.action || x.action.type === type)); + } + return this._experiments.filter(x => x.enabled && x.action && x.action.type === type); + }); + } + + public getCuratedExtensionsList(curatedExtensionsKey: string): Promise { + return this._loadExperimentsPromise.then(() => { + for (const experiment of this._experiments) { + if (experiment.enabled + && experiment.state === ExperimentState.Run + && this._curatedMapping[experiment.id] + && this._curatedMapping[experiment.id].curatedExtensionsKey === curatedExtensionsKey) { + return this._curatedMapping[experiment.id].curatedExtensionsList; + } + } + return []; + }); + } + + public markAsCompleted(experimentId: string): void { + const storageKey = 'experiments.' + experimentId; + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + experimentState.state = ExperimentState.Complete; + this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); + } + + protected getExperiments(): Promise { + if (!this.productService.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) { + return Promise.resolve([]); + } + return this.requestService.request({ type: 'GET', url: this.productService.experimentsUrl }, CancellationToken.None).then(context => { + if (context.res.statusCode !== 200) { + return Promise.resolve(null); + } + return asJson(context).then((result: any) => { + return result && Array.isArray(result['experiments']) ? result['experiments'] : []; + }); + }, () => Promise.resolve(null)); + } + + private loadExperiments(): Promise { + return this.getExperiments().then(rawExperiments => { + // Offline mode + if (!rawExperiments) { + const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []); + if (Array.isArray(allExperimentIdsFromStorage)) { + allExperimentIdsFromStorage.forEach(experimentId => { + const storageKey = 'experiments.' + experimentId; + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), null); + if (experimentState) { + this._experiments.push({ + id: experimentId, + enabled: experimentState.enabled, + state: experimentState.state + }); + } + }); + } + return Promise.resolve(null); + } + + // Clear disbaled/deleted experiments from storage + const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []); + const enabledExperiments = rawExperiments.filter(experiment => !!experiment.enabled).map(experiment => experiment.id.toLowerCase()); + if (Array.isArray(allExperimentIdsFromStorage)) { + allExperimentIdsFromStorage.forEach(experiment => { + if (enabledExperiments.indexOf(experiment) === -1) { + this.storageService.remove(`experiments.${experiment}`, StorageScope.GLOBAL); + } + }); + } + if (enabledExperiments.length) { + this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL); + } else { + this.storageService.remove('allExperiments', StorageScope.GLOBAL); + } + + const promises = rawExperiments.map(experiment => { + const processedExperiment: IExperiment = { + id: experiment.id, + enabled: !!experiment.enabled, + state: !!experiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun + }; + + if (experiment.action) { + processedExperiment.action = { + type: ExperimentActionType[experiment.action.type] || ExperimentActionType.Custom, + properties: experiment.action.properties + }; + if (processedExperiment.action.type === ExperimentActionType.Prompt) { + ((processedExperiment.action.properties).commands || []).forEach(x => { + if (x.curatedExtensionsKey && Array.isArray(x.curatedExtensionsList)) { + this._curatedMapping[experiment.id] = x; + } + }); + } + if (!processedExperiment.action.properties) { + processedExperiment.action.properties = {}; + } + } + this._experiments.push(processedExperiment); + + if (!processedExperiment.enabled) { + return Promise.resolve(null); + } + + const storageKey = 'experiments.' + experiment.id; + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + if (!experimentState.hasOwnProperty('enabled')) { + experimentState.enabled = processedExperiment.enabled; + } + if (!experimentState.hasOwnProperty('state')) { + experimentState.state = processedExperiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun; + } else { + processedExperiment.state = experimentState.state; + } + + return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => { + experimentState.state = processedExperiment.state = state; + this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); + + if (state === ExperimentState.Run) { + this.fireRunExperiment(processedExperiment); + } + return Promise.resolve(null); + }); + + }); + return Promise.all(promises).then(() => { + type ExperimentsClassification = { + experiments: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + }; + this.telemetryService.publicLog2<{ experiments: IExperiment[] }, ExperimentsClassification>('experiments', { experiments: this._experiments }); + }); + }); + } + + private fireRunExperiment(experiment: IExperiment) { + this._onExperimentEnabled.fire(experiment); + const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); + if (runExperimentIdsFromStorage.indexOf(experiment.id) === -1) { + runExperimentIdsFromStorage.push(experiment.id); + } + + // Ensure we dont store duplicates + const distinctExperiments = distinct(runExperimentIdsFromStorage); + if (runExperimentIdsFromStorage.length !== distinctExperiments.length) { + this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL); + } + } + + private checkExperimentDependencies(experiment: IRawExperiment): boolean { + const experimentsPreviouslyRun = experiment.condition ? experiment.condition.experimentsPreviouslyRun : undefined; + if (experimentsPreviouslyRun) { + const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); + let includeCheck = true; + let excludeCheck = true; + const includes = experimentsPreviouslyRun.includes; + if (Array.isArray(includes)) { + includeCheck = runExperimentIdsFromStorage.some(x => includes.indexOf(x) > -1); + } + const excludes = experimentsPreviouslyRun.excludes; + if (includeCheck && Array.isArray(excludes)) { + excludeCheck = !runExperimentIdsFromStorage.some(x => excludes.indexOf(x) > -1); + } + if (!includeCheck || !excludeCheck) { + return false; + } + } + return true; + } + + private shouldRunExperiment(experiment: IRawExperiment, processedExperiment: IExperiment): Promise { + if (processedExperiment.state !== ExperimentState.Evaluating) { + return Promise.resolve(processedExperiment.state); + } + + if (!experiment.enabled) { + return Promise.resolve(ExperimentState.NoRun); + } + + const condition = experiment.condition; + if (!condition) { + return Promise.resolve(ExperimentState.Run); + } + + if (!this.checkExperimentDependencies(experiment)) { + return Promise.resolve(ExperimentState.NoRun); + } + + if (this.environmentService.appQuality === 'stable' && condition.insidersOnly === true) { + return Promise.resolve(ExperimentState.NoRun); + } + + const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL); + if ((condition.newUser === true && !isNewUser) + || (condition.newUser === false && isNewUser)) { + return Promise.resolve(ExperimentState.NoRun); + } + + if (typeof condition.displayLanguage === 'string') { + let localeToCheck = condition.displayLanguage.toLowerCase(); + let displayLanguage = language!.toLowerCase(); + + if (localeToCheck !== displayLanguage) { + const a = displayLanguage.indexOf('-'); + const b = localeToCheck.indexOf('-'); + if (a > -1) { + displayLanguage = displayLanguage.substr(0, a); + } + if (b > -1) { + localeToCheck = localeToCheck.substr(0, b); + } + if (displayLanguage !== localeToCheck) { + return Promise.resolve(ExperimentState.NoRun); + } + } + } + + if (!condition.userProbability) { + condition.userProbability = 1; + } + + let extensionsCheckPromise = Promise.resolve(true); + const installedExtensions = condition.installedExtensions; + if (installedExtensions) { + extensionsCheckPromise = this.extensionManagementService.getInstalled(ExtensionType.User).then(locals => { + let includesCheck = true; + let excludesCheck = true; + const localExtensions = locals.map(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}`); + if (Array.isArray(installedExtensions.includes) && installedExtensions.includes.length) { + const extensionIncludes = installedExtensions.includes.map(e => e.toLowerCase()); + includesCheck = localExtensions.some(e => extensionIncludes.indexOf(e) > -1); + } + if (Array.isArray(installedExtensions.excludes) && installedExtensions.excludes.length) { + const extensionExcludes = installedExtensions.excludes.map(e => e.toLowerCase()); + excludesCheck = !localExtensions.some(e => extensionExcludes.indexOf(e) > -1); + } + return includesCheck && excludesCheck; + }); + } + + const storageKey = 'experiments.' + experiment.id; + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + + return extensionsCheckPromise.then(success => { + const fileEdits = condition.fileEdits; + if (!success || !fileEdits || typeof fileEdits.minEditCount !== 'number') { + const runExperiment = success && typeof condition.userProbability === 'number' && Math.random() < condition.userProbability; + return runExperiment ? ExperimentState.Run : ExperimentState.NoRun; + } + + experimentState.editCount = experimentState.editCount || 0; + if (experimentState.editCount >= fileEdits.minEditCount) { + return ExperimentState.Run; + } + + const onSaveHandler = this.textFileService.models.onModelsSaved(e => { + const date = new Date().toDateString(); + const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + if (latestExperimentState.state !== ExperimentState.Evaluating) { + onSaveHandler.dispose(); + return; + } + e.forEach(async event => { + if (event.kind !== StateChange.SAVED + || latestExperimentState.state !== ExperimentState.Evaluating + || date === latestExperimentState.lastEditedDate + || (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) + ) { + return; + } + let filePathCheck = true; + let workspaceCheck = true; + + if (typeof fileEdits.filePathPattern === 'string') { + filePathCheck = match(fileEdits.filePathPattern, event.resource.fsPath); + } + if (Array.isArray(fileEdits.workspaceIncludes) && fileEdits.workspaceIncludes.length) { + const tags = await this.workspaceStatsService.getTags(); + workspaceCheck = !!tags && fileEdits.workspaceIncludes.some(x => !!tags[x]); + } + if (workspaceCheck && Array.isArray(fileEdits.workspaceExcludes) && fileEdits.workspaceExcludes.length) { + const tags = await this.workspaceStatsService.getTags(); + workspaceCheck = !!tags && !fileEdits.workspaceExcludes.some(x => !!tags[x]); + } + if (filePathCheck && workspaceCheck) { + latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1; + latestExperimentState.lastEditedDate = date; + this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); + } + }); + if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) { + processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun; + this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); + if (latestExperimentState.state === ExperimentState.Run && experiment.action && ExperimentActionType[experiment.action.type] === ExperimentActionType.Prompt) { + this.fireRunExperiment(processedExperiment); + } + } + }); + this._register(onSaveHandler); + return ExperimentState.Evaluating; + }); + } +} + + +function safeParse(text: string | undefined, defaultObject: any) { + try { + return text ? JSON.parse(text) || defaultObject : defaultObject; + } catch (e) { + return defaultObject; + } +} diff --git a/src/vs/workbench/contrib/experiments/electron-browser/experimentService.ts b/src/vs/workbench/contrib/experiments/electron-browser/experimentService.ts deleted file mode 100644 index 0d880ac7fd7c2..0000000000000 --- a/src/vs/workbench/contrib/experiments/electron-browser/experimentService.ts +++ /dev/null @@ -1,407 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { language } from 'vs/base/common/platform'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { match } from 'vs/base/common/glob'; -import { IRequestService, asJson } from 'vs/platform/request/common/request'; -import { Emitter, Event } from 'vs/base/common/event'; -import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { distinct } from 'vs/base/common/arrays'; -import { ExtensionType } from 'vs/platform/extensions/common/extensions'; -import { ExperimentState, IExperimentAction, IExperimentService, IExperiment, ExperimentActionType, IExperimentActionPromptProperties } from 'vs/workbench/contrib/experiments/common/experimentService'; -import { IProductService } from 'vs/platform/product/common/product'; -import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; - -interface IExperimentStorageState { - enabled: boolean; - state: ExperimentState; - editCount?: number; - lastEditedDate?: string; -} - -interface IRawExperiment { - id: string; - enabled?: boolean; - condition?: { - insidersOnly?: boolean; - newUser?: boolean; - displayLanguage?: string; - installedExtensions?: { - excludes?: string[]; - includes?: string[]; - }, - fileEdits?: { - filePathPattern?: string; - workspaceIncludes?: string[]; - workspaceExcludes?: string[]; - minEditCount: number; - }, - experimentsPreviouslyRun?: { - excludes?: string[]; - includes?: string[]; - } - userProbability?: number; - }; - action?: IExperimentAction; -} - -export class ExperimentService extends Disposable implements IExperimentService { - _serviceBrand: any; - private _experiments: IExperiment[] = []; - private _loadExperimentsPromise: Promise; - private _curatedMapping = Object.create(null); - - private readonly _onExperimentEnabled = this._register(new Emitter()); - onExperimentEnabled: Event = this._onExperimentEnabled.event; - - constructor( - @IStorageService private readonly storageService: IStorageService, - @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @ITextFileService private readonly textFileService: ITextFileService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @ILifecycleService private readonly lifecycleService: ILifecycleService, - @IRequestService private readonly requestService: IRequestService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IProductService private readonly productService: IProductService, - @IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService - ) { - super(); - - this._loadExperimentsPromise = Promise.resolve(this.lifecycleService.when(LifecyclePhase.Eventually)).then(() => this.loadExperiments()); - } - - public getExperimentById(id: string): Promise { - return this._loadExperimentsPromise.then(() => { - return this._experiments.filter(x => x.id === id)[0]; - }); - } - - public getExperimentsByType(type: ExperimentActionType): Promise { - return this._loadExperimentsPromise.then(() => { - if (type === ExperimentActionType.Custom) { - return this._experiments.filter(x => x.enabled && (!x.action || x.action.type === type)); - } - return this._experiments.filter(x => x.enabled && x.action && x.action.type === type); - }); - } - - public getCuratedExtensionsList(curatedExtensionsKey: string): Promise { - return this._loadExperimentsPromise.then(() => { - for (const experiment of this._experiments) { - if (experiment.enabled - && experiment.state === ExperimentState.Run - && this._curatedMapping[experiment.id] - && this._curatedMapping[experiment.id].curatedExtensionsKey === curatedExtensionsKey) { - return this._curatedMapping[experiment.id].curatedExtensionsList; - } - } - return []; - }); - } - - public markAsCompleted(experimentId: string): void { - const storageKey = 'experiments.' + experimentId; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); - experimentState.state = ExperimentState.Complete; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); - } - - protected getExperiments(): Promise { - if (!this.productService.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) { - return Promise.resolve([]); - } - return this.requestService.request({ type: 'GET', url: this.productService.experimentsUrl }, CancellationToken.None).then(context => { - if (context.res.statusCode !== 200) { - return Promise.resolve(null); - } - return asJson(context).then((result: any) => { - return result && Array.isArray(result['experiments']) ? result['experiments'] : []; - }); - }, () => Promise.resolve(null)); - } - - private loadExperiments(): Promise { - return this.getExperiments().then(rawExperiments => { - // Offline mode - if (!rawExperiments) { - const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []); - if (Array.isArray(allExperimentIdsFromStorage)) { - allExperimentIdsFromStorage.forEach(experimentId => { - const storageKey = 'experiments.' + experimentId; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), null); - if (experimentState) { - this._experiments.push({ - id: experimentId, - enabled: experimentState.enabled, - state: experimentState.state - }); - } - }); - } - return Promise.resolve(null); - } - - // Clear disbaled/deleted experiments from storage - const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []); - const enabledExperiments = rawExperiments.filter(experiment => !!experiment.enabled).map(experiment => experiment.id.toLowerCase()); - if (Array.isArray(allExperimentIdsFromStorage)) { - allExperimentIdsFromStorage.forEach(experiment => { - if (enabledExperiments.indexOf(experiment) === -1) { - this.storageService.remove(`experiments.${experiment}`, StorageScope.GLOBAL); - } - }); - } - if (enabledExperiments.length) { - this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL); - } else { - this.storageService.remove('allExperiments', StorageScope.GLOBAL); - } - - const promises = rawExperiments.map(experiment => { - const processedExperiment: IExperiment = { - id: experiment.id, - enabled: !!experiment.enabled, - state: !!experiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun - }; - - if (experiment.action) { - processedExperiment.action = { - type: ExperimentActionType[experiment.action.type] || ExperimentActionType.Custom, - properties: experiment.action.properties - }; - if (processedExperiment.action.type === ExperimentActionType.Prompt) { - ((processedExperiment.action.properties).commands || []).forEach(x => { - if (x.curatedExtensionsKey && Array.isArray(x.curatedExtensionsList)) { - this._curatedMapping[experiment.id] = x; - } - }); - } - if (!processedExperiment.action.properties) { - processedExperiment.action.properties = {}; - } - } - this._experiments.push(processedExperiment); - - if (!processedExperiment.enabled) { - return Promise.resolve(null); - } - - const storageKey = 'experiments.' + experiment.id; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); - if (!experimentState.hasOwnProperty('enabled')) { - experimentState.enabled = processedExperiment.enabled; - } - if (!experimentState.hasOwnProperty('state')) { - experimentState.state = processedExperiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun; - } else { - processedExperiment.state = experimentState.state; - } - - return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => { - experimentState.state = processedExperiment.state = state; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL); - - if (state === ExperimentState.Run) { - this.fireRunExperiment(processedExperiment); - } - return Promise.resolve(null); - }); - - }); - return Promise.all(promises).then(() => { - type ExperimentsClassification = { - experiments: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - }; - this.telemetryService.publicLog2<{ experiments: IExperiment[] }, ExperimentsClassification>('experiments', { experiments: this._experiments }); - }); - }); - } - - private fireRunExperiment(experiment: IExperiment) { - this._onExperimentEnabled.fire(experiment); - const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); - if (runExperimentIdsFromStorage.indexOf(experiment.id) === -1) { - runExperimentIdsFromStorage.push(experiment.id); - } - - // Ensure we dont store duplicates - const distinctExperiments = distinct(runExperimentIdsFromStorage); - if (runExperimentIdsFromStorage.length !== distinctExperiments.length) { - this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL); - } - } - - private checkExperimentDependencies(experiment: IRawExperiment): boolean { - const experimentsPreviouslyRun = experiment.condition ? experiment.condition.experimentsPreviouslyRun : undefined; - if (experimentsPreviouslyRun) { - const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); - let includeCheck = true; - let excludeCheck = true; - const includes = experimentsPreviouslyRun.includes; - if (Array.isArray(includes)) { - includeCheck = runExperimentIdsFromStorage.some(x => includes.indexOf(x) > -1); - } - const excludes = experimentsPreviouslyRun.excludes; - if (includeCheck && Array.isArray(excludes)) { - excludeCheck = !runExperimentIdsFromStorage.some(x => excludes.indexOf(x) > -1); - } - if (!includeCheck || !excludeCheck) { - return false; - } - } - return true; - } - - private shouldRunExperiment(experiment: IRawExperiment, processedExperiment: IExperiment): Promise { - if (processedExperiment.state !== ExperimentState.Evaluating) { - return Promise.resolve(processedExperiment.state); - } - - if (!experiment.enabled) { - return Promise.resolve(ExperimentState.NoRun); - } - - const condition = experiment.condition; - if (!condition) { - return Promise.resolve(ExperimentState.Run); - } - - if (!this.checkExperimentDependencies(experiment)) { - return Promise.resolve(ExperimentState.NoRun); - } - - if (this.environmentService.appQuality === 'stable' && condition.insidersOnly === true) { - return Promise.resolve(ExperimentState.NoRun); - } - - const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL); - if ((condition.newUser === true && !isNewUser) - || (condition.newUser === false && isNewUser)) { - return Promise.resolve(ExperimentState.NoRun); - } - - if (typeof condition.displayLanguage === 'string') { - let localeToCheck = condition.displayLanguage.toLowerCase(); - let displayLanguage = language!.toLowerCase(); - - if (localeToCheck !== displayLanguage) { - const a = displayLanguage.indexOf('-'); - const b = localeToCheck.indexOf('-'); - if (a > -1) { - displayLanguage = displayLanguage.substr(0, a); - } - if (b > -1) { - localeToCheck = localeToCheck.substr(0, b); - } - if (displayLanguage !== localeToCheck) { - return Promise.resolve(ExperimentState.NoRun); - } - } - } - - if (!condition.userProbability) { - condition.userProbability = 1; - } - - let extensionsCheckPromise = Promise.resolve(true); - const installedExtensions = condition.installedExtensions; - if (installedExtensions) { - extensionsCheckPromise = this.extensionManagementService.getInstalled(ExtensionType.User).then(locals => { - let includesCheck = true; - let excludesCheck = true; - const localExtensions = locals.map(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}`); - if (Array.isArray(installedExtensions.includes) && installedExtensions.includes.length) { - const extensionIncludes = installedExtensions.includes.map(e => e.toLowerCase()); - includesCheck = localExtensions.some(e => extensionIncludes.indexOf(e) > -1); - } - if (Array.isArray(installedExtensions.excludes) && installedExtensions.excludes.length) { - const extensionExcludes = installedExtensions.excludes.map(e => e.toLowerCase()); - excludesCheck = !localExtensions.some(e => extensionExcludes.indexOf(e) > -1); - } - return includesCheck && excludesCheck; - }); - } - - const storageKey = 'experiments.' + experiment.id; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); - - return extensionsCheckPromise.then(success => { - const fileEdits = condition.fileEdits; - if (!success || !fileEdits || typeof fileEdits.minEditCount !== 'number') { - const runExperiment = success && typeof condition.userProbability === 'number' && Math.random() < condition.userProbability; - return runExperiment ? ExperimentState.Run : ExperimentState.NoRun; - } - - experimentState.editCount = experimentState.editCount || 0; - if (experimentState.editCount >= fileEdits.minEditCount) { - return ExperimentState.Run; - } - - const onSaveHandler = this.textFileService.models.onModelsSaved(e => { - const date = new Date().toDateString(); - const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); - if (latestExperimentState.state !== ExperimentState.Evaluating) { - onSaveHandler.dispose(); - return; - } - e.forEach(async event => { - if (event.kind !== StateChange.SAVED - || latestExperimentState.state !== ExperimentState.Evaluating - || date === latestExperimentState.lastEditedDate - || (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) - ) { - return; - } - let filePathCheck = true; - let workspaceCheck = true; - - if (typeof fileEdits.filePathPattern === 'string') { - filePathCheck = match(fileEdits.filePathPattern, event.resource.fsPath); - } - if (Array.isArray(fileEdits.workspaceIncludes) && fileEdits.workspaceIncludes.length) { - const tags = await this.workspaceStatsService.getTags(); - workspaceCheck = !!tags && fileEdits.workspaceIncludes.some(x => !!tags[x]); - } - if (workspaceCheck && Array.isArray(fileEdits.workspaceExcludes) && fileEdits.workspaceExcludes.length) { - const tags = await this.workspaceStatsService.getTags(); - workspaceCheck = !!tags && !fileEdits.workspaceExcludes.some(x => !!tags[x]); - } - if (filePathCheck && workspaceCheck) { - latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1; - latestExperimentState.lastEditedDate = date; - this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); - } - }); - if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) { - processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun; - this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL); - if (latestExperimentState.state === ExperimentState.Run && experiment.action && ExperimentActionType[experiment.action.type] === ExperimentActionType.Prompt) { - this.fireRunExperiment(processedExperiment); - } - } - }); - this._register(onSaveHandler); - return ExperimentState.Evaluating; - }); - } -} - - -function safeParse(text: string | undefined, defaultObject: any) { - try { - return text ? JSON.parse(text) || defaultObject : defaultObject; - } catch (e) { - return defaultObject; - } -} diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts index f44b4d3904e56..70851b908c950 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ExperimentActionType, ExperimentState, IExperiment } from 'vs/workbench/contrib/experiments/common/experimentService'; -import { ExperimentService } from 'vs/workbench/contrib/experiments/electron-browser/experimentService'; +import { ExperimentActionType, ExperimentState, IExperiment, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices'; diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts index 781fc5ab7c0fc..ff693a1f4e3ae 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentalPrompts.test.ts @@ -12,7 +12,7 @@ import { TestNotificationService } from 'vs/platform/notification/test/common/te import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/electron-browser/experimentalPrompt'; +import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/browser/experimentalPrompt'; import { ExperimentActionType, ExperimentState, IExperiment, IExperimentActionPromptProperties, IExperimentService, LocalizedPromptText } from 'vs/workbench/contrib/experiments/common/experimentService'; import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test'; import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index cf2b6c795226f..a618098b66cb1 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -35,8 +35,7 @@ import { URLService } from 'vs/platform/url/common/urlService'; import { URI } from 'vs/base/common/uri'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { SinonStub } from 'sinon'; -import { IExperimentService, ExperimentState, ExperimentActionType } from 'vs/workbench/contrib/experiments/common/experimentService'; -import { ExperimentService } from 'vs/workbench/contrib/experiments/electron-browser/experimentService'; +import { IExperimentService, ExperimentState, ExperimentActionType, ExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 72765299a3129..7c12967c4ae56 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -227,4 +227,7 @@ import 'vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution'; // Outline import 'vs/workbench/contrib/outline/browser/outline.contribution'; +// Experiments +import 'vs/workbench/contrib/experiments/browser/experiments.contribution'; + //#endregion diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 4d2996a0ed657..d426224accf4a 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -154,9 +154,6 @@ import 'vs/workbench/contrib/themes/test/electron-browser/themes.test.contributi import 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution'; import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution'; -// Experiments -import 'vs/workbench/contrib/experiments/electron-browser/experiments.contribution'; - // Issues import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; From 994e1315c09a6d92e57adf8cb9d386f5b30b4b3f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 14 Aug 2019 16:49:22 +0200 Subject: [PATCH 167/613] tests -remove unused services --- .../textfile/test/textFileService.io.test.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts index 6bbdf0e42e1c6..eab223e0d4a78 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts @@ -4,16 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; -import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { workbenchInstantiationService, TestLifecycleService, TestTextFileService, TestWindowsService, TestContextService, TestFileService } from 'vs/workbench/test/workbenchTestServices'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/workbenchTestServices'; import { ITextFileService, snapshotToString, TextFileOperationResult, TextFileOperationError } from 'vs/workbench/services/textfile/common/textfiles'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IFileService } from 'vs/platform/files/common/files'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { Schemas } from 'vs/base/common/network'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { rimraf, RimRafMode, copy, readFile, exists } from 'vs/base/node/pfs'; @@ -36,13 +31,8 @@ import { detectEncodingByBOM } from 'vs/base/test/node/encoding/encoding.test'; class ServiceAccessor { constructor( - @ILifecycleService public lifecycleService: TestLifecycleService, @ITextFileService public textFileService: TestTextFileService, - @IUntitledEditorService public untitledEditorService: IUntitledEditorService, - @IWindowsService public windowsService: TestWindowsService, - @IWorkspaceContextService public contextService: TestContextService, - @IModelService public modelService: ModelServiceImpl, - @IFileService public fileService: TestFileService + @IUntitledEditorService public untitledEditorService: IUntitledEditorService ) { } } From 9c2cc80fcdab2c949294771daa05f61652ca009e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 14 Aug 2019 16:56:16 +0200 Subject: [PATCH 168/613] Move getHashedRemotesFromUri to IWorkspaceStatsService --- src/vs/workbench/browser/web.simpleservices.ts | 4 ++++ .../electron-browser/extensionTipsService.ts | 7 +++---- .../contrib/stats/common/workspaceStats.ts | 3 +++ .../stats/electron-browser/workspaceStats.ts | 16 +--------------- .../electron-browser/workspaceStatsService.ts | 15 +++++++++++++++ 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index f9bfd69e4c9a6..46a3f6a59aa56 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -859,6 +859,10 @@ class WorkspaceStatsService implements IWorkspaceStatsService { return undefined; } + getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit?: boolean): Promise { + return Promise.resolve([]); + } + } registerSingleton(IWorkspaceStatsService, WorkspaceStatsService); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts index b424211b797c3..33e5fbc2bfe62 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts @@ -29,7 +29,6 @@ import { flatten, distinct, shuffle, coalesce } from 'vs/base/common/arrays'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { guessMimeTypes, MIME_UNKNOWN } from 'vs/base/common/mime'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { getHashedRemotesFromUri } from 'vs/workbench/contrib/stats/electron-browser/workspaceStats'; import { IRequestService, asJson } from 'vs/platform/request/common/request'; import { isNumber } from 'vs/base/common/types'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -42,9 +41,9 @@ import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/wo import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { extname } from 'vs/base/common/resources'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IExeBasedExtensionTip } from 'vs/platform/product/common/product'; import { timeout } from 'vs/base/common/async'; +import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; const milliSecondsInADay = 1000 * 60 * 60 * 24; const choiceNever = localize('neverShowAgain', "Don't Show Again"); @@ -109,7 +108,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService, @IExperimentService private readonly experimentService: IExperimentService, - @ITextFileService private readonly textFileService: ITextFileService + @IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService ) { super(); @@ -1078,7 +1077,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe const storageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations'; const workspaceUri = this.contextService.getWorkspace().folders[0].uri; - return Promise.all([getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, false), getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, true)]).then(([hashedRemotes1, hashedRemotes2]) => { + return Promise.all([this.workspaceStatsService.getHashedRemotesFromUri(workspaceUri, false), this.workspaceStatsService.getHashedRemotesFromUri(workspaceUri, true)]).then(([hashedRemotes1, hashedRemotes2]) => { const hashedRemotes = (hashedRemotes1 || []).concat(hashedRemotes2 || []); if (!hashedRemotes.length) { return undefined; diff --git a/src/vs/workbench/contrib/stats/common/workspaceStats.ts b/src/vs/workbench/contrib/stats/common/workspaceStats.ts index 177911447fcb3..7bd69fc83cda9 100644 --- a/src/vs/workbench/contrib/stats/common/workspaceStats.ts +++ b/src/vs/workbench/contrib/stats/common/workspaceStats.ts @@ -5,6 +5,7 @@ import { WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { URI } from 'vs/base/common/uri'; export type Tags = { [index: string]: boolean | number | string | undefined }; @@ -20,4 +21,6 @@ export interface IWorkspaceStatsService { * on the folder uri or workspace configuration, not time-based, and undefined for empty workspaces. */ getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): string | undefined; + + getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit?: boolean): Promise; } diff --git a/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts b/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts index 7577b5a9bdadf..d6d6c795e0cad 100644 --- a/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts +++ b/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts @@ -136,20 +136,6 @@ export function getHashedRemotesFromConfig(text: string, stripEndingDotGit: bool }); } -export function getHashedRemotesFromUri(workspaceUri: URI, fileService: IFileService, textFileService: ITextFileService, stripEndingDotGit: boolean = false): Promise { - const path = workspaceUri.path; - const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` }); - return fileService.exists(uri).then(exists => { - if (!exists) { - return []; - } - return textFileService.read(uri, { acceptTextOnly: true }).then( - content => getHashedRemotesFromConfig(content.value, stripEndingDotGit), - err => [] // ignore missing or binary file - ); - }); -} - export class WorkspaceStats implements IWorkbenchContribution { constructor( @@ -230,7 +216,7 @@ export class WorkspaceStats implements IWorkbenchContribution { private reportRemotes(workspaceUris: URI[]): void { Promise.all(workspaceUris.map(workspaceUri => { - return getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, true); + return this.workspaceStatsService.getHashedRemotesFromUri(workspaceUri, true); })).then(hashedRemotes => { /* __GDPR__ "workspace.hashedRemotes" : { diff --git a/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts b/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts index 3cce4db8bfb74..9c1c1d5718047 100644 --- a/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts +++ b/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts @@ -20,6 +20,7 @@ import Severity from 'vs/base/common/severity'; import { joinPath } from 'vs/base/common/resources'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats'; +import { getHashedRemotesFromConfig } from 'vs/workbench/contrib/stats/electron-browser/workspaceStats'; const DISABLE_WORKSPACE_PROMPT_KEY = 'workspaces.dontPromptToOpen'; @@ -136,6 +137,20 @@ export class WorkspaceStatsService implements IWorkspaceStatsService { return workspaceId; } + getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit: boolean = false): Promise { + const path = workspaceUri.path; + const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` }); + return this.fileService.exists(uri).then(exists => { + if (!exists) { + return []; + } + return this.textFileService.read(uri, { acceptTextOnly: true }).then( + content => getHashedRemotesFromConfig(content.value, stripEndingDotGit), + err => [] // ignore missing or binary file + ); + }); + } + /* __GDPR__FRAGMENT__ "WorkspaceTags" : { "workbench.filesToOpenOrCreate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, From c3fcaef0c03a88e17cb2ce46d650b3425fd5efa9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 14 Aug 2019 17:44:09 +0200 Subject: [PATCH 169/613] add and use `getWorkerBootstrapUrl`, don't use default worker factory anymore --- src/vs/base/worker/defaultWorkerFactory.ts | 33 +++++++++-------- .../browser/webWorkerExtensionHostStarter.ts | 36 +++++++++++-------- .../extensions/worker/extensionHostWorker.ts | 20 ++++------- .../worker/extensionHostWorkerMain.ts | 21 +++++++++++ 4 files changed, 68 insertions(+), 42 deletions(-) create mode 100644 src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts diff --git a/src/vs/base/worker/defaultWorkerFactory.ts b/src/vs/base/worker/defaultWorkerFactory.ts index c2a7ddc54e6a2..bce60b6b85564 100644 --- a/src/vs/base/worker/defaultWorkerFactory.ts +++ b/src/vs/base/worker/defaultWorkerFactory.ts @@ -19,26 +19,31 @@ function getWorker(workerId: string, label: string): Worker | Promise { // ESM-comment-begin if (typeof require === 'function') { // check if the JS lives on a different origin - const workerMain = require.toUrl('./' + workerId); - if (/^(http:)|(https:)|(file:)/.test(workerMain)) { - const currentUrl = String(window.location); - const currentOrigin = currentUrl.substr(0, currentUrl.length - window.location.hash.length - window.location.search.length - window.location.pathname.length); - if (workerMain.substring(0, currentOrigin.length) !== currentOrigin) { - // this is the cross-origin case - // i.e. the webpage is running at a different origin than where the scripts are loaded from - const workerBaseUrl = workerMain.substr(0, workerMain.length - 'vs/base/worker/workerMain.js'.length); - const js = `/*${label}*/self.MonacoEnvironment={baseUrl: '${workerBaseUrl}'};importScripts('${workerMain}');/*${label}*/`; - const url = `data:text/javascript;charset=utf-8,${encodeURIComponent(js)}`; - return new Worker(url); - } - } - return new Worker(workerMain + '#' + label); + const workerUrl = getWorkerBootstrapUrl(workerMain, label); + return new Worker(workerUrl, { name: label }); } // ESM-comment-end throw new Error(`You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker`); } +export function getWorkerBootstrapUrl(scriptPath: string, label: string): string { + if (/^(http:)|(https:)|(file:)/.test(scriptPath)) { + const currentUrl = String(window.location); + const currentOrigin = currentUrl.substr(0, currentUrl.length - window.location.hash.length - window.location.search.length - window.location.pathname.length); + if (scriptPath.substring(0, currentOrigin.length) !== currentOrigin) { + // this is the cross-origin case + // i.e. the webpage is running at a different origin than where the scripts are loaded from + const myPath = 'vs/base/worker/defaultWorkerFactory.js'; + const workerBaseUrl = require.toUrl(myPath).slice(0, -myPath.length); + const js = `/*${label}*/self.MonacoEnvironment={baseUrl: '${workerBaseUrl}'};importScripts('${scriptPath}');/*${label}*/`; + const url = `data:text/javascript;charset=utf-8,${encodeURIComponent(js)}`; + return url; + } + } + return scriptPath + '#' + label; +} + function isPromiseLike(obj: any): obj is PromiseLike { if (typeof obj.then === 'function') { return true; diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index 1f04364fa5d11..5794b25906167 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; +import { getWorkerBootstrapUrl } from 'vs/base/worker/defaultWorkerFactory'; import { Emitter, Event } from 'vs/base/common/event'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; @@ -49,23 +49,29 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { if (!this._protocol) { const emitter = new Emitter(); - const worker = new DefaultWorkerFactory('WorkerExtensionHost').create( - 'vs/workbench/services/extensions/worker/extensionHostWorker', data => { - if (data instanceof ArrayBuffer) { - emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); - } else { - console.warn('UNKNOWN data received', data); - this._onDidExit.fire([77, 'UNKNOWN data received']); - } - }, err => { - this._onDidExit.fire([81, err]); - console.error(err); + + const url = getWorkerBootstrapUrl(require.toUrl('../worker/extensionHostWorkerMain.js'), 'WorkerExtensionHost'); + const worker = new Worker(url); + + worker.onmessage = (event) => { + const { data } = event; + if (!(data instanceof ArrayBuffer)) { + console.warn('UNKNOWN data received', data); + this._onDidExit.fire([77, 'UNKNOWN data received']); + return; } - ); + + emitter.fire(VSBuffer.wrap(new Uint8Array(data, 0, data.byteLength))); + }; + + worker.onerror = (event) => { + console.error(event.error); + this._onDidExit.fire([81, event.error]); + }; // keep for cleanup this._toDispose.add(emitter); - this._toDispose.add(worker); + this._toDispose.add(toDisposable(() => worker.terminate())); const protocol: IMessagePassingProtocol = { onMessage: emitter.event, diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts index 3ed289be3977a..34e3f35a624bf 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter } from 'vs/base/common/event'; @@ -38,21 +37,18 @@ const hostUtil = new class implements IHostUtils { //todo@joh do not allow extensions to call postMessage and other globals... -class ExtensionWorker implements IRequestHandler { - - // worker-contract - readonly _requestHandlerBrand: any; - readonly onmessage: (data: any) => any; +class ExtensionWorker { // protocol readonly protocol: IMessagePassingProtocol; - constructor(postMessage: (message: any, transfer?: Transferable[]) => any) { + constructor() { let emitter = new Emitter(); let terminating = false; - this.onmessage = data => { + onmessage = event => { + const { data } = event; if (!(data instanceof ArrayBuffer)) { console.warn('UNKNOWN data received', data); return; @@ -98,8 +94,8 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise any): IRequestHandler { - const res = new ExtensionWorker(postMessage); +(function create(): void { + const res = new ExtensionWorker(); connectToRenderer(res.protocol).then(data => { @@ -112,6 +108,4 @@ export function create(postMessage: (message: any, transfer?: Transferable[]) => onTerminate = () => extHostMain.terminate(); }); - - return res; -} +})(); diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts new file mode 100644 index 0000000000000..79455414c06b9 --- /dev/null +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +(function () { + + let MonacoEnvironment = (self).MonacoEnvironment; + let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../../../'; + + if (typeof (self).define !== 'function' || !(self).define.amd) { + importScripts(monacoBaseUrl + 'vs/loader.js'); + } + + require.config({ + baseUrl: monacoBaseUrl, + catchError: true + }); + + require(['vs/workbench/services/extensions/worker/extensionHostWorker'], () => { }, err => console.error(err)); +})(); From 16051c7a41ee3923a01f30604805e925eae717fc Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 14 Aug 2019 17:55:16 +0200 Subject: [PATCH 170/613] - use strings for view zone ids - make it unlikely that a new view instance would accept a view zone id from a previous view instance - remove that the find widget removes a view zone id from another view (fixes #71745) --- src/vs/base/common/strings.ts | 15 ++++++ .../editor/browser/controller/mouseHandler.ts | 2 +- .../editor/browser/controller/mouseTarget.ts | 2 +- src/vs/editor/browser/editorBrowser.ts | 6 +-- src/vs/editor/browser/view/viewImpl.ts | 8 +-- .../browser/viewParts/viewZones/viewZones.ts | 34 ++++++------ .../editor/browser/widget/diffEditorWidget.ts | 2 +- src/vs/editor/common/model/textModel.ts | 17 +----- .../editor/common/viewLayout/linesLayout.ts | 6 +-- src/vs/editor/common/viewLayout/viewLayout.ts | 6 +-- .../common/viewLayout/whitespaceComputer.ts | 53 +++++++++---------- src/vs/editor/common/viewModel/viewModel.ts | 8 +-- .../editor/contrib/codelens/codelensWidget.ts | 2 +- src/vs/editor/contrib/find/findWidget.ts | 12 +---- .../editor/contrib/zoneWidget/zoneWidget.ts | 2 +- src/vs/monaco.d.ts | 6 +-- .../api/browser/mainThreadCodeInsets.ts | 2 +- .../preferences/browser/preferencesWidgets.ts | 4 +- 18 files changed, 88 insertions(+), 99 deletions(-) diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index d4397d3279de5..71185d0704613 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -729,3 +729,18 @@ export function getNLines(str: string, n = 1): string { str.substr(0, idx) : str; } + +/** + * Produces 'a'-'z', followed by 'A'-'Z'... followed by 'a'-'z', etc. + */ +export function singleLetterHash(n: number): string { + const LETTERS_CNT = (CharCode.Z - CharCode.A + 1); + + n = n % (2 * LETTERS_CNT); + + if (n < LETTERS_CNT) { + return String.fromCharCode(CharCode.a + n); + } + + return String.fromCharCode(CharCode.A + n - LETTERS_CNT); +} diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 06d4b2f5307ad..418e8ebee98ea 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -49,7 +49,7 @@ export interface IPointerHandlerHelper { */ getLastViewCursorsRenderData(): IViewCursorRenderData[]; - shouldSuppressMouseDownOnViewZone(viewZoneId: number): boolean; + shouldSuppressMouseDownOnViewZone(viewZoneId: string): boolean; shouldSuppressMouseDownOnWidget(widgetId: string): boolean; /** diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 1f4c6ff52c726..f510e0f6f82ad 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -19,7 +19,7 @@ import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; export interface IViewZoneData { - viewZoneId: number; + viewZoneId: string; positionBefore: Position | null; positionAfter: Position | null; position: Position; diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 705b6def023dd..91e6ea3864b27 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -83,17 +83,17 @@ export interface IViewZoneChangeAccessor { * @param zone Zone to create * @return A unique identifier to the view zone. */ - addZone(zone: IViewZone): number; + addZone(zone: IViewZone): string; /** * Remove a zone * @param id A unique identifier to the view zone, as returned by the `addZone` call. */ - removeZone(id: number): void; + removeZone(id: string): void; /** * Change a zone's position. * The editor will rescan the `afterLineNumber` and `afterColumn` properties of a view zone. */ - layoutZone(id: number): void; + layoutZone(id: string): void; } /** diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index a476c33912a4c..45c711ceceff8 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -248,7 +248,7 @@ export class View extends ViewEventHandler { getLastViewCursorsRenderData: () => { return this.viewCursors.getLastRenderData() || []; }, - shouldSuppressMouseDownOnViewZone: (viewZoneId: number) => { + shouldSuppressMouseDownOnViewZone: (viewZoneId: string) => { return this.viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId); }, shouldSuppressMouseDownOnWidget: (widgetId: string) => { @@ -473,17 +473,17 @@ export class View extends ViewEventHandler { this._renderOnce(() => { const changeAccessor: editorBrowser.IViewZoneChangeAccessor = { - addZone: (zone: editorBrowser.IViewZone): number => { + addZone: (zone: editorBrowser.IViewZone): string => { zonesHaveChanged = true; return this.viewZones.addZone(zone); }, - removeZone: (id: number): void => { + removeZone: (id: string): void => { if (!id) { return; } zonesHaveChanged = this.viewZones.removeZone(id) || zonesHaveChanged; }, - layoutZone: (id: number): void => { + layoutZone: (id: string): void => { if (!id) { return; } diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index a51fbe5bf0285..6c01df4e0b6fc 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -14,7 +14,7 @@ import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; export interface IMyViewZone { - whitespaceId: number; + whitespaceId: string; delegate: IViewZone; isVisible: boolean; domNode: FastDomNode; @@ -74,7 +74,7 @@ export class ViewZones extends ViewPart { const id = keys[i]; const zone = this._zones[id]; const props = this._computeWhitespaceProps(zone.delegate); - if (this._context.viewLayout.changeWhitespace(parseInt(id, 10), props.afterViewLineNumber, props.heightInPx)) { + if (this._context.viewLayout.changeWhitespace(id, props.afterViewLineNumber, props.heightInPx)) { this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); hadAChange = true; } @@ -183,7 +183,7 @@ export class ViewZones extends ViewPart { }; } - public addZone(zone: IViewZone): number { + public addZone(zone: IViewZone): string { const props = this._computeWhitespaceProps(zone); const whitespaceId = this._context.viewLayout.addWhitespace(props.afterViewLineNumber, this._getZoneOrdinal(zone), props.heightInPx, props.minWidthInPx); @@ -200,18 +200,18 @@ export class ViewZones extends ViewPart { myZone.domNode.setPosition('absolute'); myZone.domNode.domNode.style.width = '100%'; myZone.domNode.setDisplay('none'); - myZone.domNode.setAttribute('monaco-view-zone', myZone.whitespaceId.toString()); + myZone.domNode.setAttribute('monaco-view-zone', myZone.whitespaceId); this.domNode.appendChild(myZone.domNode); if (myZone.marginDomNode) { myZone.marginDomNode.setPosition('absolute'); myZone.marginDomNode.domNode.style.width = '100%'; myZone.marginDomNode.setDisplay('none'); - myZone.marginDomNode.setAttribute('monaco-view-zone', myZone.whitespaceId.toString()); + myZone.marginDomNode.setAttribute('monaco-view-zone', myZone.whitespaceId); this.marginDomNode.appendChild(myZone.marginDomNode); } - this._zones[myZone.whitespaceId.toString()] = myZone; + this._zones[myZone.whitespaceId] = myZone; this.setShouldRender(); @@ -219,10 +219,10 @@ export class ViewZones extends ViewPart { return myZone.whitespaceId; } - public removeZone(id: number): boolean { - if (this._zones.hasOwnProperty(id.toString())) { - const zone = this._zones[id.toString()]; - delete this._zones[id.toString()]; + public removeZone(id: string): boolean { + if (this._zones.hasOwnProperty(id)) { + const zone = this._zones[id]; + delete this._zones[id]; this._context.viewLayout.removeWhitespace(zone.whitespaceId); zone.domNode.removeAttribute('monaco-visible-view-zone'); @@ -242,10 +242,10 @@ export class ViewZones extends ViewPart { return false; } - public layoutZone(id: number): boolean { + public layoutZone(id: string): boolean { let changed = false; - if (this._zones.hasOwnProperty(id.toString())) { - const zone = this._zones[id.toString()]; + if (this._zones.hasOwnProperty(id)) { + const zone = this._zones[id]; const props = this._computeWhitespaceProps(zone.delegate); // const newOrdinal = this._getZoneOrdinal(zone.delegate); changed = this._context.viewLayout.changeWhitespace(zone.whitespaceId, props.afterViewLineNumber, props.heightInPx) || changed; @@ -259,9 +259,9 @@ export class ViewZones extends ViewPart { return changed; } - public shouldSuppressMouseDownOnViewZone(id: number): boolean { - if (this._zones.hasOwnProperty(id.toString())) { - const zone = this._zones[id.toString()]; + public shouldSuppressMouseDownOnViewZone(id: string): boolean { + if (this._zones.hasOwnProperty(id)) { + const zone = this._zones[id]; return Boolean(zone.delegate.suppressMouseDown); } return false; @@ -314,7 +314,7 @@ export class ViewZones extends ViewPart { let hasVisibleZone = false; for (let i = 0, len = visibleWhitespaces.length; i < len; i++) { - visibleZones[visibleWhitespaces[i].id.toString()] = visibleWhitespaces[i]; + visibleZones[visibleWhitespaces[i].id] = visibleWhitespaces[i]; hasVisibleZone = true; } diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 260f479df551e..ee32431b77a8a 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -69,7 +69,7 @@ interface IDiffEditorWidgetStyle { } class VisualEditorState { - private _zones: number[]; + private _zones: string[]; private _zonesMap: { [zoneId: string]: boolean; }; private _decorations: string[]; diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 874d7428cf3c4..0469471f8c4c4 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -110,21 +110,6 @@ export function createTextBuffer(value: string | model.ITextBufferFactory, defau let MODEL_ID = 0; -/** - * Produces 'a'-'z', followed by 'A'-'Z'... followed by 'a'-'z', etc. - */ -function singleLetter(result: number): string { - const LETTERS_CNT = (CharCode.Z - CharCode.A + 1); - - result = result % (2 * LETTERS_CNT); - - if (result < LETTERS_CNT) { - return String.fromCharCode(CharCode.a + result); - } - - return String.fromCharCode(CharCode.A + result - LETTERS_CNT); -} - const LIMIT_FIND_COUNT = 999; export const LONG_LINE_BOUNDARY = 10000; @@ -343,7 +328,7 @@ export class TextModel extends Disposable implements model.ITextModel { } }); - this._instanceId = singleLetter(MODEL_ID); + this._instanceId = strings.singleLetterHash(MODEL_ID); this._lastDecorationId = 0; this._decorations = Object.create(null); this._decorationsTree = new DecorationsTrees(); diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index 2967fb3b003f3..d6673ac8b8fbe 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -63,14 +63,14 @@ export class LinesLayout { * @param heightInPx The height of the whitespace, in pixels. * @return An id that can be used later to mutate or delete the whitespace */ - public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): number { + public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { return this._whitespaces.insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); } /** * Change properties associated with a certain whitespace. */ - public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean { + public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { return this._whitespaces.changeWhitespace(id, newAfterLineNumber, newHeight); } @@ -80,7 +80,7 @@ export class LinesLayout { * @param id The whitespace to remove * @return Returns true if the whitespace is found and it is removed. */ - public removeWhitespace(id: number): boolean { + public removeWhitespace(id: string): boolean { return this._whitespaces.removeWhitespace(id); } diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index f753f65d393b3..ad24479b4b2a9 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -173,13 +173,13 @@ export class ViewLayout extends Disposable implements IViewLayout { // ---- IVerticalLayoutProvider - public addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): number { + public addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): string { return this._linesLayout.insertWhitespace(afterLineNumber, ordinal, height, minWidth); } - public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean { + public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { return this._linesLayout.changeWhitespace(id, newAfterLineNumber, newHeight); } - public removeWhitespace(id: number): boolean { + public removeWhitespace(id: string): boolean { return this._linesLayout.removeWhitespace(id); } public getVerticalOffsetForLineNumber(lineNumber: number): number { diff --git a/src/vs/editor/common/viewLayout/whitespaceComputer.ts b/src/vs/editor/common/viewLayout/whitespaceComputer.ts index 537c36ac80740..8e5dd347e8306 100644 --- a/src/vs/editor/common/viewLayout/whitespaceComputer.ts +++ b/src/vs/editor/common/viewLayout/whitespaceComputer.ts @@ -3,8 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as strings from 'vs/base/common/strings'; + export interface IEditorWhitespace { - readonly id: number; + readonly id: string; readonly afterLineNumber: number; readonly heightInLines: number; } @@ -15,6 +17,10 @@ export interface IEditorWhitespace { */ export class WhitespaceComputer { + private static INSTANCE_COUNT = 0; + + private readonly _instanceId: string; + /** * heights[i] is the height in pixels for whitespace at index i */ @@ -48,7 +54,7 @@ export class WhitespaceComputer { /** * ids[i] is the whitespace id of whitespace at index i */ - private readonly _ids: number[]; + private readonly _ids: string[]; /** * index at which a whitespace is positioned (inside heights, afterLineNumbers, prefixSum members) @@ -65,6 +71,7 @@ export class WhitespaceComputer { private _minWidth: number; constructor() { + this._instanceId = strings.singleLetterHash(++WhitespaceComputer.INSTANCE_COUNT); this._heights = []; this._minWidths = []; this._ids = []; @@ -113,21 +120,20 @@ export class WhitespaceComputer { * @param heightInPx The height of the whitespace, in pixels. * @return An id that can be used later to mutate or delete the whitespace */ - public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): number { + public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { afterLineNumber = afterLineNumber | 0; ordinal = ordinal | 0; heightInPx = heightInPx | 0; minWidth = minWidth | 0; - let id = (++this._lastWhitespaceId); + let id = this._instanceId + (++this._lastWhitespaceId); let insertionIndex = WhitespaceComputer.findInsertionIndex(this._afterLineNumbers, afterLineNumber, this._ordinals, ordinal); this._insertWhitespaceAtIndex(id, insertionIndex, afterLineNumber, ordinal, heightInPx, minWidth); this._minWidth = -1; /* marker for not being computed */ return id; } - private _insertWhitespaceAtIndex(id: number, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): void { - id = id | 0; + private _insertWhitespaceAtIndex(id: string, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): void { insertIndex = insertIndex | 0; afterLineNumber = afterLineNumber | 0; ordinal = ordinal | 0; @@ -150,15 +156,14 @@ export class WhitespaceComputer { } } - this._whitespaceId2Index[id.toString()] = insertIndex; + this._whitespaceId2Index[id] = insertIndex; this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1); } /** * Change properties associated with a certain whitespace. */ - public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean { - id = id | 0; + public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { newAfterLineNumber = newAfterLineNumber | 0; newHeight = newHeight | 0; @@ -175,13 +180,11 @@ export class WhitespaceComputer { * @param newHeightInPx The new height of the whitespace, in pixels * @return Returns true if the whitespace is found and if the new height is different than the old height */ - public changeWhitespaceHeight(id: number, newHeightInPx: number): boolean { - id = id | 0; + public changeWhitespaceHeight(id: string, newHeightInPx: number): boolean { newHeightInPx = newHeightInPx | 0; - let sid = id.toString(); - if (this._whitespaceId2Index.hasOwnProperty(sid)) { - let index = this._whitespaceId2Index[sid]; + if (this._whitespaceId2Index.hasOwnProperty(id)) { + let index = this._whitespaceId2Index[id]; if (this._heights[index] !== newHeightInPx) { this._heights[index] = newHeightInPx; this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1); @@ -198,13 +201,11 @@ export class WhitespaceComputer { * @param newAfterLineNumber The new line number the whitespace will follow * @return Returns true if the whitespace is found and if the new line number is different than the old line number */ - public changeWhitespaceAfterLineNumber(id: number, newAfterLineNumber: number): boolean { - id = id | 0; + public changeWhitespaceAfterLineNumber(id: string, newAfterLineNumber: number): boolean { newAfterLineNumber = newAfterLineNumber | 0; - let sid = id.toString(); - if (this._whitespaceId2Index.hasOwnProperty(sid)) { - let index = this._whitespaceId2Index[sid]; + if (this._whitespaceId2Index.hasOwnProperty(id)) { + let index = this._whitespaceId2Index[id]; if (this._afterLineNumbers[index] !== newAfterLineNumber) { // `afterLineNumber` changed for this whitespace @@ -236,14 +237,10 @@ export class WhitespaceComputer { * @param id The whitespace to remove * @return Returns true if the whitespace is found and it is removed. */ - public removeWhitespace(id: number): boolean { - id = id | 0; - - let sid = id.toString(); - - if (this._whitespaceId2Index.hasOwnProperty(sid)) { - let index = this._whitespaceId2Index[sid]; - delete this._whitespaceId2Index[sid]; + public removeWhitespace(id: string): boolean { + if (this._whitespaceId2Index.hasOwnProperty(id)) { + let index = this._whitespaceId2Index[id]; + delete this._whitespaceId2Index[id]; this._removeWhitespaceAtIndex(index); this._minWidth = -1; /* marker for not being computed */ return true; @@ -459,7 +456,7 @@ export class WhitespaceComputer { * @param index The index of the whitespace. * @return `id` of whitespace at `index`. */ - public getIdForWhitespaceIndex(index: number): number { + public getIdForWhitespaceIndex(index: number): string { index = index | 0; return this._ids[index]; diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index fe832ef3707df..0f16d0a84a299 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -17,7 +17,7 @@ import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceCompute import { ITheme } from 'vs/platform/theme/common/themeService'; export interface IViewWhitespaceViewportData { - readonly id: number; + readonly id: string; readonly afterLineNumber: number; readonly verticalOffset: number; readonly height: number; @@ -74,15 +74,15 @@ export interface IViewLayout { * Reserve rendering space. * @return an identifier that can be later used to remove or change the whitespace. */ - addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): number; + addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): string; /** * Change the properties of a whitespace. */ - changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean; + changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean; /** * Remove rendering space */ - removeWhitespace(id: number): boolean; + removeWhitespace(id: string): boolean; /** * Get the layout information for whitespaces currently in the viewport */ diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index cb41dc9f45930..5ae20b27edb7a 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -193,7 +193,7 @@ export class CodeLensWidget { private readonly _editor: editorBrowser.ICodeEditor; private readonly _viewZone!: CodeLensViewZone; - private readonly _viewZoneId!: number; + private readonly _viewZoneId!: string; private readonly _contentWidget!: CodeLensContentWidget; private _decorationIds: string[]; private _data: CodeLensItem[]; diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 529d582ee47a7..a25d3b5b46432 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -116,7 +116,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private readonly _replaceFocusTracker: dom.IFocusTracker; private readonly _replaceInputFocused: IContextKey; private _viewZone?: FindWidgetViewZone; - private _viewZoneId?: number; + private _viewZoneId?: string; private _resizeSash!: Sash; private _resized!: boolean; @@ -224,15 +224,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (!this._isVisible) { return; } - if (this._viewZoneId === undefined) { - return; - } - this._codeEditor.changeViewZones((accessor) => { - if (this._viewZoneId) { - accessor.removeZone(this._viewZoneId); - } - this._viewZoneId = undefined; - }); + this._viewZoneId = undefined; })); diff --git a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts index 2e7073d55a696..3e0fe0a7d66cb 100644 --- a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts @@ -51,7 +51,7 @@ const WIDGET_ID = 'vs.editor.contrib.zoneWidget'; export class ViewZoneDelegate implements IViewZone { public domNode: HTMLElement; - public id: number = 0; // A valid zone id should be greater than 0 + public id: string = ''; // A valid zone id should be greater than 0 public afterLineNumber: number; public afterColumn: number; public heightInLines: number; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index a797b72a6f2e4..b44b3ae08ad3b 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3605,17 +3605,17 @@ declare namespace monaco.editor { * @param zone Zone to create * @return A unique identifier to the view zone. */ - addZone(zone: IViewZone): number; + addZone(zone: IViewZone): string; /** * Remove a zone * @param id A unique identifier to the view zone, as returned by the `addZone` call. */ - removeZone(id: number): void; + removeZone(id: string): void; /** * Change a zone's position. * The editor will rescan the `afterLineNumber` and `afterColumn` properties of a view zone. */ - layoutZone(id: number): void; + layoutZone(id: string): void; } /** diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index 1e23e14ce69ca..2e9ea44a2331f 100644 --- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -21,7 +21,7 @@ class EditorWebviewZone implements IViewZone { readonly afterColumn: number; readonly heightInLines: number; - private _id?: number; + private _id?: string; // suppressMouseDown?: boolean | undefined; // heightInPx?: number | undefined; // minWidthInPx?: number | undefined; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 7eeb9c15120f5..66678cbfffb4c 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -36,7 +36,7 @@ import { ISettingsGroup } from 'vs/workbench/services/preferences/common/prefere export class SettingsHeaderWidget extends Widget implements IViewZone { - private id: number; + private id: string; private _domNode: HTMLElement; protected titleContainer: HTMLElement; @@ -121,7 +121,7 @@ export class DefaultSettingsHeaderWidget extends SettingsHeaderWidget { export class SettingsGroupTitleWidget extends Widget implements IViewZone { - private id: number; + private id: string; private _afterLineNumber: number; private _domNode: HTMLElement; From a42484093f22c7ddb93c2a48d2df33cb7dc0791e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 14 Aug 2019 18:02:24 +0200 Subject: [PATCH 171/613] add `extensionHostWorker` entry point, fixes https://github.com/microsoft/vscode-internalbacklog/issues/738 --- build/gulpfile.vscode.web.js | 3 ++- src/buildfile.js | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index 11da75e482ddf..81e08f8e0a659 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -53,6 +53,7 @@ const buildfile = require('../src/buildfile'); const vscodeWebEntryPoints = [ buildfile.workbenchWeb, buildfile.serviceWorker, + buildfile.workerExtensionHost, buildfile.keyboardMaps, buildfile.base ]; @@ -148,4 +149,4 @@ const dashed = (str) => (str ? `-${str}` : ``); vscodeWebTaskCI )); gulp.task(vscodeWebTask); -}); \ No newline at end of file +}); diff --git a/src/buildfile.js b/src/buildfile.js index f9946efc7038b..43fcfa807c35f 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -function entrypoint (name) { +function entrypoint(name) { return [{ name: name, include: [], exclude: ['vs/css', 'vs/nls'] }]; } @@ -23,6 +23,14 @@ exports.serviceWorker = [{ dest: 'vs/workbench/contrib/resources/browser/resourceServiceWorkerMain.js' }]; +exports.workerExtensionHost = [{ + name: 'vs/workbench/services/extensions/worker/extensionHostWorker', + // include: [], + prepend: ['vs/loader.js'], + append: ['vs/workbench/services/extensions/worker/extensionHostWorkerMain'], + dest: 'vs/workbench/services/extensions/worker/extensionHostWorkerMain.js' +}]; + exports.workbench = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.desktop.main']); exports.workbenchWeb = entrypoint('vs/workbench/workbench.web.api'); From e677c03899d62a5814637548b904c5ac75de4a6c Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 14 Aug 2019 16:25:31 +0200 Subject: [PATCH 172/613] Update uglify-es (#79044) --- package.json | 4 ++-- yarn.lock | 25 ++++++++++--------------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index cf5ff6a7afb9b..eaa647b89173c 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "gulp-shell": "^0.6.5", "gulp-tsb": "2.0.7", "gulp-tslint": "^8.1.3", - "gulp-uglify": "^3.0.0", + "gulp-uglify": "^3.0.2", "gulp-untar": "^0.0.7", "gulp-vinyl-zip": "^2.1.2", "http-server": "^0.11.1", @@ -133,7 +133,7 @@ "tslint": "^5.16.0", "typescript": "3.5.2", "typescript-formatter": "7.1.0", - "uglify-es": "^3.0.18", + "uglify-es": "^3.3.9", "underscore": "^1.8.2", "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index 0a6b395f80dd2..8fabb92cfc06e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4047,15 +4047,18 @@ gulp-tslint@^8.1.3: plugin-error "1.0.1" through "~2.3.8" -gulp-uglify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/gulp-uglify/-/gulp-uglify-3.0.0.tgz#0df0331d72a0d302e3e37e109485dddf33c6d1ca" - integrity sha1-DfAzHXKg0wLj434QlIXd3zPG0co= +gulp-uglify@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gulp-uglify/-/gulp-uglify-3.0.2.tgz#5f5b2e8337f879ca9dec971feb1b82a5a87850b0" + integrity sha512-gk1dhB74AkV2kzqPMQBLA3jPoIAPd/nlNzP2XMDSG8XZrqnlCiDGAqC+rZOumzFvB5zOphlFh6yr3lgcAb/OOg== dependencies: + array-each "^1.0.1" + extend-shallow "^3.0.2" gulplog "^1.0.0" has-gulplog "^0.1.0" - lodash "^4.13.1" + isobject "^3.0.1" make-error-cause "^1.1.1" + safe-buffer "^5.1.2" through2 "^2.0.0" uglify-js "^3.0.5" vinyl-sourcemaps-apply "^0.2.0" @@ -5455,7 +5458,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.13.1, lodash@^4.15.0, lodash@^4.3.0: +lodash@^4.15.0, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4= @@ -9133,15 +9136,7 @@ uc.micro@^1.0.1, uc.micro@^1.0.3: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" integrity sha1-ftUNXg+an7ClczeSWfKndFjVAZI= -uglify-es@^3.0.18: - version "3.1.9" - resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.1.9.tgz#6c82df628ac9eb7af9c61fd70c744a084abe6161" - integrity sha512-wVSiJKHDgDDFmxTVVvnbAH6IpamAFHYDI+5JvwPdaqIMnk8kRTX2JKwq1Fx7gb2+Jj5Dus8kzvIpKkWOMNU51w== - dependencies: - commander "~2.11.0" - source-map "~0.6.1" - -uglify-es@^3.3.4: +uglify-es@^3.3.4, uglify-es@^3.3.9: version "3.3.9" resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== From 38cdba85df5dd29eb07a218f57bd7e609ee4d2cf Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 14 Aug 2019 16:26:21 +0200 Subject: [PATCH 173/613] Revert "Work around minifier bug (#79044)" This reverts commit 6371cad57381ead9cb744393501ae749f1a8ac40. --- .../services/extensions/node/proxyResolver.ts | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 12a6e3ea66a37..64c2e0a526e59 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -469,26 +469,24 @@ async function readCaCertificates() { } async function readWindowsCaCertificates() { - // Not using await to work around minifier bug (https://github.com/microsoft/vscode/issues/79044). - return import('vscode-windows-ca-certs') - .then(winCA => { - let ders: any[] = []; - const store = winCA(); - try { - let der: any; - while (der = store.next()) { - ders.push(der); - } - } finally { - store.done(); - } + const winCA = await import('vscode-windows-ca-certs'); + + let ders: any[] = []; + const store = winCA(); + try { + let der: any; + while (der = store.next()) { + ders.push(der); + } + } finally { + store.done(); + } - const certs = new Set(ders.map(derToPem)); - return { - certs: Array.from(certs), - append: true - }; - }); + const certs = new Set(ders.map(derToPem)); + return { + certs: Array.from(certs), + append: true + }; } async function readMacCaCertificates() { From e0a685e5854fb9b77e439611cef0af327eac6936 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 14 Aug 2019 18:21:47 +0200 Subject: [PATCH 174/613] expose product configuration in product service --- .../common/extensionGalleryService.ts | 16 ++++---- .../product/browser/productService.ts | 39 ++++--------------- src/vs/platform/product/common/product.ts | 33 +--------------- .../platform/product/node/productService.ts | 33 ++++------------ .../api/browser/mainThreadWebview.ts | 2 +- .../workbench/browser/web.simpleservices.ts | 8 ++-- .../contrib/debug/browser/debugSession.ts | 2 +- .../experiments/common/experimentService.ts | 4 +- .../extensions/browser/extensionsActions.ts | 4 +- .../browser/extensionsWorkbenchService.ts | 8 ++-- .../contrib/feedback/browser/feedback.ts | 4 +- .../feedback/browser/feedbackStatusbarItem.ts | 2 +- .../preferences/browser/preferencesSearch.ts | 6 +-- .../browser/terminalProcessManager.ts | 2 +- .../browser/webWorkerExtensionHostStarter.ts | 8 ++-- .../common/abstractExtensionService.ts | 6 +-- .../extensions/common/extensionsUtil.ts | 2 +- .../common/remoteExtensionHostClient.ts | 10 ++--- .../remoteExtensionManagementIpc.ts | 4 +- .../remote/browser/remoteAgentServiceImpl.ts | 2 +- .../telemetry/browser/telemetryService.ts | 6 +-- .../electron-browser/telemetryService.ts | 6 +-- 22 files changed, 67 insertions(+), 140 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index e291d4361155d..42fbff47fe24f 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -342,10 +342,10 @@ export class ExtensionGalleryService implements IExtensionGalleryService { @IProductService private readonly productService: IProductService, @optional(IStorageService) private readonly storageService: IStorageService, ) { - const config = productService.extensionsGallery; + const config = productService.productConfiguration.extensionsGallery; this.extensionsGalleryUrl = config && config.serviceUrl; this.extensionsControlUrl = config && config.controlUrl; - this.commonHeadersPromise = resolveMarketplaceHeaders(productService.version, this.environmentService, this.fileService, this.storageService); + this.commonHeadersPromise = resolveMarketplaceHeaders(productService.productConfiguration.version, this.environmentService, this.fileService, this.storageService); } private api(path = ''): string { @@ -358,7 +358,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { getCompatibleExtension(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise { const extension: IGalleryExtension | null = isIExtensionIdentifier(arg1) ? null : arg1; - if (extension && extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version)) { + if (extension && extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.productConfiguration.version)) { return Promise.resolve(extension); } const { id, uuid } = extension ? extension.identifier : arg1; @@ -384,7 +384,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.version)) { + if (extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.productConfiguration.version)) { return extension; } } @@ -619,7 +619,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.version) ? v : null))) + return Promise.all(galleryExtensions[0].versions.map(v => this.getEngine(v).then(engine => isEngineValid(engine, this.productService.productConfiguration.version) ? v : null))) .then(versions => versions .filter(v => !!v) .map(v => ({ version: v!.version, date: v!.lastUpdated }))); @@ -705,7 +705,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { if (!engine) { return null; } - if (isEngineValid(engine, this.productService.version)) { + if (isEngineValid(engine, this.productService.productConfiguration.version)) { return Promise.resolve(version); } } @@ -737,7 +737,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { const version = versions[0]; return this.getEngine(version) .then(engine => { - if (!isEngineValid(engine, this.productService.version)) { + if (!isEngineValid(engine, this.productService.productConfiguration.version)) { return this.getLastValidExtensionVersionRecursively(extension, versions.slice(1)); } @@ -817,4 +817,4 @@ export async function resolveMarketplaceHeaders(version: string, environmentServ return headers; -} \ No newline at end of file +} diff --git a/src/vs/platform/product/browser/productService.ts b/src/vs/platform/product/browser/productService.ts index 70bc0b31bcc14..fae6e1d5fa7c0 100644 --- a/src/vs/platform/product/browser/productService.ts +++ b/src/vs/platform/product/browser/productService.ts @@ -10,40 +10,17 @@ export class ProductService implements IProductService { _serviceBrand!: ServiceIdentifier; - private readonly productConfiguration: IProductConfiguration | null; + readonly productConfiguration: IProductConfiguration; constructor() { const element = document.getElementById('vscode-remote-product-configuration'); - this.productConfiguration = element ? JSON.parse(element.getAttribute('data-settings')!) : null; + this.productConfiguration = { + ...element ? JSON.parse(element.getAttribute('data-settings')!) : { + version: '1.38.0-unknown', + nameLong: 'Unknown', + extensionAllowedProposedApi: [], + }, ...{ urlProtocol: '', enableTelemetry: false } + }; } - get version(): string { return this.productConfiguration && this.productConfiguration.version ? this.productConfiguration.version : '1.38.0-unknown'; } - - get commit(): string | undefined { return this.productConfiguration ? this.productConfiguration.commit : undefined; } - - get nameLong(): string { return this.productConfiguration ? this.productConfiguration.nameLong : 'Unknown'; } - - get urlProtocol(): string { return ''; } - - get extensionAllowedProposedApi(): readonly string[] { return this.productConfiguration ? this.productConfiguration.extensionAllowedProposedApi : []; } - - get uiExtensions(): readonly string[] | undefined { return this.productConfiguration ? this.productConfiguration.uiExtensions : undefined; } - - get enableTelemetry(): boolean { return false; } - - get sendASmile(): { reportIssueUrl: string, requestFeatureUrl: string } | undefined { return this.productConfiguration ? this.productConfiguration.sendASmile : undefined; } - - get extensionsGallery() { return this.productConfiguration ? this.productConfiguration.extensionsGallery : undefined; } - - get settingsSearchBuildId(): number | undefined { return this.productConfiguration ? this.productConfiguration.settingsSearchBuildId : undefined; } - - get settingsSearchUrl(): string | undefined { return this.productConfiguration ? this.productConfiguration.settingsSearchUrl : undefined; } - - get experimentsUrl(): string | undefined { return this.productConfiguration ? this.productConfiguration.experimentsUrl : undefined; } - - get extensionKeywords(): { [extension: string]: readonly string[]; } | undefined { return this.productConfiguration ? this.productConfiguration.extensionKeywords : undefined; } - - get extensionAllowedBadgeProviders(): readonly string[] | undefined { return this.productConfiguration ? this.productConfiguration.extensionAllowedBadgeProviders : undefined; } - - get aiConfig() { return this.productConfiguration ? this.productConfiguration.aiConfig : undefined; } } diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 05048ee0908a3..11b6fed267a93 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -11,38 +11,7 @@ export interface IProductService { _serviceBrand: ServiceIdentifier; - readonly version: string; - readonly commit?: string; - readonly date?: string; - - readonly nameLong: string; - readonly urlProtocol: string; - readonly extensionAllowedProposedApi: readonly string[]; - readonly uiExtensions?: readonly string[]; - - readonly enableTelemetry: boolean; - readonly extensionsGallery?: { - readonly serviceUrl: string; - readonly itemUrl: string; - readonly controlUrl: string; - readonly recommendationsUrl: string; - }; - - readonly sendASmile?: { - readonly reportIssueUrl: string; - readonly requestFeatureUrl: string; - }; - - readonly settingsSearchBuildId?: number; - readonly settingsSearchUrl?: string; - - readonly experimentsUrl?: string; - readonly extensionKeywords?: { [extension: string]: readonly string[]; }; - readonly extensionAllowedBadgeProviders?: readonly string[]; - - readonly aiConfig?: { - readonly asimovKey: string; - }; + readonly productConfiguration: IProductConfiguration; } export interface IProductConfiguration { diff --git a/src/vs/platform/product/node/productService.ts b/src/vs/platform/product/node/productService.ts index ba9c84a551db5..f3986577355c8 100644 --- a/src/vs/platform/product/node/productService.ts +++ b/src/vs/platform/product/node/productService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IProductService } from 'vs/platform/product/common/product'; +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'; @@ -12,31 +12,12 @@ export class ProductService implements IProductService { _serviceBrand!: ServiceIdentifier; - get version(): string { return pkg.version; } + readonly productConfiguration: IProductConfiguration; - get commit(): string | undefined { return product.commit; } + constructor() { + this.productConfiguration = { + ...product, ...{ version: pkg.version } + }; + } - get nameLong(): string { return product.nameLong; } - - get urlProtocol(): string { return product.urlProtocol; } - - get extensionAllowedProposedApi(): readonly string[] { return product.extensionAllowedProposedApi; } - - get uiExtensions(): readonly string[] | undefined { return product.uiExtensions; } - - get enableTelemetry(): boolean { return product.enableTelemetry; } - - get sendASmile(): { reportIssueUrl: string, requestFeatureUrl: string } { return product.sendASmile; } - - get extensionsGallery() { return product.extensionsGallery; } - - get settingsSearchBuildId(): number | undefined { return product.settingsSearchBuildId; } - - get settingsSearchUrl(): string | undefined { return product.settingsSearchUrl; } - - get experimentsUrl(): string | undefined { return product.experimentsUrl; } - - get extensionKeywords(): { [extension: string]: readonly string[]; } | undefined { return product.extensionKeywords; } - - get extensionAllowedBadgeProviders(): readonly string[] | undefined { return product.extensionAllowedBadgeProviders; } } diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 90bdeb5ea748a..f2c3141d06399 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.urlProtocol === link.scheme) { + if (this._productService.productConfiguration.urlProtocol === link.scheme) { return true; } return !!webview.webview.contentOptions.enableCommandUris && link.scheme === 'command'; diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 46a3f6a59aa56..33737a0296653 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -741,13 +741,13 @@ export class SimpleWindowsService implements IWindowsService { async openAboutDialog(): Promise { const detail = localize('aboutDetail', "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", - this.productService.version || 'Unknown', - this.productService.commit || 'Unknown', - this.productService.date || 'Unknown', + this.productService.productConfiguration.version || 'Unknown', + this.productService.productConfiguration.commit || 'Unknown', + this.productService.productConfiguration.date || 'Unknown', navigator.userAgent ); - const result = await this.dialogService.show(Severity.Info, this.productService.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail }); + const result = await this.dialogService.show(Severity.Info, this.productService.productConfiguration.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail }); if (result === 0) { this.clipboardService.writeText(detail); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index d454894401968..6fdc99451a6b8 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -175,7 +175,7 @@ export class DebugSession implements IDebugSession { return this.raw!.initialize({ clientID: 'vscode', - clientName: this.productService.nameLong, + clientName: this.productService.productConfiguration.nameLong, adapterID: this.configuration.type, pathFormat: 'path', linesStartAt1: true, diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index 5707377ce4b1b..bef55ac4b26b8 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -170,10 +170,10 @@ export class ExperimentService extends Disposable implements IExperimentService } protected getExperiments(): Promise { - if (!this.productService.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) { + if (!this.productService.productConfiguration.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) { return Promise.resolve([]); } - return this.requestService.request({ type: 'GET', url: this.productService.experimentsUrl }, CancellationToken.None).then(context => { + return this.requestService.request({ type: 'GET', url: this.productService.productConfiguration.experimentsUrl }, CancellationToken.None).then(context => { if (context.res.statusCode !== 200) { return Promise.resolve(null); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index ec19e774ab1f6..05bef903d9e00 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -76,10 +76,10 @@ export function toExtensionDescription(local: ILocalExtension): IExtensionDescri const promptDownloadManually = (extension: IGalleryExtension | undefined, message: string, error: Error, instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService, productService: IProductService) => { - if (!extension || error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS || !productService.extensionsGallery) { + if (!extension || error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS || !productService.productConfiguration.extensionsGallery) { return Promise.reject(error); } else { - const downloadUrl = `${productService.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; + const downloadUrl = `${productService.productConfiguration.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; notificationService.prompt(Severity.Error, message, [{ label: localize('download', "Download Manually"), run: () => openerService.open(URI.parse(downloadUrl)).then(() => { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index b64689e02c6c9..99ec538b4426e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -114,11 +114,11 @@ class Extension implements IExtension { } get url(): string | undefined { - if (!this.productService.extensionsGallery || !this.gallery) { + if (!this.productService.productConfiguration.extensionsGallery || !this.gallery) { return undefined; } - return `${this.productService.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`; + return `${this.productService.productConfiguration.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`; } get iconUrl(): string { @@ -615,7 +615,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension text = text.replace(extensionRegex, (m, ext) => { // Get curated keywords - const lookup = this.productService.extensionKeywords || {}; + const lookup = this.productService.productConfiguration.extensionKeywords || {}; const keywords = lookup[ext] || []; // Get mode name @@ -1022,7 +1022,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension get allowedBadgeProviders(): string[] { if (!this._extensionAllowedBadgeProviders) { - this._extensionAllowedBadgeProviders = (this.productService.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase()); + this._extensionAllowedBadgeProviders = (this.productService.productConfiguration.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase()); } return this._extensionAllowedBadgeProviders; } diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.ts b/src/vs/workbench/contrib/feedback/browser/feedback.ts index 618dc7edaa9d5..130c7fd3a0441 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.ts @@ -73,8 +73,8 @@ export class FeedbackDropdown extends Dropdown { this.feedbackDelegate = options.feedbackService; this.maxFeedbackCharacters = this.feedbackDelegate.getCharacterLimit(this.sentiment); - if (productService.sendASmile) { - this.requestFeatureLink = productService.sendASmile.requestFeatureUrl; + if (productService.productConfiguration.sendASmile) { + this.requestFeatureLink = productService.productConfiguration.sendASmile.requestFeatureUrl; } this.integrityService.isPure().then(result => { diff --git a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts index c609aa4132fa3..458dc93b0b688 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts @@ -58,7 +58,7 @@ export class FeedbackStatusbarConribution extends Disposable implements IWorkben ) { super(); - if (productService.sendASmile) { + if (productService.productConfiguration.sendASmile) { this.entry = this._register(statusbarService.addEntry(this.getStatusEntry(), 'status.feedback', localize('status.feedback', "Tweet Feedback"), StatusbarAlignment.RIGHT, -100 /* towards the end of the right hand side */)); CommandsRegistry.registerCommand('_feedback.open', () => this.toggleFeedback()); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index 2887abfc0f3b1..ef36014032de0 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -73,7 +73,7 @@ export class PreferencesSearchService extends Disposable implements IPreferences }; } else { return { - urlBase: this.productService.settingsSearchUrl + urlBase: this.productService.productConfiguration.settingsSearchUrl }; } } @@ -364,7 +364,7 @@ class RemoteSearchProvider implements ISearchProvider { const extensions = await this.installedExtensions; const filters = this.options.newExtensionsOnly ? [`diminish eq 'latest'`] : - this.getVersionFilters(extensions, this.productService.settingsSearchBuildId); + this.getVersionFilters(extensions, this.productService.productConfiguration.settingsSearchBuildId); const filterStr = filters .slice(filterPage * RemoteSearchProvider.MAX_REQUEST_FILTERS, (filterPage + 1) * RemoteSearchProvider.MAX_REQUEST_FILTERS) @@ -563,4 +563,4 @@ export class SettingMatches { endColumn: setting.valueRange.startColumn + match.end + 1 }; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index b3ff7db5e88e2..b1ecf1026f3c4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -226,7 +226,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(); this._configHelper.showRecommendations(shellLaunchConfig); const baseEnv = this._configHelper.config.inheritEnv ? process.env as platform.IProcessEnvironment : await this._terminalInstanceService.getMainProcessParentEnv(); - const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.setLocaleVariables, baseEnv); + const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.productConfiguration.version, this._configHelper.config.setLocaleVariables, baseEnv); const useConpty = this._configHelper.config.windowsEnableConpty && !isScreenReaderModeEnabled; return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty); diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index 5794b25906167..7848fdee12777 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -117,15 +117,15 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { const [telemetryInfo, extensionDescriptions] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._extensions]); const workspace = this._contextService.getWorkspace(); return { - commit: this._productService.commit, - version: this._productService.version, + commit: this._productService.productConfiguration.commit, + version: this._productService.productConfiguration.version, parentPid: -1, environment: { isExtensionDevelopmentDebug: false, appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, appSettingsHome: this._environmentService.appSettingsHome ? this._environmentService.appSettingsHome : undefined, - appName: this._productService.nameLong, - appUriScheme: this._productService.urlProtocol, + appName: this._productService.productConfiguration.nameLong, + appUriScheme: this._productService.productConfiguration.urlProtocol, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 6445a03a6e631..51f991375a6a3 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -462,12 +462,12 @@ class ProposedApiController { } this.enableProposedApiForAll = !environmentService.isBuilt || - (!!environmentService.extensionDevelopmentLocationURI && productService.nameLong !== 'Visual Studio Code') || + (!!environmentService.extensionDevelopmentLocationURI && productService.productConfiguration.nameLong !== 'Visual Studio Code') || (this.enableProposedApiFor.length === 0 && 'enable-proposed-api' in environmentService.args); this.productAllowProposedApi = new Set(); - if (isNonEmptyArray(productService.extensionAllowedProposedApi)) { - productService.extensionAllowedProposedApi.forEach((id) => this.productAllowProposedApi.add(ExtensionIdentifier.toKey(id))); + if (isNonEmptyArray(productService.productConfiguration.extensionAllowedProposedApi)) { + productService.productConfiguration.extensionAllowedProposedApi.forEach((id) => this.productAllowProposedApi.add(ExtensionIdentifier.toKey(id))); } } diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts index a1496708db6d1..2f7d4e01039ac 100644 --- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -24,7 +24,7 @@ export function isUIExtension(manifest: IExtensionManifest, productService: IPro case 'workspace': return false; default: { // Tagged as UI extension in product - if (isNonEmptyArray(productService.uiExtensions) && productService.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { + if (isNonEmptyArray(productService.productConfiguration.uiExtensions) && productService.productConfiguration.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { return true; } // Not an UI extension if it has main diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts index 47aaf4a22bb3b..ca657398990c7 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts @@ -71,7 +71,7 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH public start(): Promise { const options: IConnectionOptions = { - commit: this._productService.commit, + commit: this._productService.productConfiguration.commit, socketFactory: this._socketFactory, addressProvider: { getAddress: async () => { @@ -181,15 +181,15 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH const hostExtensions = allExtensions.filter(extension => extension.main && extension.api === 'none').map(extension => extension.identifier); const workspace = this._contextService.getWorkspace(); const r: IInitData = { - commit: this._productService.commit, - version: this._productService.version, + commit: this._productService.productConfiguration.commit, + version: this._productService.productConfiguration.version, parentPid: remoteExtensionHostData.pid, environment: { isExtensionDevelopmentDebug, appRoot: remoteExtensionHostData.appRoot, appSettingsHome: remoteExtensionHostData.appSettingsHome, - appName: this._productService.nameLong, - appUriScheme: this._productService.urlProtocol, + appName: this._productService.productConfiguration.nameLong, + appUriScheme: this._productService.productConfiguration.urlProtocol, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, diff --git a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts index 84ec145c4c845..75787071f8516 100644 --- a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts +++ b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts @@ -69,7 +69,7 @@ export class RemoteExtensionManagementChannelClient extends ExtensionManagementC const installed = await this.getInstalled(ExtensionType.User); const compatible = await this.galleryService.getCompatibleExtension(extension); if (!compatible) { - return Promise.reject(new Error(localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extension.identifier.id, this.productService.version))); + return Promise.reject(new Error(localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extension.identifier.id, this.productService.productConfiguration.version))); } const manifest = await this.galleryService.getManifest(compatible, CancellationToken.None); if (manifest) { @@ -140,4 +140,4 @@ export class RemoteExtensionManagementChannelClient extends ExtensionManagementC } return this.getDependenciesAndPackedExtensionsRecursively(toGet, result, uiExtension, token); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts index fd979ca094c62..db4d2038ca3e0 100644 --- a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts @@ -28,7 +28,7 @@ export class RemoteAgentService extends AbstractRemoteAgentService implements IR super(environmentService); this.socketFactory = new BrowserSocketFactory(webSocketFactory); - this._connection = this._register(new RemoteAgentConnection(environmentService.configuration.remoteAuthority!, productService.commit, this.socketFactory, remoteAuthorityResolverService, signService)); + this._connection = this._register(new RemoteAgentConnection(environmentService.configuration.remoteAuthority!, productService.productConfiguration.commit, this.socketFactory, remoteAuthorityResolverService, signService)); } getConnection(): IRemoteAgentConnection | null { diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index eac9dcfb1d0a7..2632d935d80e4 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -79,11 +79,11 @@ export class TelemetryService extends Disposable implements ITelemetryService { ) { super(); - const aiKey = productService.aiConfig && productService.aiConfig.asimovKey; - if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.enableTelemetry && !!aiKey) { + const aiKey = productService.productConfiguration.aiConfig && productService.productConfiguration.aiConfig.asimovKey; + if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.productConfiguration.enableTelemetry && !!aiKey) { const config: ITelemetryServiceConfig = { appender: combinedAppender(new WebTelemetryAppender(aiKey, logService), new LogAppender(logService)), - commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.configuration.remoteAuthority), + commonProperties: resolveWorkbenchCommonProperties(storageService, productService.productConfiguration.commit, productService.productConfiguration.version, environmentService.configuration.machineId, environmentService.configuration.remoteAuthority), piiPaths: [environmentService.appRoot] }; diff --git a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts index 6eaaf220ce572..002b6ae4ce369 100644 --- a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts @@ -34,11 +34,11 @@ export class TelemetryService extends Disposable implements ITelemetryService { ) { super(); - if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.enableTelemetry) { + if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.productConfiguration.enableTelemetry) { const channel = sharedProcessService.getChannel('telemetryAppender'); const config: ITelemetryServiceConfig = { appender: combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)), - commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.installSourcePath, environmentService.configuration.remoteAuthority), + commonProperties: resolveWorkbenchCommonProperties(storageService, productService.productConfiguration.commit, productService.productConfiguration.version, environmentService.configuration.machineId, environmentService.installSourcePath, environmentService.configuration.remoteAuthority), piiPaths: environmentService.extensionsPath ? [environmentService.appRoot, environmentService.extensionsPath] : [environmentService.appRoot] }; @@ -69,4 +69,4 @@ export class TelemetryService extends Disposable implements ITelemetryService { } } -registerSingleton(ITelemetryService, TelemetryService); \ No newline at end of file +registerSingleton(ITelemetryService, TelemetryService); From 2180dd6973bfb5892b6e4723bb65f00169b214b3 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 14 Aug 2019 18:27:09 +0200 Subject: [PATCH 175/613] use product service --- .../electron-browser/extensionTipsService.ts | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts index 33e5fbc2bfe62..5610f403056ca 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts @@ -14,7 +14,6 @@ import { IExtensionTipsService, ExtensionRecommendationReason, IExtensionsConfig import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextModel } from 'vs/editor/common/model'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import product from 'vs/platform/product/node/product'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ShowRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction, InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import Severity from 'vs/base/common/severity'; @@ -23,7 +22,6 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionsConfiguration, ConfigurationKey, ShowRecommendationsOnlyOnDemandKey, IExtensionsViewlet, IExtensionsWorkbenchService, EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import * as pfs from 'vs/base/node/pfs'; import * as os from 'os'; import { flatten, distinct, shuffle, coalesce } from 'vs/base/common/arrays'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -41,7 +39,7 @@ import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/wo import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { extname } from 'vs/base/common/resources'; -import { IExeBasedExtensionTip } from 'vs/platform/product/common/product'; +import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/product'; import { timeout } from 'vs/base/common/async'; import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; @@ -108,7 +106,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService, @IExperimentService private readonly experimentService: IExperimentService, - @IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService + @IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService, + @IProductService private readonly productService: IProductService ) { super(); @@ -116,8 +115,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return; } - if (product.extensionsGallery && product.extensionsGallery.recommendationsUrl) { - this._extensionsRecommendationsUrl = product.extensionsGallery.recommendationsUrl; + if (this.productService.productConfiguration.extensionsGallery && this.productService.productConfiguration.extensionsGallery.recommendationsUrl) { + this._extensionsRecommendationsUrl = this.productService.productConfiguration.extensionsGallery.recommendationsUrl; } this.sessionSeed = +new Date(); @@ -243,7 +242,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } getKeymapRecommendations(): IExtensionRecommendation[] { - return (product.keymapExtensionTips || []) + return (this.productService.productConfiguration.keymapExtensionTips || []) .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)) .map(extensionId => ({ extensionId, sources: ['application'] })); } @@ -600,10 +599,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return Object.keys(this._fileBasedRecommendations) .sort((a, b) => { if (this._fileBasedRecommendations[a].recommendedTime === this._fileBasedRecommendations[b].recommendedTime) { - if (!product.extensionImportantTips || caseInsensitiveGet(product.extensionImportantTips, a)) { + if (!this.productService.productConfiguration.extensionImportantTips || caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, a)) { return -1; } - if (caseInsensitiveGet(product.extensionImportantTips, b)) { + if (caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, b)) { return 1; } } @@ -614,11 +613,11 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } /** - * Parse all file based recommendations from product.extensionTips - * Retire existing recommendations if they are older than a week or are not part of product.extensionTips anymore + * Parse all file based recommendations from this.productService.productConfiguration.extensionTips + * Retire existing recommendations if they are older than a week or are not part of this.productService.productConfiguration.extensionTips anymore */ private fetchFileBasedRecommendations() { - const extensionTips = product.extensionTips; + const extensionTips = this.productService.productConfiguration.extensionTips; if (!extensionTips) { return; } @@ -635,7 +634,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } }); - forEach(product.extensionImportantTips, entry => { + forEach(this.productService.productConfiguration.extensionImportantTips, entry => { let { key: id, value } = entry; const { pattern } = value; let ids = this._availableRecommendations[pattern]; @@ -697,7 +696,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe let { key: pattern, value: ids } = entry; if (match(pattern, model.uri.toString())) { for (let id of ids) { - if (caseInsensitiveGet(product.extensionImportantTips, id)) { + if (caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, id)) { recommendationsToSuggest.push(id); } const filedBasedRecommendation = this._fileBasedRecommendations[id.toLowerCase()] || { recommendedTime: now, sources: [] }; @@ -751,7 +750,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } const id = recommendationsToSuggest[0]; - const entry = caseInsensitiveGet(product.extensionImportantTips, id); + const entry = caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, id); if (!entry) { return false; } @@ -981,14 +980,14 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } /** - * If user has any of the tools listed in product.exeBasedExtensionTips, fetch corresponding recommendations + * If user has any of the tools listed in this.productService.productConfiguration.exeBasedExtensionTips, fetch corresponding recommendations */ private fetchExecutableRecommendations(important: boolean): Promise { const homeDir = os.homedir(); let foundExecutables: Set = new Set(); let findExecutable = (exeName: string, tip: IExeBasedExtensionTip, path: string) => { - return pfs.fileExists(path).then(exists => { + return this.fileService.exists(URI.file(path)).then(exists => { if (exists && !foundExecutables.has(exeName)) { foundExecutables.add(exeName); (tip['recommendations'] || []).forEach(extensionId => { @@ -1005,7 +1004,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe let promises: Promise[] = []; // Loop through recommended extensions - forEach(product.exeBasedExtensionTips, entry => { + forEach(this.productService.productConfiguration.exeBasedExtensionTips, entry => { if (typeof entry.value !== 'object' || !Array.isArray(entry.value['recommendations'])) { return; } From 24121a38ef023bb378582ee045f7630b0bee0d7b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 14 Aug 2019 09:44:44 -0700 Subject: [PATCH 176/613] Simplify terminal commands --- .../terminal/browser/terminal.contribution.ts | 5 ++-- .../terminal/browser/terminalActions.ts | 29 ++++--------------- .../contrib/terminal/common/terminal.ts | 2 +- .../terminal/common/terminalCommands.ts | 3 +- .../terminal/common/terminalService.ts | 10 +++++-- 5 files changed, 17 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index dbd0aa4128647..2a343f63b0588 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -20,7 +20,7 @@ import * as panel from 'vs/workbench/browser/panel'; import { getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { Extensions as QuickOpenExtensions, IQuickOpenRegistry, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; -import { AllowWorkspaceShellTerminalCommand, ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, DisallowWorkspaceShellTerminalCommand, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, ITerminalService, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -387,8 +387,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearTerminalAct mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_K } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectDefaultShellWindowsTerminalAction, SelectDefaultShellWindowsTerminalAction.ID, SelectDefaultShellWindowsTerminalAction.LABEL), 'Terminal: Select Default Shell', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(AllowWorkspaceShellTerminalCommand, AllowWorkspaceShellTerminalCommand.ID, AllowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Allow Workspace Shell Configuration', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisallowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand.ID, DisallowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Disallow Workspace Shell Configuration', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ManageWorkspaceShellPermissionsTerminalCommand, ManageWorkspaceShellPermissionsTerminalCommand.ID, ManageWorkspaceShellPermissionsTerminalCommand.LABEL), 'Terminal: Manage Workspace Shell Permissions', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RenameTerminalAction, RenameTerminalAction.ID, RenameTerminalAction.LABEL), 'Terminal: Rename', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_F diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 41ceb3e71b1bc..4d07f85dc15aa 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -993,10 +993,10 @@ export class ClearSelectionTerminalAction extends Action { } } -export class AllowWorkspaceShellTerminalCommand extends Action { +export class ManageWorkspaceShellPermissionsTerminalCommand extends Action { - public static readonly ID = TERMINAL_COMMAND_ID.WORKSPACE_SHELL_ALLOW; - public static readonly LABEL = nls.localize('workbench.action.terminal.allowWorkspaceShell', "Allow Workspace Shell Configuration"); + public static readonly ID = TERMINAL_COMMAND_ID.MANAGE_WORKSPACE_SHELL_PERMISSIONS; + public static readonly LABEL = nls.localize('workbench.action.terminal.manageWorkspaceShellPermissions', "Manage Workspace Shell Permissions"); constructor( id: string, label: string, @@ -1005,27 +1005,8 @@ export class AllowWorkspaceShellTerminalCommand extends Action { super(id, label); } - public run(event?: any): Promise { - this.terminalService.setWorkspaceShellAllowed(true); - return Promise.resolve(undefined); - } -} - -export class DisallowWorkspaceShellTerminalCommand extends Action { - - public static readonly ID = TERMINAL_COMMAND_ID.WORKSPACE_SHELL_DISALLOW; - public static readonly LABEL = nls.localize('workbench.action.terminal.disallowWorkspaceShell', "Disallow Workspace Shell Configuration"); - - constructor( - id: string, label: string, - @ITerminalService private readonly terminalService: ITerminalService - ) { - super(id, label); - } - - public run(event?: any): Promise { - this.terminalService.setWorkspaceShellAllowed(false); - return Promise.resolve(undefined); + public async run(event?: any): Promise { + await this.terminalService.manageWorkspaceShellPermissions(); } } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index bb99965b26c2f..4383fb9c82bb1 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -279,7 +279,7 @@ export interface ITerminalService { selectDefaultWindowsShell(): Promise; setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; - setWorkspaceShellAllowed(isAllowed: boolean): void; + manageWorkspaceShellPermissions(): void; /** * Takes a path and returns the properly escaped path to send to the terminal. diff --git a/src/vs/workbench/contrib/terminal/common/terminalCommands.ts b/src/vs/workbench/contrib/terminal/common/terminalCommands.ts index 60da258805df0..ddd108304ae11 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalCommands.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalCommands.ts @@ -48,8 +48,7 @@ export const enum TERMINAL_COMMAND_ID { SCROLL_TO_TOP = 'workbench.action.terminal.scrollToTop', CLEAR = 'workbench.action.terminal.clear', CLEAR_SELECTION = 'workbench.action.terminal.clearSelection', - WORKSPACE_SHELL_ALLOW = 'workbench.action.terminal.allowWorkspaceShell', - WORKSPACE_SHELL_DISALLOW = 'workbench.action.terminal.disallowWorkspaceShell', + MANAGE_WORKSPACE_SHELL_PERMISSIONS = 'workbench.action.terminal.manageWorkspaceShellPermissions', RENAME = 'workbench.action.terminal.rename', FIND_WIDGET_FOCUS = 'workbench.action.terminal.focusFindWidget', FIND_WIDGET_HIDE = 'workbench.action.terminal.hideFindWidget', diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index 1b04048fdcc09..398470899aa2e 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -479,8 +479,14 @@ export abstract class TerminalService implements ITerminalService { return terminalIndex; } - public setWorkspaceShellAllowed(isAllowed: boolean): void { - this.configHelper.setWorkspaceShellAllowed(isAllowed); + public async manageWorkspaceShellPermissions(): Promise { + const allowItem: IQuickPickItem = { label: nls.localize('workbench.action.terminal.allowWorkspaceShell', "Allow Workspace Shell Configuration") }; + const disallowItem: IQuickPickItem = { label: nls.localize('workbench.action.terminal.disallowWorkspaceShell', "Disallow Workspace Shell Configuration") }; + const value = await this._quickInputService.pick([allowItem, disallowItem], { canPickMany: false }); + if (!value) { + return; + } + this.configHelper.setWorkspaceShellAllowed(value === allowItem); } protected _showTerminalCloseConfirmation(): Promise { From 6cd382dc51232190d6a87c19cf1790fd27313dcd Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 14 Aug 2019 09:50:37 -0700 Subject: [PATCH 177/613] Improve typings, use async --- .../contrib/terminal/common/terminalService.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index 398470899aa2e..d2dc8ec3c59df 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -489,18 +489,18 @@ export abstract class TerminalService implements ITerminalService { this.configHelper.setWorkspaceShellAllowed(value === allowItem); } - protected _showTerminalCloseConfirmation(): Promise { - let message; + protected async _showTerminalCloseConfirmation(): Promise { + let message: string; if (this.terminalInstances.length === 1) { message = nls.localize('terminalService.terminalCloseConfirmationSingular', "There is an active terminal session, do you want to kill it?"); } else { message = nls.localize('terminalService.terminalCloseConfirmationPlural', "There are {0} active terminal sessions, do you want to kill them?", this.terminalInstances.length); } - - return this._dialogService.confirm({ + const res = await this._dialogService.confirm({ message, type: 'warning', - }).then(res => !res.confirmed); + }); + return !res.confirmed; } protected _showNotEnoughSpaceToast(): void { From 70ddd34a9c0c94b6de2def5e216ca176e140daa6 Mon Sep 17 00:00:00 2001 From: mayaswrath Date: Wed, 14 Aug 2019 13:08:49 -0400 Subject: [PATCH 178/613] uses getSelections and simplifed the return --- .../debug/browser/debugEditorActions.ts | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 53d6608548e3f..645e55984a5f9 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -35,24 +35,22 @@ class ToggleBreakpointAction extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { - if (editor.hasModel()) { + if (editor.hasModel() && editor.getSelections()) { const debugService = accessor.get(IDebugService); const modelUri = editor.getModel().uri; - const lineNumbers = new Set(editor._getCursors().getAll() - .filter(cs => cs.modelState.position) - .map(cs => cs.modelState.position.lineNumber)); - - const promises = Array>(); - lineNumbers.forEach(lineNumber => { - const bps = debugService.getModel().getBreakpoints({ lineNumber: lineNumber, uri: modelUri }); - + const canSet = debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel()); + // Does not account for multi line selections, Set to remove multiple cursor on the same line + const lineNumbers = [...new Set(editor.getSelections().map(s => s.getPosition().lineNumber))]; + return Promise.all(lineNumbers.map(line => { + const bps = debugService.getModel().getBreakpoints({ lineNumber: line, uri: modelUri }); if (bps.length) { - bps.map(bp => debugService.removeBreakpoints(bp.getId())).forEach(p => promises.push(p)); - } else if (debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { - promises.push(debugService.addBreakpoints(modelUri, [{ lineNumber: lineNumber }], 'debugEditorActions.toggleBreakpointAction')); + return Promise.all(bps.map(bp => debugService.removeBreakpoints(bp.getId()))); + } else if (canSet) { + return (debugService.addBreakpoints(modelUri, [{ lineNumber: line }], 'debugEditorActions.toggleBreakpointAction')); + } else { //Line where a breakpoint cant be set + return Promise.resolve([]); } - }); - return Promise.all(promises); + })); } return Promise.resolve(); } From f4273c1972c9a45a13947694a65465a5846f7877 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 14 Aug 2019 10:20:10 -0700 Subject: [PATCH 179/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eaa647b89173c..628e97a2c1a7e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "6ce1c040c0d555b998dba62dd333f437a7b2d44f", + "distro": "afe23e028d98eeca8e26c62804b876ad9cce27b9", "author": { "name": "Microsoft Corporation" }, From 9689b2b047a5565a905bb802dde6bc419e25fc1d Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 14 Aug 2019 10:52:11 -0700 Subject: [PATCH 180/613] migrate keys from legacy layout #79020 --- src/vs/workbench/browser/layout.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 7f82e7e7562a3..5fe9f43d192fd 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1177,10 +1177,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } private createGridDescriptor(): ISerializedGrid { - const width = this.storageService.getNumber(Storage.GRID_WIDTH, StorageScope.GLOBAL, 600); - const height = this.storageService.getNumber(Storage.GRID_HEIGHT, StorageScope.GLOBAL, 400); - const sideBarSize = this.storageService.getNumber(Storage.SIDEBAR_SIZE, StorageScope.GLOBAL, 300); - const panelSize = this.storageService.getNumber(Storage.PANEL_SIZE, StorageScope.GLOBAL, 300); + const workbenchDimensions = getClientArea(this.parent); + const width = this.storageService.getNumber(Storage.GRID_WIDTH, StorageScope.GLOBAL, workbenchDimensions.width); + const height = this.storageService.getNumber(Storage.GRID_HEIGHT, StorageScope.GLOBAL, workbenchDimensions.height); + // At some point, we will not fall back to old keys from legacy layout, but for now, let's migrate the keys + const sideBarSize = this.storageService.getNumber(Storage.SIDEBAR_SIZE, StorageScope.GLOBAL, this.storageService.getNumber('workbench.sidebar.width', StorageScope.GLOBAL, Math.min(workbenchDimensions.width / 4, 300))!); + const panelSize = this.storageService.getNumber(Storage.PANEL_SIZE, StorageScope.GLOBAL, this.storageService.getNumber(this.state.panel.position === Position.BOTTOM ? 'workbench.panel.height' : 'workbench.panel.width', StorageScope.GLOBAL, workbenchDimensions.height / 3)); const titleBarHeight = this.titleBarPartView.minimumHeight; const statusBarHeight = this.statusBarPartView.minimumHeight; From c75f9bb75644483adb0282169e45bea5c9be76ed Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 14 Aug 2019 20:00:14 +0200 Subject: [PATCH 181/613] fix tests --- src/vs/platform/product/common/product.ts | 4 +- .../extensionsTipsService.test.ts | 38 +++++++++++-------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 11b6fed267a93..ec42094d94cd7 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -43,8 +43,8 @@ export interface IProductConfiguration { readonly controlUrl: string; readonly recommendationsUrl: string; }; - extensionTips: { [id: string]: string; }; - extensionImportantTips: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; }; + readonly extensionTips: { [id: string]: string; }; + 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[]; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts index cd6d14c858d56..b66c6143f2439 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -52,6 +52,7 @@ import { NullLogService } from 'vs/platform/log/common/log'; import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { IFileService } from 'vs/platform/files/common/files'; +import { IProductService } from 'vs/platform/product/common/product'; const mockExtensionGallery: IGalleryExtension[] = [ aGalleryExtension('MockExtension1', { @@ -201,27 +202,32 @@ suite('ExtensionsTipsService Test', () => { instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IURLService, URLService); + instantiationService.stub(IProductService, >{ + productConfiguration: { + ...product, ...{ + extensionTips: { + 'ms-vscode.csharp': '{**/*.cs,**/project.json,**/global.json,**/*.csproj,**/*.sln,**/appsettings.json}', + 'msjsdiag.debugger-for-chrome': '{**/*.ts,**/*.tsx**/*.js,**/*.jsx,**/*.es6,**/.babelrc}', + 'lukehoban.Go': '**/*.go' + }, + extensionImportantTips: { + 'ms-python.python': { + 'name': 'Python', + 'pattern': '{**/*.py}' + }, + 'ms-vscode.PowerShell': { + 'name': 'PowerShell', + 'pattern': '{**/*.ps,**/*.ps1}' + } + } + } + } + }); experimentService = instantiationService.createInstance(TestExperimentService); instantiationService.stub(IExperimentService, experimentService); onModelAddedEvent = new Emitter(); - - product.extensionTips = { - 'ms-vscode.csharp': '{**/*.cs,**/project.json,**/global.json,**/*.csproj,**/*.sln,**/appsettings.json}', - 'msjsdiag.debugger-for-chrome': '{**/*.ts,**/*.tsx**/*.js,**/*.jsx,**/*.es6,**/.babelrc}', - 'lukehoban.Go': '**/*.go' - }; - product.extensionImportantTips = { - 'ms-python.python': { - 'name': 'Python', - 'pattern': '{**/*.py}' - }, - 'ms-vscode.PowerShell': { - 'name': 'PowerShell', - 'pattern': '{**/*.ps,**/*.ps1}' - } - }; }); suiteTeardown(() => { From 37d8f1fd26a459f162efbe84e69ef10fa22f8a97 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 14 Aug 2019 11:37:16 -0700 Subject: [PATCH 182/613] Register driver --- src/vs/workbench/browser/web.main.ts | 5 +++++ test/smoke/src/vscode/puppeteerDriver.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 1a47bd47cf86a..d3bb53ad1955d 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -84,6 +84,11 @@ class CodeRendererMain extends Disposable { })); this._register(workbench.onShutdown(() => this.dispose())); + // Driver + if (this.configuration.driver) { + registerWindowDriver().then(d => this._register(d)); + } + // Startup workbench.startup(); } diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 5d7b163b71c2c..70fa8ca0cc9a2 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -191,7 +191,7 @@ export async function launch(_args): Promise { // TODO: Don't open up the system browser const webUserDataDir = join(tmpdir(), `smoketest-${Math.random() * 10000000000}`); await promisify(mkdir)(webUserDataDir); - server = spawn(join(args[0], `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`), ['--driver', 'web', '--web-user-data-dir', webUserDataDir]); + server = spawn(join(args[0], `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`), ['--no-browser', '--driver', 'web', '--web-user-data-dir', webUserDataDir]); server.stderr.on('data', e => console.log('Server stderr: ' + e)); process.on('exit', teardown); endpoint = await waitForEndpoint(); From 1f8d0fced333afacf2250812942c4bc95d4521d4 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 14 Aug 2019 11:38:48 -0700 Subject: [PATCH 183/613] Teardown on sigint --- test/smoke/src/vscode/puppeteerDriver.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 70fa8ca0cc9a2..a5fd775cb2a69 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -194,6 +194,7 @@ export async function launch(_args): Promise { server = spawn(join(args[0], `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`), ['--no-browser', '--driver', 'web', '--web-user-data-dir', webUserDataDir]); server.stderr.on('data', e => console.log('Server stderr: ' + e)); process.on('exit', teardown); + process.on('SIGINT', teardown); endpoint = await waitForEndpoint(); } From e4863753b9fd6866c98eaeceb184ebd1bb880632 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 14 Aug 2019 11:36:57 -0700 Subject: [PATCH 184/613] Update markdown grammar --- .../syntaxes/markdown.tmLanguage.json | 37 +++++++++++++++---- .../test/colorize-results/test-33886_md.json | 20 +++++----- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index f63c46a878d6d..6fb25ae6ce24f 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/a595d8ba2ae9ce8864435d33db2afa0fe68b1487", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/05ccfa3db6edbd357390431f9e316adb38ba41d8", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -716,7 +716,7 @@ { "begin": "(^|\\G)(\\s*)(.*)", "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", - "contentName": "meta.embedded.block.cpp source.cpp", + "contentName": "meta.embedded.block.cpp", "patterns": [ { "include": "source.cpp" @@ -1987,8 +1987,29 @@ "name": "comment.block.html" }, { - "begin": "(^|\\G)\\s*(?=<(script|style|pre)(\\s|$|>)(?!.*?))", - "end": "(?=.*)", + "begin": "(?i)(^|\\G)\\s*(?=<(script|style|pre)(\\s|$|>)(?!.*?))", + "end": "(?i)(.*)(())", + "endCaptures": { + "1": { + "patterns": [ + { + "include": "text.html.basic" + } + ] + }, + "2": { + "name": "meta.tag.structure.$4.end.html" + }, + "3": { + "name": "punctuation.definition.tag.begin.html" + }, + "4": { + "name": "entity.name.tag.html" + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, "patterns": [ { "begin": "(\\s*|$)", @@ -1997,12 +2018,12 @@ "include": "text.html.basic" } ], - "while": "^(?!.*)" + "while": "(?i)^(?!.*)" } ] }, { - "begin": "(^|\\G)\\s*(?=))", + "begin": "(?i)(^|\\G)\\s*(?=))", "patterns": [ { "include": "text.html.basic" @@ -2275,7 +2296,7 @@ }, "bracket": { "comment": "Markdown will convert this for us. We match it so that the HTML grammar will not mark it up as invalid.", - "match": "<(?![a-z/?\\$!])", + "match": "<(?![a-zA-Z/?\\$!])", "name": "meta.other.valid-bracket.markdown" }, "escape": { @@ -2566,4 +2587,4 @@ "name": "markup.inline.raw.string.markdown" } } -} +} \ No newline at end of file diff --git a/extensions/markdown-basics/test/colorize-results/test-33886_md.json b/extensions/markdown-basics/test/colorize-results/test-33886_md.json index 179172a57382c..25799e89a3630 100644 --- a/extensions/markdown-basics/test/colorize-results/test-33886_md.json +++ b/extensions/markdown-basics/test/colorize-results/test-33886_md.json @@ -111,7 +111,7 @@ }, { "c": "", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.code.end.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.inline.code.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -144,7 +144,7 @@ }, { "c": "", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.structure.pre.end.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.structure.pre.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -254,7 +254,7 @@ }, { "c": "a", - "t": "text.html.markdown meta.paragraph.markdown", + "t": "text.html.markdown", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -265,7 +265,7 @@ }, { "c": "", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.structure.pre.end.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.structure.pre.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", From c4733f91c86a0aaeb2f5c122fc13512f6ea911fb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 14 Aug 2019 14:49:23 -0700 Subject: [PATCH 185/613] Still show fix all actions for fix-all actions that can fix multiple errors with multple different diagnostics --- .../src/features/quickFix.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index 6ce9f199704e1..7bc5d43f0eb2e 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -286,7 +286,12 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { } // Make sure there are multiple diagnostics of the same type in the file - if (!this.diagnosticsManager.getDiagnostics(document.uri).some(x => x.code === diagnostic.code && x !== diagnostic)) { + if (!this.diagnosticsManager.getDiagnostics(document.uri).some(x => { + if (x === diagnostic) { + return false; + } + return x.code === diagnostic.code || fixAllErrorCodes.get(x.code as number) === diagnostic.code; + })) { return results; } @@ -304,6 +309,15 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { } } +// Some fix all actions can actually fix multiple differnt diagnostics. Make sure we still show the fix all action +// in such cases +const fixAllErrorCodes = new Map([ + // Missing async + [2339, 2339], + [2345, 2339], +]); + + const preferredFixes = new Set([ 'annotateWithTypeFromJSDoc', 'constructorForDerivedNeedSuperCall', From 57c13dc0c4bc822ebb8916598ab2c5c77d0d2ab4 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 14 Aug 2019 14:50:45 -0700 Subject: [PATCH 186/613] Base web user data dir off normal one --- test/smoke/src/main.ts | 3 +-- test/smoke/src/vscode/puppeteerDriver.ts | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index bb75ee6066b78..8427b54080974 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -142,8 +142,7 @@ if (typeof stablePath === 'string' && !fs.existsSync(stablePath)) { fail(`Can't find Stable Code at ${stablePath}.`); } -// TODO: Server should be launched from smoke tests -const userDataDir = opts.web ? path.join(process.env.HOME!, '.vscode-remote/data') : path.join(testDataPath, 'd'); +const userDataDir = path.join(testDataPath, 'd'); let quality: Quality; if (process.env.VSCODE_DEV === '1') { diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index a5fd775cb2a69..8aaa6782062b2 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -181,15 +181,13 @@ function timeout(ms: number): Promise { // function runInDriver(call: string, args: (string | boolean)[]): Promise {} -let args; +let args: string[] | undefined; let server: ChildProcess | undefined; let endpoint: string | undefined; -export async function launch(_args): Promise { +export async function launch(_args: string[]): Promise { args = _args; - - // TODO: Don't open up the system browser - const webUserDataDir = join(tmpdir(), `smoketest-${Math.random() * 10000000000}`); + const webUserDataDir = args.filter(e => e.includes('--user-data-dir='))[0].replace('--user-data-dir=', ''); await promisify(mkdir)(webUserDataDir); server = spawn(join(args[0], `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`), ['--no-browser', '--driver', 'web', '--web-user-data-dir', webUserDataDir]); server.stderr.on('data', e => console.log('Server stderr: ' + e)); From d726f427e0eea0631f3900555d5308aadb287f42 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 14 Aug 2019 15:02:05 -0700 Subject: [PATCH 187/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 628e97a2c1a7e..2074de1024a5a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "afe23e028d98eeca8e26c62804b876ad9cce27b9", + "distro": "8ee1612ab2b050f366c2c4c1500fc243b407581f", "author": { "name": "Microsoft Corporation" }, From 414a35dcf793b11617a18d1db56034ace73bde8e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 14 Aug 2019 15:04:35 -0700 Subject: [PATCH 188/613] Use new browser none arg --- test/smoke/src/vscode/puppeteerDriver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 8aaa6782062b2..ba2c8359de12e 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -189,7 +189,7 @@ export async function launch(_args: string[]): Promise { args = _args; const webUserDataDir = args.filter(e => e.includes('--user-data-dir='))[0].replace('--user-data-dir=', ''); await promisify(mkdir)(webUserDataDir); - server = spawn(join(args[0], `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`), ['--no-browser', '--driver', 'web', '--web-user-data-dir', webUserDataDir]); + server = spawn(join(args[0], `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`), ['--browser', 'none', '--driver', 'web', '--web-user-data-dir', webUserDataDir]); server.stderr.on('data', e => console.log('Server stderr: ' + e)); process.on('exit', teardown); process.on('SIGINT', teardown); From 2c4edeb617e41c0222fdd6729794113537eaf331 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 14 Aug 2019 14:54:09 -0700 Subject: [PATCH 189/613] Make sure we compare fully normalized error codes when checking for fix all actions --- .../typescript-language-features/src/features/quickFix.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index 7bc5d43f0eb2e..2585142e0972a 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -290,7 +290,8 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { if (x === diagnostic) { return false; } - return x.code === diagnostic.code || fixAllErrorCodes.get(x.code as number) === diagnostic.code; + return x.code === diagnostic.code + || (fixAllErrorCodes.has(x.code as number) && fixAllErrorCodes.get(x.code as number) === fixAllErrorCodes.get(diagnostic.code as number)); })) { return results; } From 46d0bd810069e5785b6fd045b03570562fc29492 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 14 Aug 2019 17:05:01 -0700 Subject: [PATCH 190/613] Don't include closing ] in folded range Fixes #79142 --- .../typescript-language-features/src/features/folding.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/features/folding.ts b/extensions/typescript-language-features/src/features/folding.ts index a00a2db5f00cb..0867c86fdc755 100644 --- a/extensions/typescript-language-features/src/features/folding.ts +++ b/extensions/typescript-language-features/src/features/folding.ts @@ -55,7 +55,7 @@ class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { const start = range.start.line; // workaround for #47240 - const end = (range.end.character > 0 && document.getText(new vscode.Range(range.end.translate(0, -1), range.end)) === '}') + const end = (range.end.character > 0 && new Set(['}', ']']).has(document.getText(new vscode.Range(range.end.translate(0, -1), range.end)))) ? Math.max(range.end.line - 1, range.start.line) : range.end.line; @@ -81,4 +81,4 @@ export function register( return vscode.languages.registerFoldingRangeProvider(selector, new TypeScriptFoldingProvider(client)); }); -} \ No newline at end of file +} From e0dd508b0eb811a6fe821152f4d310051c1a2060 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 14 Aug 2019 17:19:17 -0700 Subject: [PATCH 191/613] Register Remote Explorer when there are contributions. --- .../api/browser/viewsExtensionPoint.ts | 41 +- src/vs/workbench/browser/parts/views/views.ts | 14 +- src/vs/workbench/common/views.ts | 14 +- .../browser/help-documentation-dark.svg | 11 + .../remote/browser/help-documentation-hc.svg | 11 + .../browser/help-documentation-light.svg | 11 + .../remote/browser/help-feedback-dark.svg | 4 + .../remote/browser/help-feedback-hc.svg | 4 + .../remote/browser/help-feedback-light.svg | 4 + .../browser/help-getting-started-dark.svg | 4 + .../browser/help-getting-started-hc.svg | 4 + .../browser/help-getting-started-light.svg | 4 + .../remote/browser/help-report-issue-dark.svg | 4 + .../remote/browser/help-report-issue-hc.svg | 4 + .../browser/help-report-issue-light.svg | 4 + .../browser/help-review-issues-dark.svg | 4 + .../remote/browser/help-review-issues-hc.svg | 4 + .../browser/help-review-issues-light.svg | 4 + .../remote/browser/remote-activity-bar.svg | 13 + .../contrib/remote/browser/remote.ts | 419 ++++++++++++++++++ .../contrib/remote/browser/remoteViewlet.css | 92 ++++ .../remote/common/remote.contribution.ts | 28 ++ src/vs/workbench/workbench.common.main.ts | 1 + 23 files changed, 696 insertions(+), 7 deletions(-) create mode 100644 src/vs/workbench/contrib/remote/browser/help-documentation-dark.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-documentation-hc.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-documentation-light.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-feedback-dark.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-feedback-hc.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-feedback-light.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-getting-started-dark.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-getting-started-hc.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-getting-started-light.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-report-issue-dark.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-report-issue-hc.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-report-issue-light.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-review-issues-dark.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-review-issues-hc.svg create mode 100644 src/vs/workbench/contrib/remote/browser/help-review-issues-light.svg create mode 100644 src/vs/workbench/contrib/remote/browser/remote-activity-bar.svg create mode 100644 src/vs/workbench/contrib/remote/browser/remote.ts create mode 100644 src/vs/workbench/contrib/remote/browser/remoteViewlet.css diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 149f1f4cebecf..95a9aff3298bc 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -19,6 +19,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { VIEWLET_ID as EXPLORER } from 'vs/workbench/contrib/files/common/files'; import { VIEWLET_ID as SCM } from 'vs/workbench/contrib/scm/common/scm'; import { VIEWLET_ID as DEBUG } from 'vs/workbench/contrib/debug/common/debug'; +import { VIEWLET_ID as REMOTE } from 'vs/workbench/contrib/remote/common/remote.contribution'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from 'vs/workbench/browser/viewlet'; @@ -79,6 +80,7 @@ interface IUserFriendlyViewDescriptor { id: string; name: string; when?: string; + group?: string; } const viewDescriptor: IJSONSchema = { @@ -99,6 +101,27 @@ const viewDescriptor: IJSONSchema = { } }; +const nestableViewDescriptor: IJSONSchema = { + type: 'object', + properties: { + id: { + description: localize('vscode.extension.contributes.view.id', 'Identifier of the view. Use this to register a data provider through `vscode.window.registerTreeDataProviderForView` API. Also to trigger activating your extension by registering `onView:${id}` event to `activationEvents`.'), + type: 'string' + }, + name: { + description: localize('vscode.extension.contributes.view.name', 'The human-readable name of the view. Will be shown'), + type: 'string' + }, + when: { + description: localize('vscode.extension.contributes.view.when', 'Condition which must be true to show this view'), + type: 'string' + }, + group: { + description: localize('vscode.extension.contributes.view.group', 'Nested group in the viewlet'), + type: 'string' + } + } +}; const viewsContribution: IJSONSchema = { description: localize('vscode.extension.contributes.views', "Contributes views to the editor"), type: 'object', @@ -126,6 +149,12 @@ const viewsContribution: IJSONSchema = { type: 'array', items: viewDescriptor, default: [] + }, + 'remote': { + description: localize('views.remote', "Contributes views to Remote container in the Activity bar"), + type: 'array', + items: nestableViewDescriptor, + default: [] } }, additionalProperties: { @@ -376,6 +405,12 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return null; } + const order = ExtensionIdentifier.equals(extension.description.identifier, container.extensionId) + ? index + 1 + : container.orderDelegate + ? container.orderDelegate.getOrder(item.group) + : undefined; + const viewDescriptor = { id: item.id, name: item.name, @@ -384,9 +419,10 @@ class ViewsExtensionHandler implements IWorkbenchContribution { canToggleVisibility: true, collapsed: this.showCollapsed(container), treeView: this.instantiationService.createInstance(CustomTreeView, item.id, item.name, container), - order: ExtensionIdentifier.equals(extension.description.identifier, container.extensionId) ? index + 1 : undefined, + order: order, extensionId: extension.description.identifier, - originalContainerId: entry.key + originalContainerId: entry.key, + group: item.group }; viewIds.push(viewDescriptor.id); @@ -440,6 +476,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { case 'explorer': return this.viewContainersRegistry.get(EXPLORER); case 'debug': return this.viewContainersRegistry.get(DEBUG); case 'scm': return this.viewContainersRegistry.get(SCM); + case 'remote': return this.viewContainersRegistry.get(REMOTE); default: return this.viewContainersRegistry.get(`workbench.view.extension.${value}`); } } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 2983c1092501d..23accec76fa88 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -382,7 +382,19 @@ export class ContributableViewsModel extends Disposable { return 0; } - return (this.getViewOrder(a) - this.getViewOrder(b)) || (a.id < b.id ? -1 : 1); + return (this.getViewOrder(a) - this.getViewOrder(b)) || this.getGroupOrderResult(a, b) || (a.id < b.id ? -1 : 1); + } + + private getGroupOrderResult(a: IViewDescriptor, b: IViewDescriptor) { + if (!a.group || !b.group) { + return 0; + } + + if (a.group === b.group) { + return 0; + } + + return a.group < b.group ? -1 : 1; } private getViewOrder(viewDescriptor: IViewDescriptor): number { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index c9ec1a841f638..92596fd942889 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -51,7 +51,7 @@ export interface IViewContainersRegistry { * * @returns the registered ViewContainer. */ - registerViewContainer(id: string, hideIfEmpty?: boolean, extensionId?: ExtensionIdentifier): ViewContainer; + registerViewContainer(id: string, hideIfEmpty?: boolean, extensionId?: ExtensionIdentifier, viewOrderDelegate?: ViewOrderDelegate): ViewContainer; /** * Deregisters the given view container @@ -67,8 +67,12 @@ export interface IViewContainersRegistry { get(id: string): ViewContainer | undefined; } +interface ViewOrderDelegate { + getOrder(group?: string): number | undefined; +} + export class ViewContainer { - protected constructor(readonly id: string, readonly hideIfEmpty: boolean, readonly extensionId?: ExtensionIdentifier) { } + protected constructor(readonly id: string, readonly hideIfEmpty: boolean, readonly extensionId?: ExtensionIdentifier, readonly orderDelegate?: ViewOrderDelegate) { } } class ViewContainersRegistryImpl extends Disposable implements IViewContainersRegistry { @@ -85,7 +89,7 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe return values(this.viewContainers); } - registerViewContainer(id: string, hideIfEmpty?: boolean, extensionId?: ExtensionIdentifier): ViewContainer { + registerViewContainer(id: string, hideIfEmpty?: boolean, extensionId?: ExtensionIdentifier, viewOrderDelegate?: ViewOrderDelegate): ViewContainer { const existing = this.viewContainers.get(id); if (existing) { return existing; @@ -93,7 +97,7 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe const viewContainer = new class extends ViewContainer { constructor() { - super(id, !!hideIfEmpty, extensionId); + super(id, !!hideIfEmpty, extensionId, viewOrderDelegate); } }; this.viewContainers.set(id, viewContainer); @@ -126,6 +130,8 @@ export interface IViewDescriptor { readonly when?: ContextKeyExpr; + readonly group?: string; + readonly order?: number; readonly weight?: number; diff --git a/src/vs/workbench/contrib/remote/browser/help-documentation-dark.svg b/src/vs/workbench/contrib/remote/browser/help-documentation-dark.svg new file mode 100644 index 0000000000000..2673902c68405 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-documentation-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-documentation-hc.svg b/src/vs/workbench/contrib/remote/browser/help-documentation-hc.svg new file mode 100644 index 0000000000000..e8dc8205bab38 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-documentation-hc.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-documentation-light.svg b/src/vs/workbench/contrib/remote/browser/help-documentation-light.svg new file mode 100644 index 0000000000000..4a3009baeee47 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-documentation-light.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-feedback-dark.svg b/src/vs/workbench/contrib/remote/browser/help-feedback-dark.svg new file mode 100644 index 0000000000000..5d99408934e83 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-feedback-dark.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-feedback-hc.svg b/src/vs/workbench/contrib/remote/browser/help-feedback-hc.svg new file mode 100644 index 0000000000000..941430e9dd6a9 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-feedback-hc.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-feedback-light.svg b/src/vs/workbench/contrib/remote/browser/help-feedback-light.svg new file mode 100644 index 0000000000000..72437202b7252 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-feedback-light.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-getting-started-dark.svg b/src/vs/workbench/contrib/remote/browser/help-getting-started-dark.svg new file mode 100644 index 0000000000000..0ea65d83198b2 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-getting-started-dark.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-getting-started-hc.svg b/src/vs/workbench/contrib/remote/browser/help-getting-started-hc.svg new file mode 100644 index 0000000000000..5bb05d3d8c550 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-getting-started-hc.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-getting-started-light.svg b/src/vs/workbench/contrib/remote/browser/help-getting-started-light.svg new file mode 100644 index 0000000000000..46cde7f7cc051 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-getting-started-light.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-report-issue-dark.svg b/src/vs/workbench/contrib/remote/browser/help-report-issue-dark.svg new file mode 100644 index 0000000000000..0117ceb7ded96 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-report-issue-dark.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-report-issue-hc.svg b/src/vs/workbench/contrib/remote/browser/help-report-issue-hc.svg new file mode 100644 index 0000000000000..b0c521b7dc6b1 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-report-issue-hc.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-report-issue-light.svg b/src/vs/workbench/contrib/remote/browser/help-report-issue-light.svg new file mode 100644 index 0000000000000..5da9322b6a931 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-report-issue-light.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-review-issues-dark.svg b/src/vs/workbench/contrib/remote/browser/help-review-issues-dark.svg new file mode 100644 index 0000000000000..21eec9cbcb85f --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-review-issues-dark.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-review-issues-hc.svg b/src/vs/workbench/contrib/remote/browser/help-review-issues-hc.svg new file mode 100644 index 0000000000000..94013ea52ae7b --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-review-issues-hc.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/help-review-issues-light.svg b/src/vs/workbench/contrib/remote/browser/help-review-issues-light.svg new file mode 100644 index 0000000000000..826d0eefbf476 --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/help-review-issues-light.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/vs/workbench/contrib/remote/browser/remote-activity-bar.svg b/src/vs/workbench/contrib/remote/browser/remote-activity-bar.svg new file mode 100644 index 0000000000000..029e6b051c28a --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/remote-activity-bar.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts new file mode 100644 index 0000000000000..ea530271414bb --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -0,0 +1,419 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./remoteViewlet'; +import * as nls from 'vs/nls'; +import * as dom from 'vs/base/browser/dom'; +import { URI } from 'vs/base/common/uri'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { VIEWLET_ID, VIEW_CONTAINER } from 'vs/workbench/contrib/remote/common/remote.contribution'; +import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { IAddedViewDescriptorRef } from 'vs/workbench/browser/parts/views/views'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IViewDescriptor, IViewsRegistry, Extensions } from 'vs/workbench/common/views'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; +import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { ITreeRenderer, ITreeNode, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; +import { Event } from 'vs/base/common/event'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; + +interface HelpInformation { + extensionDescription: IExtensionDescription; + getStarted?: string; + documentation?: string; + feedback?: string; + issues?: string; +} + +const remoteHelpExtPoint = ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'remoteHelp', + jsonSchema: { + description: nls.localize('RemoteHelpInformationExtPoint', 'Contributes help information for Remote'), + type: 'object', + properties: { + 'getStarted': { + description: nls.localize('RemoteHelpInformationExtPoint.getStarted', "The url to your project's Getting Started page"), + type: 'string' + }, + 'documentation': { + description: nls.localize('RemoteHelpInformationExtPoint.documentation', "The url to your project's documentation page"), + type: 'string' + }, + 'feedback': { + description: nls.localize('RemoteHelpInformationExtPoint.feedback', "The url to your project's feedback reporter"), + type: 'string' + }, + 'issues': { + description: nls.localize('RemoteHelpInformationExtPoint.issues', "The url to your project's issues list"), + type: 'string' + } + } + } +}); + +interface IViewModel { + helpInformations: HelpInformation[]; +} + +class HelpTreeVirtualDelegate implements IListVirtualDelegate { + getHeight(element: IHelpItem): number { + return 22; + } + + getTemplateId(element: IHelpItem): string { + return 'HelpItemTemplate'; + } +} + +interface IHelpItemTemplateData { + parent: HTMLElement; + icon: HTMLElement; +} + +class HelpTreeRenderer implements ITreeRenderer { + templateId: string = 'HelpItemTemplate'; + + renderTemplate(container: HTMLElement): IHelpItemTemplateData { + dom.addClass(container, 'remote-help-tree-node-item'); + + const icon = dom.append(container, dom.$('.remote-help-tree-node-item-icon')); + + const data = Object.create(null); + data.parent = container; + data.icon = icon; + + return data; + } + + renderElement(element: ITreeNode, index: number, templateData: IHelpItemTemplateData, height: number | undefined): void { + const container = templateData.parent; + dom.append(container, templateData.icon); + dom.addClass(templateData.icon, element.element.key); + const labelContainer = dom.append(container, dom.$('.help-item-label')); + labelContainer.innerText = element.element.label; + } + + disposeTemplate(templateData: IHelpItemTemplateData): void { + + } +} + +class HelpDataSource implements IAsyncDataSource { + hasChildren(element: any) { + return element instanceof HelpModel; + } + + getChildren(element: any) { + if (element instanceof HelpModel) { + return element.items; + } + + return []; + } +} + +interface IHelpItem { + key: string; + label: string; + handleClick(): Promise; +} + +class HelpItem implements IHelpItem { + constructor( + public key: string, + public label: string, + public values: { extensionDescription: IExtensionDescription; url: string }[], + private openerService: IOpenerService, + private quickInputService: IQuickInputService + ) { + } + + async handleClick() { + if (this.values.length > 1) { + let actions = this.values.map(value => { + return { + label: value.extensionDescription.displayName || value.extensionDescription.identifier.value, + description: value.url + }; + }); + + const action = await this.quickInputService.pick(actions, { placeHolder: nls.localize('pickRemoteExtension', "Select url to open") }); + + if (action) { + await this.openerService.open(URI.parse(action.label)); + } + } else { + await this.openerService.open(URI.parse(this.values[0].url)); + } + } +} + +class IssueReporterItem implements IHelpItem { + constructor( + public key: string, + public label: string, + public extensionDescriptions: IExtensionDescription[], + private quickInputService: IQuickInputService, + private commandService: ICommandService + ) { + } + + async handleClick() { + if (this.extensionDescriptions.length > 1) { + let actions = this.extensionDescriptions.map(extension => { + return { + label: extension.displayName || extension.identifier.value, + identifier: extension.identifier + }; + }); + + const action = await this.quickInputService.pick(actions, { placeHolder: nls.localize('pickRemoteExtensionToReportIssue', "Select an extension to report issue") }); + + if (action) { + await this.commandService.executeCommand('workbench.action.openIssueReporter', [action.identifier.value]); + } + } else { + await this.commandService.executeCommand('workbench.action.openIssueReporter', [this.extensionDescriptions[0].identifier.value]); + } + } +} + +class HelpModel { + items: IHelpItem[]; + + constructor( + viewModel: IViewModel, + openerService: IOpenerService, + quickInputService: IQuickInputService, + commandService: ICommandService + ) { + let helpItems: IHelpItem[] = []; + const getStarted = viewModel.helpInformations.filter(info => info.getStarted); + + if (getStarted.length) { + helpItems.push(new HelpItem( + 'getStarted', + nls.localize('remote.help.getStarted', "Get Started"), + getStarted.map((info: HelpInformation) => ({ + extensionDescription: info.extensionDescription, + url: info.getStarted! + })), + openerService, + quickInputService + )); + } + + const documentation = viewModel.helpInformations.filter(info => info.documentation); + + if (documentation.length) { + helpItems.push(new HelpItem( + 'documentation', + nls.localize('remote.help.documentation', "Read Documentation"), + documentation.map((info: HelpInformation) => ({ + extensionDescription: info.extensionDescription, + url: info.documentation! + })), + openerService, + quickInputService + )); + } + + const feedback = viewModel.helpInformations.filter(info => info.feedback); + + if (feedback.length) { + helpItems.push(new HelpItem( + 'feedback', + nls.localize('remote.help.feedback', "Provide Feedback"), + feedback.map((info: HelpInformation) => ({ + extensionDescription: info.extensionDescription, + url: info.feedback! + })), + openerService, + quickInputService + )); + } + + const issues = viewModel.helpInformations.filter(info => info.issues); + + if (issues.length) { + helpItems.push(new HelpItem( + 'issues', + nls.localize('remote.help.issues', "Review Issues"), + issues.map((info: HelpInformation) => ({ + extensionDescription: info.extensionDescription, + url: info.issues! + })), + openerService, + quickInputService + )); + } + + if (helpItems.length) { + helpItems.push(new IssueReporterItem( + 'issueReporter', + nls.localize('remote.help.report', "Report Issue"), + viewModel.helpInformations.map(info => info.extensionDescription), + quickInputService, + commandService + )); + } + + if (helpItems.length) { + this.items = helpItems; + } + } +} + +class HelpPanel extends ViewletPanel { + static readonly ID = '~remote.helpPanel'; + static readonly TITLE = nls.localize('remote.help', "Help and feedback"); + private tree!: WorkbenchAsyncDataTree; + + constructor( + protected viewModel: IViewModel, + options: IViewletPanelOptions, + @IKeybindingService protected keybindingService: IKeybindingService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IContextKeyService protected contextKeyService: IContextKeyService, + @IConfigurationService protected configurationService: IConfigurationService, + @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IOpenerService protected openerService: IOpenerService, + @IQuickInputService protected quickInputService: IQuickInputService, + @ICommandService protected commandService: ICommandService + + + ) { + super(options, keybindingService, contextMenuService, configurationService, contextKeyService); + } + + protected renderBody(container: HTMLElement): void { + dom.addClass(container, 'remote-help'); + const treeContainer = document.createElement('div'); + dom.addClass(treeContainer, 'remote-help-content'); + container.appendChild(treeContainer); + + this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, + treeContainer, + new HelpTreeVirtualDelegate(), + [new HelpTreeRenderer()], + new HelpDataSource(), + { + keyboardSupport: true, + } + ); + + const model = new HelpModel(this.viewModel, this.openerService, this.quickInputService, this.commandService); + + this.tree.setInput(model); + + const helpItemNavigator = this._register(new TreeResourceNavigator2(this.tree, { openOnFocus: false, openOnSelection: false })); + + this._register(Event.debounce(helpItemNavigator.onDidOpenResource, (last, event) => event, 75, true)(e => { + e.element.handleClick(); + })); + } + + protected layoutBody(height: number, width: number): void { + this.tree.layout(height, width); + } +} + +class HelpPanelDescriptor implements IViewDescriptor { + readonly id = HelpPanel.ID; + readonly name = HelpPanel.TITLE; + readonly ctorDescriptor: { ctor: any, arguments?: any[] }; + readonly canToggleVisibility = true; + readonly hideByDefault = false; + readonly workspace = true; + + constructor(viewModel: IViewModel) { + this.ctorDescriptor = { ctor: HelpPanel, arguments: [viewModel] }; + } +} + + +export class RemoteViewlet extends ViewContainerViewlet implements IViewModel { + private helpPanelDescriptor = new HelpPanelDescriptor(this); + + helpInformations: HelpInformation[] = []; + + constructor( + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, + @ITelemetryService telemetryService: ITelemetryService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IStorageService storageService: IStorageService, + @IConfigurationService configurationService: IConfigurationService, + @IInstantiationService instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService + ) { + super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + + remoteHelpExtPoint.setHandler((extensions) => { + let helpInformation: HelpInformation[] = []; + for (let extension of extensions) { + this._handleRemoteInfoExtensionPoint(extension, helpInformation); + } + + this.helpInformations = helpInformation; + + const viewsRegistry = Registry.as(Extensions.ViewsRegistry); + if (this.helpInformations.length) { + viewsRegistry.registerViews([this.helpPanelDescriptor], VIEW_CONTAINER); + } else { + viewsRegistry.deregisterViews([this.helpPanelDescriptor], VIEW_CONTAINER); + } + }); + } + + private _handleRemoteInfoExtensionPoint(extension: IExtensionPointUser, helpInformation: HelpInformation[]) { + if (!extension.value.documentation && !extension.value.feedback && !extension.value.getStarted && !extension.value.issues) { + return; + } + + helpInformation.push({ + extensionDescription: extension.description, + getStarted: extension.value.getStarted, + documentation: extension.value.documentation, + feedback: extension.value.feedback, + issues: extension.value.issues + }); + } + + onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] { + // too late, already added to the view model + return super.onDidAddViews(added); + } + + getTitle(): string { + const title = nls.localize('remote.explorer', "Remote Explorer"); + return title; + } +} + +Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( + RemoteViewlet, + VIEWLET_ID, + nls.localize('remote.explorer', "Remote Explorer"), + 'remote', + 4 +)); diff --git a/src/vs/workbench/contrib/remote/browser/remoteViewlet.css b/src/vs/workbench/contrib/remote/browser/remoteViewlet.css new file mode 100644 index 0000000000000..86bd4b76dc9cd --- /dev/null +++ b/src/vs/workbench/contrib/remote/browser/remoteViewlet.css @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .activitybar>.content .monaco-action-bar .action-label.remote { + -webkit-mask: url('remote-activity-bar.svg') no-repeat 50% 50%; +} + +.remote-help-content .monaco-list .monaco-list-row .remote-help-tree-node-item { + display: flex; + height: 22px; + line-height: 22px; + flex: 1; + text-overflow: ellipsis; + overflow: hidden; + flex-wrap: nowrap; +} + +.remote-help-content .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon { + background-size: 16px; + background-position: left center; + background-repeat: no-repeat; + padding-right: 6px; + width: 16px; + height: 22px; + -webkit-font-smoothing: antialiased; +} + +.remote-help-content .monaco-list .monaco-list-row .monaco-tl-twistie { + width: 0px !important; +} + +.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.getStarted { + background-image: url('help-getting-started-light.svg') +} + +.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.documentation { + background-image: url('help-documentation-light.svg') +} + +.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.feedback { + background-image: url('help-feedback-light.svg') +} + +.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issues { + background-image: url('help-review-issues-light.svg') +} + +.vs .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issueReporter { + background-image: url('help-report-issue-light.svg') +} + +.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.getStarted { + background-image: url('help-getting-started-dark.svg') +} + +.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.documentation { + background-image: url('help-documentation-dark.svg') +} + +.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.feedback { + background-image: url('help-feedback-dark.svg') +} + +.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issues { + background-image: url('help-review-issues-dark.svg') +} + +.vs-dark .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issueReporter { + background-image: url('help-report-issue-dark.svg') +} + +.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.getStarted { + background-image: url('help-getting-started-hc.svg') +} + +.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.documentation { + background-image: url('help-documentation-hc.svg') +} + +.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.feedback { + background-image: url('help-feedback-hc.svg') +} + +.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issues { + background-image: url('help-review-issues-hc.svg') +} + +.hc-black .monaco-list .monaco-list-row .remote-help-tree-node-item>.remote-help-tree-node-item-icon.issueReporter { + background-image: url('help-report-issue-hc.svg') +} diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 9235c739fb014..832193a8c2203 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -17,6 +17,34 @@ import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/c import { localize } from 'vs/nls'; import { joinPath } from 'vs/base/common/resources'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; + +export const VIEWLET_ID = 'workbench.view.remote'; +export const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer( + VIEWLET_ID, + true, + undefined, + { + getOrder: (group?: string) => { + if (!group) { + return; + } + + let matches = /^targets@(\d+)$/.exec(group); + if (matches) { + return -1000; + } + + matches = /^details@(\d+)$/.exec(group); + + if (matches) { + return -500; + } + + return; + } + } +); export class LabelContribution implements IWorkbenchContribution { constructor( diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 3558c2ffef3be..e54be8a3da6b0 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -194,6 +194,7 @@ import 'vs/workbench/contrib/tasks/browser/task.contribution'; // Remote import 'vs/workbench/contrib/remote/common/remote.contribution'; +import 'vs/workbench/contrib/remote/browser/remote'; // Emmet import 'vs/workbench/contrib/emmet/browser/emmet.contribution'; From 9649fa56d9eaa6f682c691bf03a47634097099ec Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 14 Aug 2019 17:48:37 -0700 Subject: [PATCH 192/613] Use undefined instead of null in IEditorOpeningEvent and IOpenEditorOverride --- .../workbench/browser/parts/editor/editor.ts | 2 +- .../browser/debugConfigurationManager.ts | 3 +- .../contrib/files/browser/fileActions.ts | 2 +- .../preferences/browser/preferencesActions.ts | 2 +- .../preferences/browser/settingsEditor2.ts | 4 +-- .../contrib/search/browser/searchView.ts | 2 +- .../services/editor/common/editorService.ts | 4 +-- .../preferences/browser/preferencesService.ts | 35 ++++++++++--------- .../preferences/common/preferences.ts | 10 +++--- 9 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 5a56c61b3f039..634812bfa8415 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -74,7 +74,7 @@ export interface IEditorOpeningEvent extends IEditorIdentifier { * Allows to prevent the opening of an editor by providing a callback * that will be executed instead. By returning another editor promise * it is possible to override the opening with another editor. It is ok - * to return a promise that resolves to NULL to prevent the opening + * to return a promise that resolves to `undefined` to prevent the opening * alltogether. */ prevent(callback: () => undefined | Promise): void; diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 637086088b3c6..6682ac405d7b7 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -35,6 +35,7 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c import { onUnexpectedError } from 'vs/base/common/errors'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { withUndefinedAsNull } from 'vs/base/common/types'; const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); jsonRegistry.registerSchema(launchSchemaId, launchSchema); @@ -647,6 +648,6 @@ class UserLaunch extends AbstractLaunch implements ILaunch { } openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Promise<{ editor: IEditor | null, created: boolean }> { - return this.preferencesService.openGlobalSettings(false, { preserveFocus }).then(editor => ({ editor, created: false })); + return this.preferencesService.openGlobalSettings(false, { preserveFocus }).then(editor => ({ editor: withUndefinedAsNull(editor), created: false })); } } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 9d87a9c16b18a..e204c90c0bcd6 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -471,7 +471,7 @@ export class GlobalCompareResourcesAction extends Action { override: this.editorService.openEditor({ leftResource: activeResource, rightResource: resource - }).then(() => null) + }).then(() => undefined) }; } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts index 34d9362a283ec..b93c9b2fd62d2 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts @@ -225,7 +225,7 @@ export class OpenFolderSettingsAction extends Action { return this.preferencesService.openFolderSettings(workspaceFolder.uri); } - return null; + return undefined; }); } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 986cef518185a..1382fd0df3851 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -458,12 +458,12 @@ export class SettingsEditor2 extends BaseEditor { } } - switchToSettingsFile(): Promise { + switchToSettingsFile(): Promise { const query = parseQuery(this.searchWidget.getValue()); return this.openSettingsFile(query.query); } - private openSettingsFile(query?: string): Promise { + private openSettingsFile(query?: string): Promise { const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget; const options: ISettingsEditorOptions = { query }; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 2daad95ca686e..1683a0325d191 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1455,7 +1455,7 @@ export class SearchView extends ViewletPanel { this.openSettings('.exclude'); } - private openSettings(query: string): Promise { + private openSettings(query: string): Promise { const options: ISettingsEditorOptions = { query }; return this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? this.preferencesService.openWorkspaceSettings(undefined, options) : diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index ab79c65ff489e..be82e683eca27 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -36,7 +36,7 @@ export interface IOpenEditorOverride { * If defined, will prevent the opening of an editor and replace the resulting * promise with the provided promise for the openEditor() call. */ - override?: Promise; + override?: Promise; } export interface IVisibleEditor extends IEditor { @@ -185,4 +185,4 @@ export interface IEditorService { * Converts a lightweight input to a workbench editor input. */ createInput(input: IResourceEditor): IEditorInput | null; -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 8788a416741e3..4ef77c8b683eb 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -38,6 +38,7 @@ import { defaultKeybindingsContents, DefaultKeybindingsEditorModel, DefaultSetti import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { withNullAsUndefined } from 'vs/base/common/types'; const emptyEditableSettingsContent = '{\n}'; @@ -196,7 +197,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.editorService.openEditor({ resource: this.userSettingsResource }); } - openSettings(jsonEditor: boolean | undefined, query: string | undefined): Promise { + openSettings(jsonEditor: boolean | undefined, query: string | undefined): Promise { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -217,7 +218,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic .then(() => this.editorGroupService.activeGroup.activeControl!); } - openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -227,16 +228,16 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openOrSwitchSettings2(ConfigurationTarget.USER_LOCAL, undefined, options, group); } - async openRemoteSettings(): Promise { + async openRemoteSettings(): Promise { const environment = await this.remoteAgentService.getEnvironment(); if (environment) { await this.createIfNotExists(environment.settingsPath, emptyEditableSettingsContent); - return this.editorService.openEditor({ resource: environment.settingsPath, options: { pinned: true, revealIfOpened: true } }); + return this.editorService.openEditor({ resource: environment.settingsPath, options: { pinned: true, revealIfOpened: true } }).then(withNullAsUndefined); } - return null; + return undefined; } - openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -251,7 +252,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE, undefined, options, group); } - async openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + async openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -328,7 +329,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic })); } - private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { + private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { const editorInput = this.getActiveSettingsEditorInput(group); if (editorInput) { const editorInputResource = editorInput.master.getResource(); @@ -339,11 +340,11 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.doOpenSettings(configurationTarget, resource, options, group); } - private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { + private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Promise { return this.doOpenSettings2(configurationTarget, folderUri, options, group); } - private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { const openSplitJSON = !!this.configurationService.getValue(USE_SPLIT_JSON_SETTING); if (openSplitJSON) { return this.doOpenSplitJSON(configurationTarget, resource, options, group); @@ -365,14 +366,14 @@ export class PreferencesService extends Disposable implements IPreferencesServic return Promise.all([ this.editorService.openEditor({ resource: this.defaultSettingsRawResource, options: { pinned: true, preserveFocus: true, revealIfOpened: true }, label: nls.localize('defaultSettings', "Default Settings"), description: '' }), this.editorService.openEditor(editableSettingsEditorInput, { pinned: true, revealIfOpened: true }, sideEditorGroup.id) - ]).then(([defaultEditor, editor]) => editor); + ]).then(([defaultEditor, editor]) => withNullAsUndefined(editor)); } else { - return this.editorService.openEditor(editableSettingsEditorInput, SettingsEditorOptions.create(options), group); + return this.editorService.openEditor(editableSettingsEditorInput, SettingsEditorOptions.create(options), group).then(withNullAsUndefined); } }); } - private doOpenSplitJSON(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { + private doOpenSplitJSON(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise { return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource) .then(editableSettingsEditorInput => { if (!options) { @@ -384,7 +385,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(configurationTarget)); const preferencesEditorInput = new PreferencesEditorInput(this.getPreferencesEditorInputName(configurationTarget, resource), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, editableSettingsEditorInput); this.lastOpenedSettingsInput = preferencesEditorInput; - return this.editorService.openEditor(preferencesEditorInput, SettingsEditorOptions.create(options), group); + return this.editorService.openEditor(preferencesEditorInput, SettingsEditorOptions.create(options), group).then(withNullAsUndefined); }); } @@ -392,7 +393,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.instantiationService.createInstance(Settings2EditorModel, this.getDefaultSettings(ConfigurationTarget.USER_LOCAL)); } - private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): Promise { + private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): Promise { const input = this.settingsEditor2Input; const settingsOptions: ISettingsEditorOptions = { ...options, @@ -400,7 +401,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic folderUri }; - return this.editorService.openEditor(input, SettingsEditorOptions.create(settingsOptions), group); + return this.editorService.openEditor(input, SettingsEditorOptions.create(settingsOptions), group).then(withNullAsUndefined); } private async doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup, options?: ISettingsEditorOptions): Promise { @@ -633,4 +634,4 @@ export class PreferencesService extends Disposable implements IPreferencesServic } } -registerSingleton(IPreferencesService, PreferencesService); \ No newline at end of file +registerSingleton(IPreferencesService, PreferencesService); diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index abf288178cc3a..e3ffd3edf175c 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -202,11 +202,11 @@ export interface IPreferencesService { createSettings2EditorModel(): Settings2EditorModel; // TODO openRawDefaultSettings(): Promise; - openSettings(jsonEditor: boolean | undefined, query: string | undefined): Promise; - openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; - openRemoteSettings(): Promise; - openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; - openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; + openSettings(jsonEditor: boolean | undefined, query: string | undefined): Promise; + openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; + openRemoteSettings(): Promise; + openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; + openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Promise; openGlobalKeybindingSettings(textual: boolean): Promise; openDefaultKeybindingsFile(): Promise; From d145b27f0511a0721f9ec6f60ec8f152b64f197a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 14 Aug 2019 18:00:03 -0700 Subject: [PATCH 193/613] Change openEditor to return undefined instead of null For #70020 --- .../contrib/debug/browser/breakpointsView.ts | 4 ++-- .../debug/browser/debugConfigurationManager.ts | 4 ++-- .../contrib/debug/common/debugSource.ts | 3 ++- .../contrib/files/browser/explorerViewlet.ts | 5 +++-- .../browser/keyboardLayoutPicker.ts | 4 ++-- .../services/editor/browser/editorService.ts | 18 +++++++++--------- .../services/editor/common/editorService.ts | 10 +++++----- .../services/history/browser/history.ts | 2 +- .../preferences/browser/preferencesService.ts | 14 +++++++------- .../services/preferences/common/preferences.ts | 4 ++-- 10 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 72de87a6f1cd6..4c8f5b36ee8d7 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -543,9 +543,9 @@ class FunctionBreakpointInputRenderer implements IListRenderer { +export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): Promise { if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) { - return Promise.resolve(null); + return Promise.resolve(undefined); } const selection = breakpoint.endLineNumber ? { diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 6682ac405d7b7..3e71419c14a59 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -578,7 +578,7 @@ class Launch extends AbstractLaunch implements ILaunch { pinned: created, revealIfVisible: true }, - }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor, created }))); + }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor: withUndefinedAsNull(editor), created }))); }, (error: Error) => { throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error.message)); }); @@ -614,7 +614,7 @@ class WorkspaceLaunch extends AbstractLaunch implements ILaunch { return this.editorService.openEditor({ resource: this.contextService.getWorkspace().configuration!, options: { preserveFocus } - }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor, created: false })); + }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor: withUndefinedAsNull(editor), created: false })); } } diff --git a/src/vs/workbench/contrib/debug/common/debugSource.ts b/src/vs/workbench/contrib/debug/common/debugSource.ts index 133e3f8bec8d9..fa0376d936e69 100644 --- a/src/vs/workbench/contrib/debug/common/debugSource.ts +++ b/src/vs/workbench/contrib/debug/common/debugSource.ts @@ -13,6 +13,7 @@ import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/ import { Schemas } from 'vs/base/common/network'; import { isUri } from 'vs/workbench/contrib/debug/common/debugUtils'; import { ITextEditor } from 'vs/workbench/common/editor'; +import { withUndefinedAsNull } from 'vs/base/common/types'; export const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source"); @@ -104,7 +105,7 @@ export class Source { revealInCenterIfOutsideViewport: true, pinned: pinned || (!preserveFocus && !this.inMemory) } - }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(withUndefinedAsNull); } static getEncodedDebugData(modelUri: uri): { name: string, path: string, sessionId?: string, sourceReference?: number } { diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index c3e3a622b58d1..c9713c80989ea 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -34,6 +34,7 @@ import { ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { KeyChord, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { withUndefinedAsNull } from 'vs/base/common/types'; export class ExplorerViewletViewsContribution extends Disposable implements IWorkbenchContribution { @@ -202,7 +203,7 @@ export class ExplorerViewlet extends ViewContainerViewlet { openEditorsView.setStructuralRefreshDelay(delay); } - let openedEditor: IEditor | null = null; + let openedEditor: IEditor | undefined; try { openedEditor = await this.editorService.openEditor(editor, options, group); } catch (error) { @@ -214,7 +215,7 @@ export class ExplorerViewlet extends ViewContainerViewlet { } } - return openedEditor; + return withUndefinedAsNull(openedEditor); }); const explorerInstantiator = this.instantiationService.createChild(new ServiceCollection([IEditorService, delegatingEditorService])); diff --git a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts index 35a40b06fea10..7180550bc6550 100644 --- a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts +++ b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts @@ -158,9 +158,9 @@ export class KeyboardLayoutPickerAction extends Action { await this.fileService.resolve(file).then(undefined, (error) => { return this.fileService.createFile(file, VSBuffer.fromString(KeyboardLayoutPickerAction.DEFAULT_CONTENT)); - }).then((stat): Promise | null => { + }).then((stat): Promise | undefined => { if (!stat) { - return null; + return undefined; } return this.editorService.openEditor({ resource: stat.resource, diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 3bd6b03f963d2..bf36c64c500b6 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -216,11 +216,11 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#region openEditor() - openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IEditorInput | IResourceEditor, optionsOrGroup?: IEditorOptions | ITextEditorOptions | IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, group?: GroupIdentifier): Promise { + openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IEditorInput | IResourceEditor, optionsOrGroup?: IEditorOptions | ITextEditorOptions | IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, group?: GroupIdentifier): Promise { // Typed Editor Support if (editor instanceof EditorInput) { @@ -240,11 +240,11 @@ export class EditorService extends Disposable implements EditorServiceImpl { return this.doOpenEditor(targetGroup, typedInput, editorOptions); } - return Promise.resolve(null); + return Promise.resolve(undefined); } - protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { - return group.openEditor(editor, options); + protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { + return group.openEditor(editor, options).then(withNullAsUndefined); } private findTargetGroup(input: IEditorInput, options?: IEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): IEditorGroup { @@ -651,7 +651,7 @@ export class DelegatingEditorService extends EditorService { this.editorOpenHandler = handler; } - protected async doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { + protected async doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { if (!this.editorOpenHandler) { return super.doOpenEditor(group, editor, options); } diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index be82e683eca27..5664c9c8c4530 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -117,13 +117,13 @@ export interface IEditorService { * active group. Use `SIDE_GROUP_TYPE` to open the editor in a new editor group to the side * of the currently active group. * - * @returns the editor that opened or NULL if the operation failed or the editor was not + * @returns the editor that opened or `undefined` if the operation failed or the editor was not * opened to be active. */ - openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; + openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; /** * Open editors in an editor group. diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 9ae41e0797795..23ad652c71c0f 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -453,7 +453,7 @@ export class HistoryService extends Disposable implements IHistoryService { this.doNavigate(this.stack[this.index], !acrossEditors).finally(() => this.navigatingInStack = false); } - private doNavigate(location: IStackEntry, withSelection: boolean): Promise { + private doNavigate(location: IStackEntry, withSelection: boolean): Promise { const options: ITextEditorOptions = { revealIfOpened: true // support to navigate across editor groups }; diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 4ef77c8b683eb..1e579f2968b1d 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -189,11 +189,11 @@ export class PreferencesService extends Disposable implements IPreferencesServic return null; } - openRawDefaultSettings(): Promise { + openRawDefaultSettings(): Promise { return this.editorService.openEditor({ resource: this.defaultSettingsRawResource }); } - openRawUserSettings(): Promise { + openRawUserSettings(): Promise { return this.editorService.openEditor({ resource: this.userSettingsResource }); } @@ -232,7 +232,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic const environment = await this.remoteAgentService.getEnvironment(); if (environment) { await this.createIfNotExists(environment.settingsPath, emptyEditableSettingsContent); - return this.editorService.openEditor({ resource: environment.settingsPath, options: { pinned: true, revealIfOpened: true } }).then(withNullAsUndefined); + return this.editorService.openEditor({ resource: environment.settingsPath, options: { pinned: true, revealIfOpened: true } }); } return undefined; } @@ -307,7 +307,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true, revealIfOpened: true }).then(() => undefined); } - openDefaultKeybindingsFile(): Promise { + openDefaultKeybindingsFile(): Promise { return this.editorService.openEditor({ resource: this.defaultKeybindingsResource, label: nls.localize('defaultKeybindings', "Default Keybindings") }); } @@ -368,7 +368,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.editorService.openEditor(editableSettingsEditorInput, { pinned: true, revealIfOpened: true }, sideEditorGroup.id) ]).then(([defaultEditor, editor]) => withNullAsUndefined(editor)); } else { - return this.editorService.openEditor(editableSettingsEditorInput, SettingsEditorOptions.create(options), group).then(withNullAsUndefined); + return this.editorService.openEditor(editableSettingsEditorInput, SettingsEditorOptions.create(options), group); } }); } @@ -385,7 +385,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(configurationTarget)); const preferencesEditorInput = new PreferencesEditorInput(this.getPreferencesEditorInputName(configurationTarget, resource), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, editableSettingsEditorInput); this.lastOpenedSettingsInput = preferencesEditorInput; - return this.editorService.openEditor(preferencesEditorInput, SettingsEditorOptions.create(options), group).then(withNullAsUndefined); + return this.editorService.openEditor(preferencesEditorInput, SettingsEditorOptions.create(options), group); }); } @@ -401,7 +401,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic folderUri }; - return this.editorService.openEditor(input, SettingsEditorOptions.create(settingsOptions), group).then(withNullAsUndefined); + return this.editorService.openEditor(input, SettingsEditorOptions.create(settingsOptions), group); } private async doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup, options?: ISettingsEditorOptions): Promise { diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index e3ffd3edf175c..195631aa108d0 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -201,7 +201,7 @@ export interface IPreferencesService { createPreferencesEditorModel(uri: URI): Promise | null>; createSettings2EditorModel(): Settings2EditorModel; // TODO - openRawDefaultSettings(): Promise; + openRawDefaultSettings(): Promise; openSettings(jsonEditor: boolean | undefined, query: string | undefined): Promise; openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; openRemoteSettings(): Promise; @@ -209,7 +209,7 @@ export interface IPreferencesService { openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Promise; switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Promise; openGlobalKeybindingSettings(textual: boolean): Promise; - openDefaultKeybindingsFile(): Promise; + openDefaultKeybindingsFile(): Promise; configureSettingsForLanguage(language: string | null): void; } From df96e402b2c1bcd7a9cc22f647a1c33f9a906677 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 14 Aug 2019 18:06:01 -0700 Subject: [PATCH 194/613] Improve jsdoc section of walkthrough Fixes #71023 As discussed in #75033 --- .../walkThrough/browser/editor/vs_code_editor_walkthrough.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md index 43e8bc83a893b..ffd95c8971832 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md @@ -77,6 +77,9 @@ new Book("The Martian", "Andy Weir"); /** * Represents a book. + * + * @param {string} title Title of the book + * @param {string} author Who wrote the book */ function Book(title, author) { this.title = title; @@ -84,7 +87,7 @@ function Book(title, author) { } ``` -> **JSDoc Tip:** The example above also showcased another way to get IntelliSense hints by using `JSDoc` comments. You can try this out by invoking the `Book` function and seeing the enhanced context in the IntelliSense menu for the function as well as parameters. +> **JSDoc Tip:** VS Code's IntelliSense uses JSDoc comments to provide richer suggestions. The types and documentation from JSDoc comments show up when you hover over a reference to `Book` or in IntelliSense when you create a new instance of `Book`. ### Refactoring via Extraction From 44a48f75d64333b8e33a1fe4f57f6a60553b1e6b Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 15 Aug 2019 06:38:47 +0200 Subject: [PATCH 195/613] Add installer assets for OSS (#79045) --- build/win32/code.iss | 4 ++-- resources/win32/inno-big-100.bmp | Bin 0 -> 154544 bytes .../win32/{inno-big.bmp => inno-big-125.bmp} | Bin 206038 -> 222392 bytes resources/win32/inno-big-150.bmp | Bin 0 -> 339716 bytes resources/win32/inno-big-175.bmp | Bin 0 -> 455976 bytes resources/win32/inno-big-200.bmp | Bin 0 -> 594392 bytes resources/win32/inno-big-225.bmp | Bin 0 -> 747656 bytes resources/win32/inno-big-250.bmp | Bin 0 -> 1307136 bytes resources/win32/inno-small-100.bmp | Bin 0 -> 9296 bytes resources/win32/inno-small-125.bmp | Bin 0 -> 13112 bytes resources/win32/inno-small-150.bmp | Bin 0 -> 20216 bytes resources/win32/inno-small-175.bmp | Bin 0 -> 26828 bytes resources/win32/inno-small-200.bmp | Bin 0 -> 35248 bytes resources/win32/inno-small-225.bmp | Bin 0 -> 44336 bytes resources/win32/inno-small-250.bmp | Bin 0 -> 58296 bytes resources/win32/inno-small.bmp | Bin 12814 -> 0 bytes 16 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 resources/win32/inno-big-100.bmp rename resources/win32/{inno-big.bmp => inno-big-125.bmp} (67%) mode change 100644 => 100755 create mode 100755 resources/win32/inno-big-150.bmp create mode 100755 resources/win32/inno-big-175.bmp create mode 100755 resources/win32/inno-big-200.bmp create mode 100755 resources/win32/inno-big-225.bmp create mode 100644 resources/win32/inno-big-250.bmp create mode 100755 resources/win32/inno-small-100.bmp create mode 100755 resources/win32/inno-small-125.bmp create mode 100755 resources/win32/inno-small-150.bmp create mode 100755 resources/win32/inno-small-175.bmp create mode 100755 resources/win32/inno-small-200.bmp create mode 100755 resources/win32/inno-small-225.bmp create mode 100755 resources/win32/inno-small-250.bmp delete mode 100644 resources/win32/inno-small.bmp diff --git a/build/win32/code.iss b/build/win32/code.iss index 831b31a3c711c..ee70efb974d02 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -19,8 +19,8 @@ Compression=lzma SolidCompression=yes AppMutex={code:GetAppMutex} SetupMutex={#AppMutex}setup -WizardImageFile={#RepoDir}\resources\win32\inno-big.bmp -WizardSmallImageFile={#RepoDir}\resources\win32\inno-small.bmp +WizardImageFile="{#RepoDir}\resources\win32\inno-big-100.bmp,{#RepoDir}\resources\win32\inno-big-125.bmp,{#RepoDir}\resources\win32\inno-big-150.bmp,{#RepoDir}\resources\win32\inno-big-175.bmp,{#RepoDir}\resources\win32\inno-big-200.bmp,{#RepoDir}\resources\win32\inno-big-225.bmp,{#RepoDir}\resources\win32\inno-big-250.bmp" +WizardSmallImageFile="{#RepoDir}\resources\win32\inno-small-100.bmp,{#RepoDir}\resources\win32\inno-small-125.bmp,{#RepoDir}\resources\win32\inno-small-150.bmp,{#RepoDir}\resources\win32\inno-small-175.bmp,{#RepoDir}\resources\win32\inno-small-200.bmp,{#RepoDir}\resources\win32\inno-small-225.bmp,{#RepoDir}\resources\win32\inno-small-250.bmp" SetupIconFile={#RepoDir}\resources\win32\code.ico UninstallDisplayIcon={app}\{#ExeBasename}.exe ChangesEnvironment=true diff --git a/resources/win32/inno-big-100.bmp b/resources/win32/inno-big-100.bmp new file mode 100644 index 0000000000000000000000000000000000000000..99cf4ba66683ae1ade11909259216a5e59177c36 GIT binary patch literal 154544 zcmeI*v8p817RTWpAHYz21fRgbK+ssh+&7uG5F;@)G%zwSF%T1l(L~IQ1qICv40JmU zO+nXLQ>XU7Rypv=nM+f-qWnuRn_q z-t!qw+@&~s+fv){wQg(r&_-JK2t4H9dA;YPV;9eLXV%rdwC)jD#c#gYb5hTBbJo@E zwC)jD#c#gYb5hTBbJo@EwC)jD#c#gYb5hTBbJo@EwC)jD#c#gYbJEG@`qv*n|LvzQ zn}5cpL7e8StJ?*SQ;(YY-%{#(SI;$naco+j=uxZlpJ#r;qdxG=o9kZns@ISgP+`Ej52p>pDuYTK6h$)O!AVd!0}t2UoRDCEGnGShF6b_3lQg8|y&9 z)tT16|NPY-zkJ*LGd2w#Kh1<9GUhzWeCyS~6R0-?aJ@23OnunQNcJ+1As@9QSGed$k>rq-qL}E=2sRIR9+geY8?7MxOuD04YHhWk734^OzXT$B77OYv1(t1ao z>@9Vm;HuWya66_2Yu2N*-ccueOC2b@x}e8I_QelCsw|0eNfr^v07^#|H_?0>hIypr`vOV%9n7t=`zQmueDyX zpYr<9zRTfSpVD`^=`zQmuiIMp|ANY~qu)`v{PMK5UT*sTdA)qT&U)RZq4imbZ~JQ9 zBX9#h@kP%`$1a}h6Dwc0*1AXF^w8r2^qkalt@TZy?JIZIMz@om_@eFX{`>mG%Ga&6 z?h!aW^!NZhCmp-YJlFm|kt1J~m!ft3J&8En4;h;Vaqj2$UkM(k9#!|6x%VJ6t?MYo zs@4a6C%2C+>SUL+-X7(kcV>eY)3iRQ;P$aao$Qj<+oK%x&TPx5MLForZRgecpn}`SYTYBSy~uIz>^bS!#dCdJ z#Ep}+?h)9S5!jf++XxUKK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF m5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAh1#3a`_i*_`;w7 literal 0 HcmV?d00001 diff --git a/resources/win32/inno-big.bmp b/resources/win32/inno-big-125.bmp old mode 100644 new mode 100755 similarity index 67% rename from resources/win32/inno-big.bmp rename to resources/win32/inno-big-125.bmp index d9aa14eb708990ac1ddb11e04681ec40d1ba3533..d781943ada50c442e7e3b731e036b3e5e2e1c9dc GIT binary patch literal 222392 zcmeI*O^cOf0LJl=_AR3i5EPi37VT?Y(7HvdK7_Qg2nDyUq*e3{TxeimV22h#K_bW~ zwh&zSf`Lej7Io*qFwAu^2j)4R=ShEYMvm9Ko%1_??$Mc3efaVBpTD;A-#_n~>#y%Te|bKPU;gnA0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009Cs z2|T)W_2JFS+rK=%eQoVMe)eE@`!{T9pK&}uYoTU*gX>?ub?u8cwtxBg{CjKf^&dZ+ z-~J6-+GiXO&|0V&-#|Z03+B-6(a#}}z27>2Doc*~Nt3bYr?TXzpES`=HL0V1DvR@{ zvgD|rG#QJ2Doc*~NfZ54lRE0BvN(S#OOEi}Rbvf9QBhX zW6@7#$x%OPqMvF~NBvY5=TBwHQ9o%i7X4I~9QBhX`l%*$)K6t`{#2G6^^+!J*;GGw zuAI5Iy10bP?xoZ9o=zO~Qvo;CPyOI8)2N^NXzHi3Zh_ee=19k z`bm?q%-GMb7e5-$RbI&DmkS?MpZ!rk`xFPS%-GM3?>;;Dy!Uyp|Mtx%2U+sEe$r$t z`q}GNKQ)iFqki(7N;#y6vmP#UlD_xI{EV?XuwEOyjSeQ5MkS#s1*nv6w1l_f|0q=|m2Ngef5 zS)4zWB}e_F$yoGLS#s1*n&_vR)KNc`#racNa@0?nj72|{B}e_FiGHd{9raULoIjN% zNByM9SZ3^Jb?)8q6xM}Ye){_D>a#!UXP@HWl^Of-{O7$N{cq&gk@vxeb!*r42*OOEi}RML(4#NByLUeyT|w^;21#Kb0j%{iMlQ^ix@K)K8k|r<&AJKb6J# zQ(1DzP9%1?#~|VZvTcY?K6%CXf2dC zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk h1PBlyK!5-N0t5&UAV7cs0RjXF5FkL{1qtlz{0qv$XLtYr literal 206038 zcmeI*-ESOM83*7%1vk(J5ls0~`6?~6_xu6ea|IGYZ~@|q548zRpiV;5Hk7}Cn+l2| zq!c9t87d_tc3cNkAyg9MICfH`LMZ)C+c+hpEga@-x{?*IW53?nnX{{Br6W6=iRbt^ zzkPS+oY~12zBqsD4V&`g^Huw7)%I2GkN->Q9h*K;eRuV-0fh2^tWxV=>9vt)mWGQSp3b7 zbY$zh(y<@Buhn*B+q=`5Uwv;)u7!hJ(~)iM_qT1Y=>GE<=dmvRadu)n9o_!kG_~`C zt+t~(ZcGaYwyw#wcxZb%x}*L6w(S+&e;(sJ)}=q>vEA2UG*=tlULIrPtUkuPqPdjE zn6Es>yyS7fUXC9Jdi<_F#`0TleH=?uq7&r zd)>UP^;I6*$>Zes2hy`o?oXGVn=hc9Npt(YQO)aaZatIo*j^rA`^TS)nPyu&`2A|W z^hSAH=ZK8j{9~?rN$Hi~(4e!X&T!+g(5lk3gd*0ny9&##~R%cIrYVGA+o_LNYj7cJ*Le-y`gNc>rp`6P`L)xwTV3zhZT}vn?q7Xu zvp&Z5U22Bv?3X;oGNC@kyrNl^$C$4?#=PWlz+SEoALuc>`WVY^v-NSTniIS!9osP^ zt?KL4&i>BT<+0Q}j-~1QK9**G@%1$K%Wn-un|BQdKQeJzC9yfNkm&cOx___4PrI*@_SZLo@E zKFVY01<~Nfqqu(EeUIw(RUR7**9LYaj~hU1_@8*cjvf2;szcG|ykD;#a(R@;)zi`Z z-^k;r=dqgaxpJxbU~O=ZZ(e?_I={Z%d{2Yt*iF4(=XH+h`)=vni#OlfbQs$Fqu=e! z;rFvfWBxH#W1Guk?pL*5Sgn`m`|J)w%YE%9i}_ey9!t(+tZ&hCvl6$L$Jm%RTOVV6 zD=}ZuKFMQj%;hoWC65F4a{c3mua#I&Hr2;ie$~gA*LpIzd>eU;`8J=&e0}RMwV3Df zEava!G3M8N9%KExc6^8J&+-@>@22vYuW6mx`{PmV{>h)`pgKf z&vdUh%WH7M@_6I*3$Dj)FpHhzn}*J>yLohdc$u$8*FTmyF3Wr6`kvUaUmeHx%^g4H z&vW^_!t%VA^u796(rEPVi~6WO_D)7S&&uQI=P`e#c~~C#+{@>$o6TgC$B#2VzCA5H zGn+2Ia(*b<#ebemGY{Ts{;|P1pQiHIIrrLib9(&#Po~Eo_|$N;s;^T!`@8S6^_f_G zZ8VShbyls@gm8CjfW6JO5v>SH-$v06{mNA+>FY_##bJj!Dmxm#_Y@+gn1WuuMf<#E*W zct`s0mDk&d)`s@^*5!-t*WB>+SWTT@=Q-u+Js(f!e!DyU?eIfG(aufm>fB@6e0Xzp zvbj9w-)XG6Pjhv@=Izz}nunpeUvuBrBS+^Uo6Tc$ud}GbptiSO7#nkWjCn zd5n3<oChvWJn*3qQ zt?EM^pPi0ouh;hKV=TXQ)yMq#{_`h(nl79>UPL?fv#)eM8__ExO+F`&?d37w7rd0m z;;--N%)K|6yeEk@634V~V5>X^Mq|{j$Ib88^0=}VsE;d6dcBWypR4ou zQ65*?%i~IuZuWY8mB*M@FT?9SCyy~-d5n2oFMGZIP99^vwdV2fe+nEy4|daqgWH_X z1c*oReCDOa->3Y4T9we|_kX*}<4uiUzjtkoR3F=^k9izs_THBM@RK`>Xw!Rc>HFS0 z*Jf)Yf0oDg^4Pftb7##kuN$`8rpELCX&kc{#P;$S8-u!z9|tj(qwq83Q65Xmu{_G7 zJO)mz-`;f8&aX?#KuLX3AJs>BR3A&qKuLX3AJs>BR3A&qKuLX3AJs>BR3A&qKuLX3 zAJs>BR3A&qKuLX3AJs>BR3A&qKuLWWmioAG=$>@=u6MS6U(UmK-;n0_e`n1X)R_Z! zrNei(-`_mG!_@K^=dmvR@#5*nJ009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ QfB*pk1PBly(4WBn0bS-;6aWAK diff --git a/resources/win32/inno-big-150.bmp b/resources/win32/inno-big-150.bmp new file mode 100755 index 0000000000000000000000000000000000000000..554461982f01c8b8d2c8943fc683587aa46a39e5 GIT binary patch literal 339716 zcmeI5zw2bx8OCRcwiYQCiJfeSRYV~vOc0c`30U|Kq|ZOF!D6)u-_kCfja|w#vVuRB zu!WGufP$!OLL}f4NES>Nb7M8EckX%5^_=&d=RO~qm9I1J-1|Py^SSSwnK^#<#a}-1 z^rNT$|MS!3&r{2vua-X_TsxhvogQC)I-UOf<3~?-9y@*W=bJm0e|^nAIB;kOVEoYW zB5rX2#=+p|4#4=)6Gk75!@$uUfbpXzj6N8LfulR{{Wp%T5Pj|rFvr~qIKF-uKYqsU z3Sr!xfaB}`$+hEaM4`6>%yIAh9n{0PL(1M7Vca`^2lX)Skg~T%828TKK|PE+r0lH` z#=Y}*P!Hn{DSK;#aqs*c)U)r-a6i!?;Dv)*@ltI(`fBFm4gEwMZDZj^9E&j9bKPEfU79-a6i!?;Dv)*@ltI(`fBzhd8^Mb6G5nd8psduWGokDQ%V!nkw#9@=5t zBWGuoFz%ebhjtkE$k|yXj60|Ap&iCOa&}e;p=;hRWj-=Rs+#xj}X#^IYthjEjjjb*~Parh?EUt-^(Nz%SDnd83M zyC{com!y4l!nkktF3MrtC23!sFz%bZi*guuN!nK@jQeKqq8!FulJ?aJl#9eRRXPPu8wVVca!&AKftSleMc-7mZ7WbLXH#$A*5(apX?pR8S#GRIw$_t6dGK3Tgeg>l#9eRMz1zC)j|O_egoO@lX* z4dX^(n@WXo)8LI{!?;n{rczlc^om9iPQ`(+d zVcau!C)F_Sl(wf<828NGNi~c+rR}K|#yxX)Qq8_Yr?fq_GRHl0cTx@GPHB5;g>lc^ zomBJt4xQ5W)XE(9%-u;fj60?6sTIaOb9Yh=<4$RNYK3vn+?`azxKrAmT4CHXcPG^_ z?v%EtRv7op-AOh34xQ5W)XE(9%-u;fj60?6sTIaOb9YkxG{5iADQ-)x%yG-utwh7P zRos?hVcaryE72*8-@SU~&aE4B-adWnwRqt7-hF*e6{1}&V8qSeU75o8lE6DRug`h= z$KS97Wm?P>udZvO5{7>Dt3;|Io5yl0LNd<}qc7*BBk#&@rR&YsA6d>}_N zpE_rbr|dg~@tHvz*jov7iODQm*jD+$I^yk|YWN?*>MFb?A>4&>mxdKMT@NtijlN?*>MFb?A> z4&>mxdKMT@Nx0t}fA5ti?!0$dc~3S*}`VO*T?s;g)BcMol!xb)l!=hef)IE=Ri zIB+x=516!CvRtmKm+=gDR!!Tg{|~6@$g95P-+4&J*>~9OJ0KV@&M?+0tX0wn`61^x z-{mvM&#ojGKQp81N7mz2b`2~(B;!A0-(j!cJ;8dM^?1s1yJ&M2iF~e7^s}mf_navg z0gRt7jSjh1;XNnsInN-6@iQ~3e)N3L`TngZ?_NEtbz6F_T5NpBV%93+YnAVqzr)jE z9L7@|7-^joclYVaj@<@^Ms7^JtvIsqe|KbYL%P!=5)G z<3D2G;ne#Mhq1c%#1k^kdYtw6UYu8-2ji8m)Vo*N@h48c9{=bMztOt=<;`Em&F8!P zlnY{V#^bywR>C-pr#LY1;(+m#gqh z``|F1k}z|88^f-T!Z?hlIM9{i?Stnq&c4IBzXRH4ME%hm#`&HT#>c%{z<7%Htj7nw z7{EA;r#NtU#(&Pf!>RWj4g@ga)XedeDaKkQf#pl5fN>a4ao`ew3HQTzO2U)R@xT4) z_gc5lZvQTRalkl?^Lw%?cSfz2^ZEGPpO3GGHuIhQe?TdtIsRI?R8GEDnHlHR-e4TY zD;-$nXy!X%yfWdciM+3vIo|e)fpHkGbf7JO2}hfdarPbZp0g?W6Ee={DliV?4IEfL z>fg6mPIwmU@dg2(9fCPtxf+G>GkP?BoRIM!uT-%%^6KGsjaL7a z`8#Omd-0uoj`w0uS|G*1UGqDsn2igLda5NaNOt{IU-aZI(ye;?>GS0q3 zK09m*`Gkz~`8bTjcmoI6ud=;f+IaNA7awZf?tgrHL(W4Fv0o)+@jm%_{2?d~_MbV7 ze~;gHIQ8#4JQ&mu$;n}y_4wTD@rR%|*nh$}Yn2oS23|KXo{})@aTss#vC7xWo6mK6qhjI2D@*U8)D=CL@zUPE-7;o?a`Et2#zh(`5l=b*P^O@r*^Q9l-@85dz z?$xtex25Myo#?`JR(G&Kys9OIftJio~kF*RATvRWqYsF>`#cx6JXq&SgIj#`ii7<9nUUejbeP zbsWa`I+y)C7~ktSjPG?W`*|?F*Kru%>s_R#u<9r9yjq&$hdE(Bi zH?(d`&&5w$8x|}t@MjlTuODH&8{-XGIO?HbJmpKG<<&VpKdQb1Ix3hAb>%SrfWK!M z#;-TTw(3whjI&l@j;Fj{uCfo~Dc-YIS*0)MP8f&r6bEu}UOfwprzFfAU!^bSP8f&r z6bEu}UOfwprzFfAU!^bSP8f&r6bEu}UOfwprzFfAU!^bSP8f&r6bEu}UOh_=~E_*{9=O?wAm zyuGI!aV`g7e6Bp`ro96&-riG=IF|!3K35)e)7}9XZ|^BboXY_ipDPc#Y41SFzQguy zb3_~nJHQ+dTScL$128Tc9Bqdkfbp_zdovH&cu9_TuM;(Cis8iIbIsoIU$>UnBzgCD3f&n#$}_U@t^}R9yE$FSqJ{bzC+pUXguZsb3En~Rk{wqxNdfI z9&-T3V?I%(>i~@FW=H2S2Vgwr6IHqnz_@O9bRKg6#$!HFrRxBU>t;viF$Z8g<`Y%A z4zTY~H#<6yIlvr``9zhj12C?e9i7JV2M3JxqFfJS(t%n?d@sLdv z2|K{PL*ejfJ>&p$JY*9^!VbW=aCo#Hasb9dHc=$(0Q(Mw!=v?(1I+P|O%w?`0OP{p z(R#=M7!TP*k+1_WE*u`Mha7V2M3JxqFfJS(t%n?d@sLdv z2|K{PL*ejfJ>&p$JY*9^!VbW=aCo#Hasb9dHc=$(z?b~KL*evjJ>md!JmL~H$_~J| za(eV0aRA06E>WZG0E{cANAD2_U_9ayHOdaaxN>^*9&rH1BQ8;+>;Q}_r$_G*2Vgwn z5;e*Wu$nh z0h1_^c7T0{((%!JzyaoXz$8kf9e{D^_-H=h0E`DrqD0z(d+a-u&X4BH4lu{d9#Ns~ z0E}zrNB3n1V7%-R71|EKxORSYUv>b-%N|jo?Es8x=STNt2VlJH5f$1Fz_@mPbYFG= z#>*a2q3r}|DUvz*uUbKh; zaR*>rJV4qnIsoHEizpCx0LH}wr2V1;FkZBX0&xdmTs%P9FFF9@MT;m9cL2u41El?; z12A5+hyrm3*mo!%Ang|&V2&3pqCngM7#9za_KOa{c+nyX#2t9RzC-Z@X}{zEbG+mb zkE=TXJGrTdV=&{asb9l4)M6U12C?hApMsdfbo(;Jg)8ljH@R| y|0M@tyyOs%t2@BHL-hpdzvKXOyyOs%t2+SW>Iu?+$pIKIImF}Y4t#Jro&E=305^I7 literal 0 HcmV?d00001 diff --git a/resources/win32/inno-big-175.bmp b/resources/win32/inno-big-175.bmp new file mode 100755 index 0000000000000000000000000000000000000000..be0e7df91cf7cab1b3caff6ddb4180b85cbcb89f GIT binary patch literal 455976 zcmeI)F^?oi6~OT|0U;6z0wN?FAVFXe5IBPvBw_--0bc+)00&4oa6*Jg1`&`DlYlf9 zCu|`ENFW3mi6sIofe^@n1G5Apy=LOsbGV32-J9ugwz@;7r1#3MrHT zXF~DXJRt$jBuuK1LJ4pt6tB$_65ve2qzWmN0B1t++B_iv&Lm8#kU|M?CKRvD6B6J| z!lVi*lmKT!@!C8g0nQ{$s*pkna3&P5%@Y#fOv0oJDU<+bLh;%>Apy=LOsbGV32-J9 zugwz@;7r1#3MrHTXF~DXJRt$jBuuK1LJ4pt6tB$_65ve2qzWmN0B1t++B_iv&Lm8# zkU|M?CKRvD6B6J|!lVi*lmKT!@!C8g0nQ{$s*pkna3&P5%@Y#fOv0oJDU<+bLh;%> zApy=LOsbGV32-J9ugwz@;7r1#3MrHTXF~DXJRt$jBuuK1LJ4pt6tB$_65ve2qzWmN z0B1t++B_iv&Lm8#kU|M?CKRvD6B6J|!lVi*l)ww2yW1Rrd<68Fd^r_SECJ5M^0j+D z0-VX0QxU}y;7lxEyXPaonS41FQ7i$@#PYR!J_4M{ms1hN5;(+eZ+8UJ5zuGSrBpz{ z1UM56*!JlNa3);825kFu z1UQo}r2+~jz?opcwogZZGwD()pkM-=2?lKYbObn)E~NqrCU6Me-}VS(BcRV@%cyvw z32-Kwu>G?U;7qoRiYJ-?XQBz)KN|tgWXq^{q6xUKIhwHjvk}l|vSm~}(F8aXP1yd~ z2yiA_M#U3NfHTpA?VpVRXR>8fJkbO=6HVCu*$8kZTSmnbP2doH0{bJ7jDS9qETO^) zC%~C-#7>Zm0B4dVR5;-TI1`T836c@uOtORuC!B!$n!^!0K{5jROtORuC!7Fh!Vx<` zG6I}QmQdk@6W~lZVkbyOfHTPwDx7cvoC!zl1jz_+CRsv-6Hed|eg-E%AQu6BCRaX1 z6HkCM@r<1z7Xi-X%BN`J32-K!u`}c%z?oe66iqw<_cg~ec7|L8^qE}w6iqw<&criz zhFk6oGgq(g zhz&WznSlsIl=#*6?CebM`ydKUBR5AL@VvUY=kTaYah`?xiEM5<12C5i+X0b*`JIEQ% z3`AfwJr=L0vor2%t`(?0vsj~}9qjCkpU-fnMqo5O7O#gh1Eo-(S*+2~4svE+&iv~2 zm;dtf*AhJ5dhPSi%O%e2%b7p?=<65!y6b>n^1B~=l`|oExqnG`USuygZ-@70oVg$` zlP~GqXWZ9ZYipd@{=%uxY-iQBQlFW;M(Q&YAQucf_Zgox>oaYydgoT_Gv~-W_P9PX zc^%bfCO|G2>NB+${tJLU=1Y7(v;8$vpV`i;ZKXalaaf<30J&hO&*(F4FGGW@)@Py; zq0dZSNA;NrkPC*L`;7aVYi-T-+6ZuF z?u`4IYc-_LaAxj|pU-fnMu0P%X{!%?hBKVuOdA2taHg$dZmZ9{`Q4ZP^pkHSc>MA^ zpKWs0ea&rU-2}IF@ohP?+kaZ1vor2%t|`iy`8$&iM_T`BfzHnO+>A3FEND9gX9`~T zFA1%0ke=gueWplWuK{NY(wvEV3Gn?)QEFiW@6Eh73oEWmH`Zqga{7!uQxwF~fHMUZ z&TytEh@}B%3M!o8Oi>U^gTb9~U$gILmdf_I9vj>lp8;^@x%#nma?TV~yf^dStSE@3 z0cQ#-oQZShjaNSTfHH;DPsMdET2f)mf&+oT(LY=S<($y)&KR zjB~oq&bY6+rYL8|5uIt>G+wx4D3I-$v)X zb=V{NOp&}^1I`qrITQB+;k{W=YGDKK&Ac}YE3Qj7)@KTG`iwqP6vWbiGX)jSaHc4T zr2%IODxBd=Q4mXm!JTnmv+rk?%J#V)8{8S60dVHI`muC!&J+Fm^ zQ>&QUx<~US>O0~hIa4d*2;!AJIa7f%&J<@jGZ2As#Hd^eX9lVmeWp^W<4kdeGXoJA zM~upqaAu&2(Pt`^I?fblI5QA|am1)xNoQx=*IX-5eWp^W<4kpS#?NOsQzI~r7?msG z%s?sBXDXFC&J<@jGZ2As#Hd^eX9lVmeWp^W<4kdeGXoJAM~upqbauvl&9wr(tv>U{ zE1!Jw>Wc{;Z@lt}iXro<&d&JUERQq(9nFPbb_qDd8O{Wh)#JAGyUhi^`!+i7t-~I< zt@md7Oi^lK1ARuH2`jEkH`Zqga{5eMQ`KjRQVSdCGx|(eab3ExK2wm>XY`q(AeIJ$ zJ9AjN=D=f|;mm4z4epHhX5O2v)^P6yoGFOwGx|(X5K9Bj6jV6FnW7+;2AnCVaE3EQ zK`ae8Q&8bdoHK8J_oYAmW;n<(8ERZ(QY1NSxO`3#_`&N40T?2OONI8!5V z6KZj0pcLNLJ(@RB-w_wd8O{tuAfm*tz9(k}su+Fd>h&G5A!j%<5P^sizxtk>8K`3P znXA`##D<*V%s>PpO8n}3c6P>n&9ws6XRcn~5gT@P#?NOsQzH;j;#c33GXteipSgN{ zM{LL$&J08#qQtMhCuat#7=7mI^&PPxXE-wufrt{n`ktMgabI(-K=qlc*LTE*ot^Qy z8E0w)B1-(~dva!=6n5w{zyIOa-h1~S2_C=w{+F*V!^RCcvoB|M`;T94^kK^~&TuC9 zcXMO5Z~Z0yEi!Ce!x_%R6m{^foQVQ_|B|rr#bbnJ9loFO-t6&-)@SsYs28lzefQI6 z+}B)FRG-{qY9{Fk4(&Rq;=I5QA|b7eZz zan1}>G5X9%m`%m7Jn2{%Q6GdGnk7drp}&J2(Q zmvB=Adf(T4Q*c}?0t*E6nFS~gAy6T}nF<_J2rLlb%mNgL5U3E~Oa+c91QrNzW&w&r z2vi7grUJ(l0t*B1GjRlg8UfDK=$J!bPJlCWCXOIbBXFoqnnM7A83BD}hQt8`ssuPw#bXkI83E4B zkT`%ql>ld|cuXQNBfyy%5(f~d65vb~k4Xe(1UNH8;s63w0-UMhF^Ry80B2@M96+E- zfHPG*CJ~qs;LHq(0|-@5ttI-%#?^R0(AnMsq-<5z?1-IrbLVps1x8!osU@rrUW=M zC1Q*~od9R*e9R&+CBT^}5n}}E1UOUYV-|ra0nSW`7$Z<8aHvn4MF4>b0exly#1MfM z0-RX^WEz190nSW-7$UGjfHNzAOd~KMz?lgULj+a`aApONX#^$&I5Poah`nH4~$5ttC*%mj!b0xJYKvjWI80uut9nE)|FV1>Y8W!^Lb2$Tf$ znUaSQ0&4^~vqs1~0wn>?lst?OSR=rhHA3bQC<$<;M z3LFLqtP~W}T502%Hk&%qb1e5m+a{nRP~1 zAaF{6Gp95>M_`@6VSR}e2q17mK%Y5*;VA+e2ykWtBufxDA;6gv7@i`qfdFSVK(YjZ z69Sw$f#E3v8whY_10+ijI3d896BwQ%uz>()HbAljffE9pIf3CR0viZ$W&EvDFPb^95$@61OWsd2# zz``>Gwh-XV7E0D2@IZhw4=g-GU<(1xY@uWg0uKZ@^T5J01hx?1%oa-4An-tdGY>30 zLtqO5&TOG%4FV4YIP<{5GX%B};LH|E)*$defHMy)JVRg$fy0(X)*yhuGXna|GZda6 zu!#U?Hes>|foB9b^9+S22y7z2nN651Lf{zz&OAfm2?Co4ys&ANMF=49lz=|-l!U(# z*hYXe+c;T;z*7R8c}l|H2y7$3nQfe`Lf|O@&O9aIZv?gx;LJ8oRw3||0B4?(@HYb6 z2pqO8vkCzO{!c)kQ3?pO5#UT4FRKyYj2b|ojR0rbcv+1AXVd@!Z3H;e#>;91IHLv- zXd}RxHeOaEz!^1wKpO$hwDGbU0nVra1lkC2rj3`?2yjLXAkaqO(6-!a1a?nApV@s0 z0MJN)GmXG3-#r1&?7jp5Xe7XyMqrljo&aZdUjhI$65vcDFw1vOfHS)<0RS2aaHbKM z<+~@qncbHF0F4AV(+JG+-4o!+GpfkWeZ%MsW+0exog z6(Igr0-R|jX8qm?aAxlnApTYYoM|Oy{oV<1X73ds{#F83+K3X$Ri*Z-4)G@DBk31PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB=DO68PrhcV0ewveN6j&)z@z0zTXyR-(Ahb1Ap< z$v40w4078;7lHUF*6L$fUb;t!rIR8gJvUb**b%Hv*Y-m#%fK>q+Bn9Ja1?t?Nc0 zlkU>Bu5~?Wyp6-wwXSvD2xQV-y4JO>Cylpp*t*uWt{Z_&x=Yu(*7csr^3+x3h0-n^>U7w^8&=qqcNu66y0U6-P(cUyk*)$^xW@isnJ z*SfCK8~Ocax^A>~$z8hEwXQ3HO`D$Ao478yxO}-?^1Ji`pOy9h3*dQufs1?0M|fVB zFA!bFZ&Cj}JZf}j&+9Hx)&gBed0j`1?yT!BQPu)oM|oXGjqa@LE>YG3T}OFcM~&{R z>n>5&0$oRWT}O@Xtm`gO)&gBed0j`1?yT!BQPu)oM|oXGjqa@LE>YG3T}OFcM~&{R z>n>5&0$oRWT}O@Xtm`gO)&gBed0j`1?yT!BQPu)oM|oXGjqa@LE>YG3T}Szmu8;Yj zE?aW}fpSa7YkHltuH$vr^#!^+_ebcu+|s(%^|?d4@cZjp*D>lcdtP6-!E=0^u63>J zbCi|)p1Rhxu5%ka$EWLB*SbDOS-J12YhCL)x50CKx~_Gt>vNQq`<}YiwXSm;JjbW& zTGzThM_IY=scT*9I=8`de7df6t?P4?mHVE$*0rv48$8FS>sr^kK1W%(@2P8D>pHi= zb9}n4b*<}jl$HCQy4JO>a~nLzr|VkRx;{r)x$miKUF$lx!E=1Nu5~?2*Y{t2Qhw*d z^}N3S{DYH|*R`%^>H6XRP#x;poB#dx@46nj#O2HFWWas?#lzk0$soJ^JxIDf{$}&M zF89B#je?>!^`9>v|Ah*AYseUSEFr z*6}~+QLQYubiAh5IqN!J_uY0~-MTg#0=h1@w61mCXk^A+y4H1!y3C%}GlpCpv#xcm z>ne%`Hg&CQT`v%Fbqc!HwXUlu7TDCau64aY$ki$6TGzU+qF7*4*SgmA0wGtYple<0 zx{6|fOGe%wXSu&K*-f8=vvph zuA*3AQ`frI^#UPRr=V+H>$-|!flXcOTGtDNT%Cfhbv;Yh|Nh&bUOj($J+E&+{bN<@ z@teBV^(p^^7M?hUijl@~kgZR3RfVz$viLpBAJI%*`&x*o*W zbp+IP)JU9lJ&3RC2&n6*kvQvm5MS33P}fl-an|)9zOEynuA@fctm{F1T}MD&M~%c; z*Ms=Fj)1z38i}*62k~_s0d*ZU5@%fx;_EsB?zU?`r|IWD;*Hv!bsdRz+x78xwUyN0y?$3P~Xb**b%Pa1FIuyw6#T{i-m zbeFDmt?NnSZ5+0)b*<}0Ad~LWwXStNX}pcY*0rv6-3VmTUAoq_t|yJRaoD=nwXPe1 zOu9?gy4Lli@iq=y*SgkqBalgV=~~yio;2RZVe4Agx^4tA=`LOCTGx}t+c<1p>sr^1 zKqlR#YhCMl(s&z(t!rJ+()HILz5Vjplk0hX`QclQa@)Ift?OC3ez-qWhr0Hr-$y!l zean|y``d52y@$KolTCK}`%-j${C((oUG9Hf$1ee0M~%eU^Lh|p*AYmvm1k`oZNSt*&h_CAisOzYaIO}>4U)K>(*HI&J*7YF1t|OqXqekMa>p^^7M?hUi zjl@~kgZR3RfV=Jb{;N;Qv0cyW`_Df({s%p(mF1R>*YrAPUB~Ob+peoy*JeXN*X5Sh zwXPeD%(zR}x{gto+4FkFkgH?XwXSttMX|u9u63>J1wyV)LD#z0brr<|o4VGut``Wo zIt5+pTGv$+3vB9I*ScOHjgrt zPC?hY)^!!d0-L(lwXPQkxjF@1>sr@U6bo$XTGzT>Amr*4bggS$S5YjmscT*9dV!Fu zQ_!`pbzMcVz^1Ns{fJ$^c<<_dUEZtbPj~n|ple<0x)Rv5sq0N#mt0)F+;;etJ90>e zyW5iiM}EJV=k>@{d&Ix%d0p;*UB@p0T}O@X?0MZK%37f7D6i|N(VcbOCCXZ$>nN}5 zsL`Eu-6hIepzA2F>!{J4b=@V(TA=GFuj{DMops$M%37f7D6i|N(VcbOCCXZ$>nN}5 zsL`Eu-6hIepzA2F>!{J4b=@V(TA=GFuj{DMops$M%37f7D6i|N(VcbOCCXZ$>nN}5 zsL`Eu-6hIepzA2F>!{J4b=@V(TA=GFKhkyi$%XQxA6NDI;@vkM@#i%j>#fQy9k1zi z&bp4*eX#4tx--4ch=8ukEv;)^k3_V?zpHCq$EeHfdA-9J z{dcsr_Q?{w=C>RQ*j-dfH6*Xvr>y54`MTaQrJy4Lm9YWBZg*SgmA{yW`zgu2$XuD4dR z|Mj}owXXNy>DD9EwXSu&wVM5}*R`&7z5hzkb*=0Dce?cmbzSc2 zZ$5tK<+CR%y}tYG{i@dEHy`c~D^XnMxs+S__$zKD0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly OK!5-N0$qWdoBspz9(5A{ literal 0 HcmV?d00001 diff --git a/resources/win32/inno-big-225.bmp b/resources/win32/inno-big-225.bmp new file mode 100755 index 0000000000000000000000000000000000000000..89cf4efb1e0b855e145ec7708bad421cf1096f8a GIT binary patch literal 747656 zcmeI*zpJlFeZcW++Ca(>@E=GgL`Y+Gtt|Gku+1$Lo1knHmTgzGl4ZG1Xe}{?|`F`rkj^{`>pefB*EcCr{q{t0zxB_T=wA>VERtZT#Q==Ez`+kOK%HkVAkgawwUD00LYg2M|CYhX7aPP%;Mr1h_&DAb>y)0j|iQWDWudaD^N| z0D&9=T#-Y`90U;H3ORrP0yzY@B8QSW2q3@}asUAYatLrm4kdFCK!7Xc00Ic)5a5a& zO6DMd09VKX1Q5s}z!f=^%s~JFu8;!=Ado|VD{?5Ag8%|tAqNmZAcp`~0R#}pA;1+ml*~Z@0j`h(2q2I{fGct+nS%fV zTpmpiX2MjAbZp$RWTLIh4#n z00FL$0|+3HLx3xCD4Bx*0$d>n5I`V@09WKtG6w+!xIzvffItobuE?Qe4gv^pg&aTt zfgA!{kweKG1Q6f~Ie-8HIRv;Ohmtu6Aix!J009JY2yjIXC36r!fGgwx0tn;~;EEhd z<{*FoSI7Yb5Xd3G6*-j5K>z`+kOK%HkVAkgawwUD00LYg2M|CYhX7aPP%;Mr1h_&D zAb>y)0j|iQWDWudaD^N|0D&9=T#-Y`90U;H3ORrP0yzY@B8QSW2q3@}asUAYatLrm z4kdFCK!7Xc00Ic)5a5a&O6DMd09VKX1Q5s}z!f=^%s~JFu8;!=Ado|VD{?5Ag8%|t zAqNmZAcp`~0R#}pA;1+ml*~Z@ z0j`h(2q2I{fGct+nS%fVTpmpiX2Mj zAbZp$RWTLIh4#n00FL$0|+3HLx3xCD4Bx*0$d>n5I`V@09WKtG6w+!xIzvf zfItobuE?Qe4gv^pg&aTtfgA!{kweKG1Q6f~Ie-8HIRv;Ohmtu6Aix!J009JY2yjIX zC36r!fGgwx0tn;~;EEhd<{*FoSI7Yb5I_I{1Q0*~0R#|0009ILKmY**5Qs>?_ckL& z&6yFno&Z-|&)gFch)94dB1X-b5xAbfE3dqA{X;zw0R+Mk;EHe|b6^Cz5a5a~*gX`1 za0Iv_T*w?4fi48Nq6>BpMIamjt_T-02S%U^0j}tR-9r%wM}RBBh0K8w=t6)ix?uNE z1i}&Eif|!wUxaG6zPW3jwa^g55(A2uFY`!iCI%5$HmI zE4pC!Pz1se;EHe|b6^Cz5a5a~*gX`1a0Iv_T*w?4fi48Nq6>BpMIamjt_T-02S%U^ z0j}tR-9r%wM}RBBh0K8w=t6)ix?uNE1i}&Eif|!wUxa zG6zPW3jwa^g55(A2uFY`!iCI%5$HmIE4pC!Pz1se;EHe|b6^Cz5a5a~*gX`1a0Iv_ zT*w?4fi48Nq6>BpMIamjt_T-02S%U^0j}tR-9r%wM}RBBh0K8w=t6)ix?uNE1i}&E zif|!wUxaG6zPW3jwa^g55(A2uEN*SN!^?-~QG2-<|m5 z55N7OCQttK`wu6E<7zhow-TtykVjfOpeug(;vfF)t-qc4f7kuHI4lq)tArwy(ssJ>WB zxZ*VPkDXj`WU}w!idcnTa9Xa|Cw!vK6$v=EA+9K>zF14RB3%O8C|7JKP8(bitMCg> z%N3`Yf9&LnBa?j(R}@rVtR-BrPxwTeD-v*SLtGK7@C#1M73mV#M!8}`aoXUDg6fO4 zgey)n|JcbDM<)9ou839m1*heTeZnW&T#Wj66E7B#fjdI0?;WBxFTHw+bCCTC{7z(5v%YE zPRkXinSbo$iX)SK4_6daU#uluu}}C!n=2A%N6Mo*haZxLvh;Rih}Bk zwS+59GymAh6-Orf9E!>00kt;?^n1b^Ro?~OW!oN{iU;7W;!Pg)K)!lXpR}}E% ziicKu{d}${NYu7VxT1h3SJc=3LwDecf<$e*gewYoa>YX{y?#Dd6eMcfC0tR!lPl_L z|Dii@MM0vrUBVRwJh|ebm0mxeD+&^|?Gmmi;K>#Bwg1o^xS}9Y+b-dX0-jv)&`PhL z&lLrU+I9(76!7GV`r3c!4qQ=?sBM>UMFCH)cxa{9&*zGQL~Xl-D++jWMSbl*bO){| zNYu7VxT1h3S3I=R>*sfO#fLxs<{y9e%auQV@Xjad;O~d^>BJTF4Cmf~E4bqSGn^~tuJrobaRpa!MSUHuume|c1y`(4 z>GgNy3a;Ra`Z`)+2d>}>u2`Yc>+i@FT)`Ffb+p0`T)`Dwu|lQS-*HS=y!F;wnI(ML zv>T{;+9U7a&rC;-&Io54(-nSh^ZMN2itCxjdg7R_c>etPWiJew5At4-237y>j>RgR z1y4JsE4)`+pNifqu4f+WiCmHXbh(Xsuh>wWHoRBFD*S@ea>Z%pA3M3?$YkHc6$RB7 zYYA8E6F$-AiUge75Ld)1{DRYRMY;sGQLflfoHn?kp!#Ah;fmADKX!7(k;%S?D`FLX z!D+c-pYVw`S0v!vhPa}j`eH5NigXEVqg=6}IBjr6timriEmxdo{;`uQj!gDFTv1Sc zv6gVfKH(E>u1LVS4UO-LpMUG~zj^;VD}Vg#-B)VtG^Q(Fym*n$%c+UbABn=7Vt#j|J6noG5EL+=%7fZi+8d`1|Z(iPq- z%H9oXR(Y=&p`0J}3|#T3c(={HS8OOw8{R8Uu?*G871xA=E7F{~BF$%nQLeZq3S5!q z%oS-qBaCvzHBsP-G-s|z^BG~3E3SzGSEM;}MVikDqg-)K6u2VInJdzKMi}LaYofpv zY0g}c<}<=5S6mYXu1Is{iZq`QM!DjeC~!rZGgqYfj4;X-*F=FU(ww;>&1ZyBuDB)& zT#@F?6=^;rjB>>_QQ(R+=kZAE<&PhI?Nc?sF?1_|bPjYG^}Ay=F)dp= z{qK&IVXz!jM?^j?t$HU4|Wmh&2A1h^ty0^2B8Y$#3}Tv1klfmU(FY33h0x#GxV z-@_Gws=n-~T(M91M4KxTaBf3fQC5I~R&hnT1h!GG*if7{xFS&1mmQTWPBZ`5$rVQ? z`yQ?+E5JajxMH91i8fax;M|6|B2d+r9hEE6C9sWh#fIXv!4+i%7-$t&oM!&9lPiu) z_B~t?sOrm(I;Jb$e*5ix9*)rFigXmXqO1S|ts2u6e#ZOy6!J6P*E5gx#4%m*xzBwr z{aNh#QSTLL=H4q}6@I~K$8^QZmoL+qdHpC?q?vO?LG{I2GNvnh4RU=J`5NSU=CPj0 z73oix+o<=74aI50dqu3mFE}k%oM!&9lPiu)_B~utP<^qMaK%316K$?Yz_|@^MXbUv zI4xJCOJE!2iVekSgDVQEFV+&SIL-WHCs!Pq?0dK(R^b<%mMiuNpJ;PM0?uuSD+;PF z))KBrm%uj46&s4v#`v!I;Jp{8Cts&?%D?mfwbx!7ErMK;jtEzz`HV0+r7Q04?nYp{ zKLc0nX&l>Jabz-D&y=q4bDP(HB|H7kZTcF7E3UD(_lh)U?-gl2BaBYziZ6cgizBez zpMfj(G>&bqI5HWnXG&N28pPKi%?bD#ge$JGxA%%PXYUnhJ|m2B#WhjjiZo}gNb?zC zlq;@@0#~Ftb48lZ2%}taO%%8y&6z9Gd`1}Miff|46=}{~k>)ePC|6t)1+GYQ=880* z5k|S@?J5S8IIQ( z(-qz;j*)uo9`6;8O*s1gT#^2Cxs7_S*if7{yjMh5!M*3_iqp(Lc5=m$$-ajx?ya@x z6S!iZ@QF58B;eeJxFWg=?ma(Oq)T8M<%$i(X@e{7t+nVAxZ*VPkDXj`WU}w!is&l1 z_xxP3PxwTeD-v*SLtJrhtwo=}73mV#M!8}`aoXUD=qkAP{9JLG`NvMKI5OGyaK*i~ z7JUL&>=QoG=86QI+YnboSHZpK=ZbU*Y@=MUp*U@D#l5u_eF9gUX8y60D~?R|JzNo8 z1^1qxEA|PWXmdpZ&TWV*?ya@x6SyK>0^2B8Y$#3}ToGLb_nx0CPBZ`5$rVQ?`yQ^i zx7MOh;EH|1C)!+*fO8w-is&l1_xxOuE`e>7D>f9T4X(Ji)}l|~iqp(Lc5=m$$-ajx zqO0KE^K->M;S+7HNWi%bjqi&0zww!0eD_N$fBficpSo8Fi3wbhE`e>7D>f9TjqzRa z!Fw-GPrgp)lz->{>#x5)S_HWw9TBcb^BG}uN>}*1c@wXKk2Ey>-_7g2f-9~uw)cuO zXYUnhJ|m2B#WhjjiZo}gNb?zClq;@@0#~Ftb48lZ2%}taO%%8y&6z9Gd`1}Miff|4 z6=}{~k>)ePC|6t)1+GYQ=880*5k|S_aKt6v>~?fwj0v8QotbH$O#XgyQ9!taAT z(l31^w)OiUqm_2^^GxZAySuyQpei@?UXcbk{(HsGzxDawy#Jk*KYsS^D~V4Q_YU1k zAe{qUM!i?u8@T2Zrhl(!PHeRexgt}B-Ye3e#(%F^t+bopmn+gGu#IxXhT^or70qjN zv<xguQx+bCCTC{7z( zFp2!ua->Exx-ay5Q>_bn>6%Qr0+I+4!YT>kpD^9U&o&FqTdUbHc zmhI^VSETuH1y`gq<*^N+v#<;ovFc;^!ZC4AYEFCCa^0dKoj4LveXYe?3MP?jF zo;G-naYbhG3?4_W$c*F2(+1BmuE+Tc0H6`9F1cpSMRGmaxq8$8Fj zA~Sggk0V!P#&P6ngXb7mWG2txapa23IF3AR@Eqfc%;Xt7j$Dx$$C0NEo?~2*nLLBX zkt;IeIP$c?b8Jjky!qyvqs4UaIC8~k2~%*M!E{_na8T0!d^n=8^Ku#IxXhT^or71K+D zD^4^2*vS<~Ci@<);EH`dT-Nseit7{4_baYv9_xwTD>4P>y&?_Ddqu3mFE}k%q)T8M z<%$i(X@e^YsxQ_Ot~ky7V<%S}ne2PGB39uSoOVoC__@u3P*yJS{fbI}(=Q*>75*)u z>l2SFu4f+WiDSCLpMzYVMO<+`^H@(D(-p72`s)5`;St(5P_ZKW(9?3oLy4_6e@s_+ zued%Bx#D`}v7X2knJ+EgE7G9+IY_L+FE}k%q)T8M<%$i(X@e^YsxQ_Ot~ky7V<%S} zne2PGB39uSoR%y037=?lMFP%kh${-JFV+&SNSDAi$`u=m(*{?>D*S@ea>Z%pA3M3? z$YkHc6$RB7YYA8E6F$-AiUge75Ld)1{DRYt=?Z^0Z$T(4m-zXLN`TWZAJY{B{w&t7 zfBNlTegEBwKmPFB57KWA>Cr#^{=EJA=Q774U#B2YP)UF* zDgj!Kz#;^=Vv#_*CIXcN-gx7U${j360D%<C%)HAdL0xJ-B_UzdT z*K|h&5U3>Jy`mDJ*T5kR1ZfcJ_Tde$Pa4gs!MC)RF?Kn($|sG(;q z0_zaqigjY`rU=v!;EEc0)*`SD0j^jl)^3VG4FRsGp=T`u>k#0Ibz<$N2-FbZiW+*> zBCrmDySuw}F6*WUAW%%edqpui3lUg~09Py(ZkI)%m;hH4qq7i!r3i4vQsH)41d0i8 zMKL-H5m<@Fvk-x$2yn$x;dWUBiV1K45d&2noIKwuF9-YXUfv}+EJA=Q774U#B2YP)UF*Dgj!Kz#;^=Vv#_*CIXcNxS|rEC%)HAdL0xJ;UiWQ>ljtJBf;EH;Nc0gbS0$i~|l-&`5dIDTg z&(IDCtU!P(R*14YB2Z6&E9x2A0f7|=aK#Eyc1Hy232;R{Lpva_0s*dAAEHxS|P0n;qA$CConh@ZMCKzpk!1M&TV)_ufAOcMYa77c0HbG!|0$eeDh+PnYCIq;m2}YYB zFg*dTm_EcVh(HqpT+sxhO%Rx#09Q;OVi!c92?4HXg3%@jOizF-rVp_TBG7~YS2V$B z69lFwz!lSn*aZ=2LVzopV6+JW(-Yu|=|k*-2s9zU6-_YO1cB)ZaK-c?c0mN15a5a? z7;S>U^aQwK`VhMy0!;{TMH7rRL120UTrqu!T@ZmL1h}FJMw=ioJprzmKEy7FKobI7 z(FCJS5SX3-S4vF@1<#5P>EHxS|P0n;qA$CConh@ZMCKzpk!1M&TV)_ufAOcMYa77c0HbG!|0$eeDh+PnYCIq;m2}YYB zFg*dTm_EcVh(HqpT+sxhO%Rx#09Q;OVi!c92?4HXg3%@jOizF-rVp_TBG7~YS2V$B z69lFwz!lSn*aZ=2LVzopV6+JW(-Yu|=|k*-2s9zU6-_YO1cB)ZaK-c?c0mN15a5a? z7;S>U^aQwK`VhMy0!;{TMH7rRL120UTrqu!T@ZmL1h}FJMw=ioJprzmKEy7FKobI7 z(FCJS5SX3-S4vF@1<#5P>EHxS|P0n;C*^yfgoF?PLWnfpEB5`u(7hVNvhRu%LSVhu?V7tg+&@ci0mSP zh!BY)5>(vAZZ&DPu=uiLJ0D#3-8tu;bG~Qc{^1{)nRlK!@B7bL#|95ru&;LGqb^qsAKmGjlA3Xj)C%*^~AV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+Krw-j@4j%lfBTl70E4gIe?9zbfBWHG-!9ItEa#}?)>`n_dh=8C&1u8fBRGTP<->j9p5hC+u0NK&V=o(@~fwS&cy8} zor$y6fy=YinVyZ=R;@E}`&DP+Y<1@HY;{Iw?EC$`*O|SEIbpIo13IHKtFtrtI-PN* zPM+`Vchec2$)33m&(j&5=`g2V)~GW&(`8mO*Qql)lR0nGpQST8GkwNa-cM(AW@T<% zbAE1gMrZ7e{oc@-y@@$tvN{7gqcf|sGx<85ai&h5@9cNe8J)?Vxem|M8J+1cr(M>l zGdj~{Rx{VBGdhzwZ_}TpGdeSU##i1?XLM#|Zd`MIZgfUx?2Y~2(3!o7IbpIo13IHK ztFtrtI-PN*PM+`Vchec2$)33m&(j&5=`g2V)~GW&(`8mO*Qql)lR0nGpQST8GkwNa z-cM(AW@T<%bAE1gMrZ7e{oc@-y@@$tvN{7gqcf|sGx<85ai&h5@9cNe8J)?Vxem|M z8J+1cr(M>lGdj~{Rx{VBGdhzwZ_}TpGdeSU##i1?XLM#|Zd`MIZgfUx?2Y~2(3!o7 zIbpIo13IHKtFtrtI-PN*PM+`Vchec2$)33m&(j&5=`g2V)~GW&(`8mO*Qql)lR0nG zpQST8GkwNa-fv20K6~q>)AwJ0z>b@GS263KDZP9{W9L;DMt9>F4dWf`{eCkSZCt?H-wDy zS!ddB!6$wJoq1w17u>Lb&gjf_fA-Ai6)|t!u=H1`epJ)7e=DL<<^s@Ut%3QzBWNLfX5du1+Gdgp` zPRhH7&g5Bp(lG)$qcb{l%wEd6i_T<8d(Ke;I-@f>bJT9ixsT4|ID5)*0y?8JI&<89 z%D9uxWGH*akpeoSGdgqRj*7pR&cqvg!m$E6qcb{l?4F9go6bZFyZ_MwI-@f>bM&r? zy`Rp+`nvn^0y?8JI&=KKioB!FMC!VCgn-WIjLt;tthjsXOq{Jd#|Y?*&ge|c-io@b z&P2(&Zg4&(em9-bne3VC z@I0N-nGSQ>WsN$cGhJpibDcV)Gnw-?{aHGrGt*~$<^6OXLQEi*zXOU z*_)UXCaW``Gdi<6JCm=|8E5L``Obbfoza=>nd|U8oza;NbJ}H%I-@gPW;Ju2I-@h0 z^EUliI-@hwXME-TbVg@Z=EgPW=SF9A#@^WP4V~GWm=h+eGoUj%vpPGIuhSW4>g4&( zem9-bne3VC@I0N-nGSQ>WsN$cGhJpibDcV)Gnw-?{aHGrGt*~$<^6O zXLQEi*zXOU*_)UXCaW``Gdi<6JCm=|8E5L``Obbfoza=>nd|U8oza;NbJ}H%I-@gP zW;Ju2I-@h0^EUliI-@hwXME-TbVg@Z=EgPW=SF9A#@^WP4V~GW962ZVU%PekQ^Vli z?dMNNe%o{R(ixpOHy4-wpXp3&XZr5Cc`JYP@PW?E`>cn((e`_UOYcF{`|FI(9JW!5 z-$-YQf1n#)p3cObg*p>wyZzq4uQ~!cQ|DV$T#3%aorO9RXR9-{P7|G}^*ySsMrY#A zLY;}T)tOo+iO$se9@SQ(GjV63&cxa3Os$hdXKHwZ2ES z)#yyzS*SB{wmMVmB+;2#-=o@UbSCaB)R{P2ovC$_=uEBeQEfFk6L%KsOq{LG)H+FY zrq=hUwi=y@I}3Ft&Q@n?og_L_>w8pNjn2fKg*p>wt24Du5}m2_J*urnXX4I6or$y6 znOY}_&eZxI)mEc3ac7~<#M$aht&>D&YJHDttI?UbvruQ^Y;~sANuo2gzDKpy=uF&M zs55c4I#cT;(V1G`quOe8ChjcMnK)aWsdbX*Os(%xZ8bU*cNXeQoUP8(I!Sb<*7vBk z8l8zd3w0*WcI(V{pS*v|a>uYWlCsbTQ1zx@6by(e`>XQF4UyiL=$2TBnK5)cPLPR--dP)SZL}zM!k7}#YnYgo1XX0#irq)TKGqt`) zwbkfM+*znIake^B>m<>cTHmAEYIG*P(!i&eS?d zbf(t#sJ0rNi8~8*CeBu8YMmrHQ|o(FTaC`dorO9RXS;RA-`QN_?`+<_gTJ@A+R0vP zJvtM27V1o#?baDTmu0Y>Retpp(3!Zssxxu68gqHJI@7Z;+p2XYZoleGoUP7Wo~_R4 zjD5e~_d2sTF(*t`XFz9kW_5NZU#Bz9)XDRm{cbv=GuboO;dwfvGacr%%Nlh?XS&R4 z<~nsoXENt)`m=OKXQt2i%KPbz&aBLhYtGM&&ghK2vELgyvo|p(Ojc(=XLM$Db|zn^ zGtSh>^PT-}I-@h$GuPpHI-@fk=CsQibw+2p%xdO3bw+10=WY74bVg^U&-lvw>5R^- z%#CZ#&yCLLjJ>hn8#=Q$F(*t`XFz9kW_5NZU#Bz9)XDRm{cbv=GuboO;dwfvGacr% z%Nlh?XS&R4<~nsoXENt)`m=OKXQt2i%KPbz&aBLhYtGM&&ghK2vELgyvo|p(Ojc(= zXLM$Db|zn^GtSh>^PT-}I-@h$GuPpHI-@fk=CsQibw+2p%xdO3bw+10=WY74bVg^U z&-lvw>5R^-%#CZ#&yCLLjJ>hn8#=Q$F(*t`XFz9kW_5NZU#By%XXP(!i z&eS?dbf(t#sJ0rNi8~8*CeBu8YMmrHQ|o(FTaC`dorO9RXR9-{P7wZ2ES z)#yyzS*SB{wmMVmB+;2#-=o@UbSCaB)R{P2ovC$_=uEBeQEfFk6L%KsOq{LG)H+FY zrq=hUwi=y@I}3Ft&Q@n?og_L_>w8pNjn2fKg*p>wt24Du5}m2_J*urnXX4I6or$y6 znOY}_&eZxI)mEc3ac7~<#M$aht&>D&YJHDttI?UbvruQ^Y;~sANuo2gzDKpy=uF&M zs55c4TW9XScFRu@gL}815C8iTx3kKxo&q`(cNXeQobA>bKbK{oGd&x#ty*W|_Nvar z*=o$?+3JkW*!TN=uQPiSbHZeG26RSeR%d7Obvolrojl*!@1`?4lRa}Co~JW9(_v1# ztWjrlrpv5mu2W}pCUf4VKTBtHX8Me;yr0hK%*x!j=KS2~jLz5_`@NwvdlPfQWOW8~ zMrT%MXYzGA<4m1A-`Ve`Gdhz!a~+ia+~|zX*cia+~|zX*cBmPu_$gxW`8zM4I?VskYt$K?IeMlh+;2)}GTx)_4$5Eo z7w^8JGx_YE_!I)$?-3@P=@s80<9z<+gFE5hFXKI)VuUa5Qk}WDPu~88btdkAL&!Lv zb*B9meBu|-nI|T5!3_)OjLuy5XTgkK-tabP^?03W^|>tev4GC#jL!5r3UsE|_p(}@ zx$ca8ntz`0=K%k`+2!qGow>Yk-u;dJdB&e-u4`#VFT3xf%=PO`rnYAtA)qrlqccbB zq`Z6ROrEtT9V4JKI-@hk?4_)`=uDQh=Nu)VGdiO)NA0GZ`{+!Lv!@&Fpfft7Gso|%$UEvxq^^5M26XCmgR`#p80*E!aG-lnf#XLM%z zjIX?(&gjg_+_>ia+~|zX*cia+~|zX*cia+~|zX*cqv1Ikp41Hi>Y6s1 zKIs#l_xE%WL|<0AkRw4t=;|LQ_AQ-yy+^7A#n~#Dpic;(au~V~qkL1u?S>t;2Jul=Gwcox1sbsI=>d%s6Pu z=%0_!96%(O4(=Q({Wy3fg&8JjBEY1JD|pdV0=ZN#KZlAthswAOuBaqQoDT6$l6!C| z9)9j5W^8aUjC*5-{J@IfDwhVsGjN@Ra}Lf0I0uSRxE94Atd~RG zvm?Oe%MT~u1e|~qZ~{)i2{?i36F6y18OP1xM!4O#hf`xzZ*HMb(rB#>8^_hbO33zd z+crk^<`xPi4SRm?-_9zDJvi8Niu~6{ayNOoJ)zeP?IjI84_=k}FSo{%RU(JS_8!hm z?Z{u5(pX9PxBYL9eSW-Vjr-#riBeZrfJaMu9=s~?$3DMhM@=eIdqwZx&Po9uE!lI{ zivROP^Zn`4KENN7j36sh9HrY?{GoHDC|v@7T%9^d3Xan4TJ?XnyLi68Y#-o{=}EMy zj-zxti@%;Kxk~$#lw_*l;Qa>_NkgJd^<=8xkj4MHxq5fhun+JLg}|VFN(zqB?X3R& z`%?lQ={j?~n#I5B`v>264|W7unc9C)<{r?L~z%EL(scz4i#owNm^`w1D3LDOf-am_l6(!nKXTw=h;$K$$`Jq35rXM%m zCz3*AB}Ljlt%|?DknZ;b?K~ literal 0 HcmV?d00001 diff --git a/resources/win32/inno-small-150.bmp b/resources/win32/inno-small-150.bmp new file mode 100755 index 0000000000000000000000000000000000000000..3f65efc5f4c33f7c8dee74826756505f186848a7 GIT binary patch literal 20216 zcmeHNy-EW?5Wc*CrT7SzB4}e1>=nV@GO2t4eFB4^MH*{cOG`mfG+L*yun0l04p=By z=wM)3hMSwYS;Fn!PS`tU=Vtci`@Xrm{B7@i?-u{BP1vu(wh!ALY@K2S_C;|I#~+sF z+rI;Lzq!5)d!EXh?xuTg3|P2A8`#(?#FZi?HzPT2FeN6U4`+x4;EPLrEK z-4z1k{j-Hzmbaw<=&nx;zo#JSxjHa&l=hKbx<$-QMu7nT@vhz2E9o-FF zJrV9BK=^2re1=W=9sLn-94SNLxrh5fB z^FxZBtiIkA3)W3xLB*!~`{RXfOz+MSImuyX3#I~ozGii?_j)~QHQ$exl}vR#@1L}m zt-#*xb|t5ffJHI!V;BDXj&Mn)I_#stx^6t3tf8t{O=H83>JOQ+*gKsm8v_A~8g#lV zPDG`!xQ4?a8`DV%dwW=h=d(fEMVk`=S!ETJT+^-?V`?)gjIwTh9mL_0t_j z$}sE{GtTexDYSLZIN{=wFjqWfG^Kmazd@s}prk4FLvGT#8}?FJS!e3H=lmZt$psr2 z!}d>8_xV6vl61pf!GP&rLC*Y;q{mPUdWxa{*P-A=Oj%Gu-Pv_g3^42|suo_e=^oBu riYu7zDXJD;v*{krVTvo5?kTDkUbE>Q&S8oxnC>a67G87ax);SC>jLOQ literal 0 HcmV?d00001 diff --git a/resources/win32/inno-small-175.bmp b/resources/win32/inno-small-175.bmp new file mode 100755 index 0000000000000000000000000000000000000000..a11b53bcfe15b9cedeac90bcb8bc286de7037b11 GIT binary patch literal 26828 zcmeI3F-{v%6h$W+phQ^$E1*PzI!IYSnsiwMyFig5McD#%3YruVO^65$qN7Giq@+th z5x$J%!Y6+7zTemOh4n6CN33Qndj!b6HaJ*SGuYGZw~`Gp{hMODXnAzRtkeYGBQQe{_gx zU5ZSIOD2;u`F(d$-+tYmv#`+%Brq_oODXnA*SWgP!CM|3>N*k4GLZ=hP_a*BLIPBZ zeNwz}{WSf*x35PR(!jvC*`*Yj1cl{?S`Dl@Kb=mdbty6-E}6)L1gI4I#5I;kpHJb+ znOBNc75k)k_F#IR0G&Z>W=JNaBompC0F`2&)Nd%Ygqht_;`+9M(moe{SR3Rlr!cKc zDfY>-jLH+4klxIg)PL}52{Zf6tLr-LGP9lXO4o^KF0LmGoJz?PptD$mObAdWG9dvf znLhDv{4(6Qtc@xSK9d7wG%zp(v6-PvpU@N3vu;QxBqNjAGx2{_GON%QJnXc*QY??_ zXEAW9>3ITl7Hg0R0m?)sBtT`BeZmz+MFJ9#fCMBU0SQPz0uqpb1SB8<2}nQ!5+H$L Fcm!k1tRnyb literal 0 HcmV?d00001 diff --git a/resources/win32/inno-small-200.bmp b/resources/win32/inno-small-200.bmp new file mode 100755 index 0000000000000000000000000000000000000000..c0ad5436b66a9ab14e87d1a9ca1198b1ce51c505 GIT binary patch literal 35248 zcmeI4J&P1U5QfJ;Ac%073M$4TCYKrO?+^qvFk3T?yu`@heu0U~1(yhdaLXbWc*%hW z3JNEFfS|!0h#;KpY%m4IOubuE<95%z%goYLSJ(7D`&NIwE)sP~ zmT?5)B2kxQ8Al*45_L(IaRlNbQI}*HM<6Z|bxD?S1mYqsqRWFXyZw*DZ*O()|NK7e zA9kXMOeO?tn@#o_s{9R{QNNMA9i>va|%!Mo!zwk}Ly88*og}`fIaL zm(*pBx_rLXrdVwUlcL*=^LP!m8xmNHv)q2P`hN3X;Ia4Y4%kWQrZv@O$#}fndwQ#_ z*FO8NANvv#SjMk)c@_5sFW&8j>#{hO)MeZ}zA>XNyAuiL^BH$N>b@pei|6>x`n9ib zo&+BITaUp`N;e!(#w_IvT4EVznelkJM1JzpWNFPPl2&(_ESJHOE`UbY5?rvB9Frfik;bsUx`T{WkH-4~(N#!^$dc zZd)Ery`v+#33XWm)nFW!5wk3It?qameNCFYv`S=Y?c6PbO?|N}(^wI^8BfttOs(C9 zl~pR1CD-bs-uFjj5p#QPrJ+lX7MlOAd3KC6pPLAfyn8w20&3wmRzgnxQC=J<8=A% zKqS0T)MJUm!nvoW>>#I`;4F*#4PVAN&fh#+!w;@YvLs7m)|pr9!OvkCu`V;df64zY ziOX?^=!a+mrEKk#@jN~R7)MO9w9*5IN|t2#|L#~&mvL$%%ki)b`Ez6b_cA`29b?Z$ z)|bZ^6ZF!_62b&IktKu)aw1Cz6XZme5GKfpEFnyg6Inu-ASbefFhNda31Nbq$P&T? sIgurV334J!2ovN)mJlY$i7X*ZkP}%#m>?&zgfKx)WC>w{oT~Kr3n0c86951J literal 0 HcmV?d00001 diff --git a/resources/win32/inno-small-225.bmp b/resources/win32/inno-small-225.bmp new file mode 100755 index 0000000000000000000000000000000000000000..9a974c4d9c07c817000d700d83f4131103e60d33 GIT binary patch literal 44336 zcmeI5ziSjh6vsC zK2L>@hvDN%_?QSTgwH|nZ#f9o&I}&|NBGp|pL+t+2M6wxFhl=3IYU&iva$ln<9ia; zXN&@Ry&fi!=UEO94?QP<8UFF{IZPtYv$R^C0{{)b-3BG{Jk8>w=Kw&%5Bj|BMxgGU z=6M~0bG!c*k;eNRM;DL0XMg~3w%bLd@jeGRgCxKcI}B*Pp90~``*je^{VtD#5YX+c z8yuT2ZmKVx56@Bu_xtCTdM-@B)ZxUPneV53dVF4e`MNwG--oDu(pgu}g$Wo9!PFpU zm5DInjr`5>RDwXOn4)IEjX_(T>i1X zTVg}5l>U27M$0fUNP;zmjC<5=j2Sz#WXPPbvHSa^N>Jh`C@$@B4T|G1+%(v zDdgaE*?Ku!?6uh>J(nSX>D!FDfnil93ptmem%lGL=Vw)BOL8tlFMnTh&d;jMmgHQ9 zUjDvbaSqRccV0hmw|ri@Z6fa#=Rf;9nd2?9f#qWlnPF>j-EC5l@RPFuGVc94(cNT{ zvmisx$vPttshH$kYtH+dZ^A>ByCr;s(j>DaH{og0anJ+-;1^VrCONCjLK3X-bUJ^9 zN0=(RA%Zxn&ms%b6ws0;^{g@-NwC5*F>y+GgsHL{B8Z22-q`pizzS0!QZYT`ytgN~ zN>c@_2;w2<+1dM&?5HgeshA#eZjzp>e1C(SM_;}+i}yu`)vU^TZqoU4l{vF+*eb>7 zx-xZxoJU#OtTLRNR5w)4Ig16cP0wH*ITr&fdS!BMQr!?^N1BzNL?&m}4Gia*nS1uRP}vO;#L<0Gey#w+xytu9npB^w%z19^QDf>xl{r(- zcA3T3@;=hA{!`DG<6xElZ-VE~LIcIjczH=}lXEe!qE{;0xRaa(v0`Q%X=;0Naw^py zIg91B_i}VSXiw^NZG_9zXtmy(Rt^0wcF#l3tQ#sqvTHS5J3W2PzKzOmh#(&7xfa)h z4fx)kTGS1GubLftNe9yjx@EM$=Sv4B=UD2Q|E%t@&q#VTs{ROgapVL zBLi|KXAA*M7V4RtF$6GKsAqD<5Wr-ip2-|OTvX^-#qrsIB$KE`G&j8=6kcV@4lb;&d%*=a`5oe+1BRQ zf#Pw0alcaBuNL=Rt$oE~t2J0W|8mdPeN!kNHt=QG0q`*lrzj14z{j=&wyQ&az{j=& zwyQ&az{j=&wyQ&az{j=&wyQ&az{j=&wyQ&az{j=&wyQ&aZ1Wiyw_P8qg9DZwKz=OK z+1LOd@UiWH?dp&p@UiWH?dp&p@UiWH?dp8;GdbL?dRtwdc?!<}<4lj=Tv z#s~JQ-hR!0p#^R!Q+Ik)58ZI*+{>go_$UuLCwa<&Q@{s&q_ZOCH<}{k2l+vMst(Yc z#88~4x`-m?H%bZfo9+3{=kx7|QE_&K{LuWQ&u8lPkxx%9G*>o@QLpN<2?ci%zh zY+vfHW;6Zs&+8k9K0fYi?B?{_SFC|jdOCeRzTQ}9Ujk6G2|l>KQ@wi2jo`5L0oR*l zy(ToJr;#7z2l=TwPzp%RCh~*)R7VeYX2AJ}h6(vWeyrqY`r)a@J|_Bb|2Q{I6gtfb zr{t8MpI_f|HbmDKK2Ly;=hOr~o`zLMnD+W0MLs^kuGksQA6XvDH zUK|aFp3(Y1!|AX-9UK^`tBZNUA&1o>-wX4b!*mZx?sQm7@bQ4*Xd33ad@t~!rss5h zR6bI)TT%`&6z!^!A4(~w^>vExPjljf>(}^vp)*6_`kVh)NHITWJHQ3sQ(qsX$VY%> zy9xMgTT#0aYksReP^kgf^5rz|dV&rm(A7WrPt5A_^;Z085_n{7VGk8M7EXXu>ADe^N(SWBFLsPpYM)i@>0 zb@^V}d{TUW`h1L|L3O#)VJ)@!qvEpAMl}LHii=G^PilqAEfyI>vPJF{7k6bQl8L@C**CltJYp0XxA_A zU+(#<6?t3v@xxkz4`s2wPBl&mb6vg{_)zD2d#T1La;L*uf)8~b>jT@LFMyA2J~_Sr z37d5__M-C`r=-tkXa9ck^yfGH{e@2UG?HHKGi?2~v-u!DozW-!ULH`v+rncN&0h4# z+Xj~-+!6VqEarbujZ=g+2R8#B&Uu+0Y`kzs@Sz+YO;e3igf<5^3*)o1|GsAW>x1Co z@E>>N5?!N(2@S0&)%a>b4YK6Y5RD#7}| z<%%6`>6@KePziNl{5#d4*m0nJ&rU6<#Noh_LxW_-frSM#rJxXp1J{NeA|x{oV0~={ zg|iUwaoS==#PtI+D4c~LKTccBh?oADDFuZ%9Jv0#Awsg_fXDB5u~T$Dm8^OE`>K3g evGYpLowid8DxnTw{YE{pq=1hl8nl*H@%0yn(VE8q literal 0 HcmV?d00001 diff --git a/resources/win32/inno-small.bmp b/resources/win32/inno-small.bmp deleted file mode 100644 index 98feb812d2542ab614c8a7125eead6e9e63866f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12814 zcmeI2+e=hI9LG24LRf0q77;-dQ6v;dm!}AXAS$DJ3yY}!fYNeZm4d89)aU}4r4MD= zqh!6LyJy!+uHkKSm2$V#h!V`pgSb#3{F>j{#+BW}x^vES&Y)-D>)GQuJ2Ri(%=~_H z+4PLWG>e?}lfOHO4iI?&V2Nc5`4mbi2^I^ACi2)!rUgt3m=-WCFpmXBx@y7yDhC2@ z@|onX&xPsHf!W+=eg|Qwu?PaM?M(TwmK*DR@dl96ZL~!S?XOfhg4C4b1nhjx_gtnqoaM@G- zk8 zQ|zm(8tttE>=(2>;=Ss_mIU6)Q?zR^U&sBD&}<*7PBSynFHC-J7M9JGy9PS$AB3O2 zdQ}>1xJ~@eS|R%+jt>{{b&@SfmC!cvj5og^|t|T=c$dpW=8lf(_?MU)}ep8`ONo*r^o%jA9#hmTG3A8FW8BDiqsL!yUl2 z0uObS9}$MlooAKji0!3&frpxJY$m=WdcJ8ypG2RAz769s4=HhB8HTZuwGR4A9>RVg zN({g@YcENIo~I|csD-S7mH3QkH;e(p(!%wV!_E@w!Zlw-ivbHYcH>;K=Sc<(k{HKB zp5i^k@6vsl@IH?b<4ukMgfG?}70WY`=E`AzjlP?Q!fh4K+qn1^s>24t#zW$;;W!wm z%Z91%UM}i*a0n!O^xE8r@g}cvTz575ewsmd#W7*#$}*c&MysazDij;(lL2xz5*+n& zQRl;S(r#k(N%SL41smmkblgkVDt7_;qiBC(#CQwUgZO@&zw?j+7nY$H8^%|bcs%!A z+}BiUm-+su2OFMAd5U+3&KdEnkx{ Date: Thu, 15 Aug 2019 09:11:33 +0200 Subject: [PATCH 196/613] Revert "Update uglify-es (#79044)" This reverts commit e677c03899d62a5814637548b904c5ac75de4a6c. --- package.json | 4 ++-- yarn.lock | 25 +++++++++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 2074de1024a5a..f522296d780d2 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "gulp-shell": "^0.6.5", "gulp-tsb": "2.0.7", "gulp-tslint": "^8.1.3", - "gulp-uglify": "^3.0.2", + "gulp-uglify": "^3.0.0", "gulp-untar": "^0.0.7", "gulp-vinyl-zip": "^2.1.2", "http-server": "^0.11.1", @@ -133,7 +133,7 @@ "tslint": "^5.16.0", "typescript": "3.5.2", "typescript-formatter": "7.1.0", - "uglify-es": "^3.3.9", + "uglify-es": "^3.0.18", "underscore": "^1.8.2", "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index 8fabb92cfc06e..0a6b395f80dd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4047,18 +4047,15 @@ gulp-tslint@^8.1.3: plugin-error "1.0.1" through "~2.3.8" -gulp-uglify@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/gulp-uglify/-/gulp-uglify-3.0.2.tgz#5f5b2e8337f879ca9dec971feb1b82a5a87850b0" - integrity sha512-gk1dhB74AkV2kzqPMQBLA3jPoIAPd/nlNzP2XMDSG8XZrqnlCiDGAqC+rZOumzFvB5zOphlFh6yr3lgcAb/OOg== +gulp-uglify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/gulp-uglify/-/gulp-uglify-3.0.0.tgz#0df0331d72a0d302e3e37e109485dddf33c6d1ca" + integrity sha1-DfAzHXKg0wLj434QlIXd3zPG0co= dependencies: - array-each "^1.0.1" - extend-shallow "^3.0.2" gulplog "^1.0.0" has-gulplog "^0.1.0" - isobject "^3.0.1" + lodash "^4.13.1" make-error-cause "^1.1.1" - safe-buffer "^5.1.2" through2 "^2.0.0" uglify-js "^3.0.5" vinyl-sourcemaps-apply "^0.2.0" @@ -5458,7 +5455,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.15.0, lodash@^4.3.0: +lodash@^4.13.1, lodash@^4.15.0, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4= @@ -9136,7 +9133,15 @@ uc.micro@^1.0.1, uc.micro@^1.0.3: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" integrity sha1-ftUNXg+an7ClczeSWfKndFjVAZI= -uglify-es@^3.3.4, uglify-es@^3.3.9: +uglify-es@^3.0.18: + version "3.1.9" + resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.1.9.tgz#6c82df628ac9eb7af9c61fd70c744a084abe6161" + integrity sha512-wVSiJKHDgDDFmxTVVvnbAH6IpamAFHYDI+5JvwPdaqIMnk8kRTX2JKwq1Fx7gb2+Jj5Dus8kzvIpKkWOMNU51w== + dependencies: + commander "~2.11.0" + source-map "~0.6.1" + +uglify-es@^3.3.4: version "3.3.9" resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== From 8985ff7c9d38bd2ce7d1e6b58e40d27c5bbdb67b Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Tue, 13 Aug 2019 16:46:00 +0200 Subject: [PATCH 197/613] Work around minifier bug (#79044) --- .../services/extensions/node/proxyResolver.ts | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index 64c2e0a526e59..12a6e3ea66a37 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -469,24 +469,26 @@ async function readCaCertificates() { } async function readWindowsCaCertificates() { - const winCA = await import('vscode-windows-ca-certs'); - - let ders: any[] = []; - const store = winCA(); - try { - let der: any; - while (der = store.next()) { - ders.push(der); - } - } finally { - store.done(); - } + // Not using await to work around minifier bug (https://github.com/microsoft/vscode/issues/79044). + return import('vscode-windows-ca-certs') + .then(winCA => { + let ders: any[] = []; + const store = winCA(); + try { + let der: any; + while (der = store.next()) { + ders.push(der); + } + } finally { + store.done(); + } - const certs = new Set(ders.map(derToPem)); - return { - certs: Array.from(certs), - append: true - }; + const certs = new Set(ders.map(derToPem)); + return { + certs: Array.from(certs), + append: true + }; + }); } async function readMacCaCertificates() { From 6ccb91b918b4494677e9e1f53ed4f877dd50586c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 09:25:56 +0200 Subject: [PATCH 198/613] debt - avoid some StrictNullOverride --- src/vs/base/parts/ipc/node/ipc.cp.ts | 10 ++++++---- .../files/node/watcher/win32/csharpWatcherService.ts | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 9104deb503c02..42671a2e1a6b3 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -86,7 +86,7 @@ export interface IIPCOptions { export class Client implements IChannelClient, IDisposable { - private disposeDelayer: Delayer; + private disposeDelayer: Delayer | undefined; private activeRequests = new Set(); private child: ChildProcess | null; private _client: IPCClient | null; @@ -137,7 +137,7 @@ export class Client implements IChannelClient, IDisposable { cancellationTokenListener.dispose(); this.activeRequests.delete(disposable); - if (this.activeRequests.size === 0) { + if (this.activeRequests.size === 0 && this.disposeDelayer) { this.disposeDelayer.trigger(() => this.disposeClient()); } }); @@ -271,8 +271,10 @@ export class Client implements IChannelClient, IDisposable { dispose() { this._onDidProcessExit.dispose(); - this.disposeDelayer.cancel(); - this.disposeDelayer = null!; // StrictNullOverride: nulling out ok in dispose + if (this.disposeDelayer) { + this.disposeDelayer.cancel(); + this.disposeDelayer = undefined; + } this.disposeClient(); this.activeRequests.clear(); } diff --git a/src/vs/platform/files/node/watcher/win32/csharpWatcherService.ts b/src/vs/platform/files/node/watcher/win32/csharpWatcherService.ts index c2d96732a3c4f..1948481a857d3 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; } } } From 03a43bbacbc02d2946dc0397f1b8c2c66ca82df1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 09:31:57 +0200 Subject: [PATCH 199/613] debt - move issue service out of common since it will not be supported in the web for now --- src/vs/code/electron-browser/issue/issueReporterMain.ts | 2 +- src/vs/code/electron-browser/issue/issueReporterModel.ts | 2 +- .../code/electron-browser/issue/test/testReporterModel.test.ts | 2 +- .../electron-browser/processExplorer/processExplorerMain.ts | 2 +- src/vs/code/electron-main/app.ts | 2 +- src/vs/platform/issue/electron-browser/issueService.ts | 2 +- src/vs/platform/issue/electron-main/issueService.ts | 2 +- src/vs/platform/issue/{common => node}/issue.ts | 0 src/vs/platform/issue/node/issueIpc.ts | 2 +- .../contrib/issue/electron-browser/issue.contribution.ts | 2 +- src/vs/workbench/contrib/issue/electron-browser/issue.ts | 2 +- src/vs/workbench/contrib/issue/electron-browser/issueActions.ts | 2 +- src/vs/workbench/contrib/issue/electron-browser/issueService.ts | 2 +- src/vs/workbench/workbench.desktop.main.ts | 2 +- 14 files changed, 13 insertions(+), 13 deletions(-) rename src/vs/platform/issue/{common => node}/issue.ts (100%) diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index e6ee71c50f002..b6be9c71cc9db 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -31,7 +31,7 @@ import { WindowsService } from 'vs/platform/windows/electron-browser/windowsServ import { MainProcessService, IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/code/electron-browser/issue/issueReporterModel'; -import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; +import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/node/issue'; import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; diff --git a/src/vs/code/electron-browser/issue/issueReporterModel.ts b/src/vs/code/electron-browser/issue/issueReporterModel.ts index c1f78969153e6..0d51dc8f300a2 100644 --- a/src/vs/code/electron-browser/issue/issueReporterModel.ts +++ b/src/vs/code/electron-browser/issue/issueReporterModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { assign } from 'vs/base/common/objects'; -import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; +import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/node/issue'; import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService'; export interface IssueReporterData { diff --git a/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts b/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts index 95857638e41a4..c69454639917f 100644 --- a/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts +++ b/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel'; import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil'; -import { IssueType } from 'vs/platform/issue/common/issue'; +import { IssueType } from 'vs/platform/issue/node/issue'; suite('IssueReporter', () => { diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts index 321a5611e746b..bf7140842612e 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts @@ -9,7 +9,7 @@ import { repeat } from 'vs/base/common/strings'; import { totalmem } from 'os'; import product from 'vs/platform/product/node/product'; import { localize } from 'vs/nls'; -import { ProcessExplorerStyles, ProcessExplorerData } from 'vs/platform/issue/common/issue'; +import { ProcessExplorerStyles, ProcessExplorerData } from 'vs/platform/issue/node/issue'; import * as browser from 'vs/base/browser/browser'; import * as platform from 'vs/base/common/platform'; import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 15e9c438ddb26..a8787943fc936 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -47,7 +47,7 @@ import { getMachineId } from 'vs/base/node/id'; import { Win32UpdateService } from 'vs/platform/update/electron-main/updateService.win32'; import { LinuxUpdateService } from 'vs/platform/update/electron-main/updateService.linux'; import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateService.darwin'; -import { IIssueService } from 'vs/platform/issue/common/issue'; +import { IIssueService } from 'vs/platform/issue/node/issue'; import { IssueChannel } from 'vs/platform/issue/node/issueIpc'; import { IssueService } from 'vs/platform/issue/electron-main/issueService'; import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc'; diff --git a/src/vs/platform/issue/electron-browser/issueService.ts b/src/vs/platform/issue/electron-browser/issueService.ts index 29ea55b6044a9..6228ef3367121 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 48b0293e933a6..65aa0fb4c05f3 100644 --- a/src/vs/platform/issue/electron-main/issueService.ts +++ b/src/vs/platform/issue/electron-main/issueService.ts @@ -6,7 +6,7 @@ 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'; 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 4eca97ef53cb1..271bcf5ceeebb 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/workbench/contrib/issue/electron-browser/issue.contribution.ts b/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts index 3aa862c759987..1cb252b5e686c 100644 --- a/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts +++ b/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts @@ -13,7 +13,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issue'; import { WorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issueService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IIssueService } from 'vs/platform/issue/common/issue'; +import { IIssueService } from 'vs/platform/issue/node/issue'; const helpCategory = { value: nls.localize('help', "Help"), original: 'Help' }; const workbenchActionsRegistry = Registry.as(Extensions.WorkbenchActions); diff --git a/src/vs/workbench/contrib/issue/electron-browser/issue.ts b/src/vs/workbench/contrib/issue/electron-browser/issue.ts index 2cc057f6bccd4..ca98eb60acae1 100644 --- a/src/vs/workbench/contrib/issue/electron-browser/issue.ts +++ b/src/vs/workbench/contrib/issue/electron-browser/issue.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IssueReporterData } from 'vs/platform/issue/common/issue'; +import { IssueReporterData } from 'vs/platform/issue/node/issue'; export const IWorkbenchIssueService = createDecorator('workbenchIssueService'); diff --git a/src/vs/workbench/contrib/issue/electron-browser/issueActions.ts b/src/vs/workbench/contrib/issue/electron-browser/issueActions.ts index 4eaaf7c30296b..4b6b25c31c0a4 100644 --- a/src/vs/workbench/contrib/issue/electron-browser/issueActions.ts +++ b/src/vs/workbench/contrib/issue/electron-browser/issueActions.ts @@ -5,7 +5,7 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; -import { IssueType } from 'vs/platform/issue/common/issue'; +import { IssueType } from 'vs/platform/issue/node/issue'; import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issue'; export class OpenProcessExplorer extends Action { diff --git a/src/vs/workbench/contrib/issue/electron-browser/issueService.ts b/src/vs/workbench/contrib/issue/electron-browser/issueService.ts index e93eac14556a1..5dfea6f09da4f 100644 --- a/src/vs/workbench/contrib/issue/electron-browser/issueService.ts +++ b/src/vs/workbench/contrib/issue/electron-browser/issueService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IssueReporterStyles, IIssueService, IssueReporterData, ProcessExplorerData, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; +import { IssueReporterStyles, IIssueService, IssueReporterData, ProcessExplorerData, IssueReporterExtensionData } from 'vs/platform/issue/node/issue'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { textLinkForeground, inputBackground, inputBorder, inputForeground, buttonBackground, buttonHoverBackground, buttonForeground, inputValidationErrorBorder, foreground, inputActiveOptionBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, editorBackground, editorForeground, listHoverBackground, listHoverForeground, listHighlightForeground, textLinkActiveForeground } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index d426224accf4a..6da5363c04abb 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -66,7 +66,7 @@ import { IWindowsService } from 'vs/platform/windows/common/windows'; import { WindowsService } from 'vs/platform/windows/electron-browser/windowsService'; import { IUpdateService } from 'vs/platform/update/common/update'; import { UpdateService } from 'vs/platform/update/electron-browser/updateService'; -import { IIssueService } from 'vs/platform/issue/common/issue'; +import { IIssueService } from 'vs/platform/issue/node/issue'; import { IssueService } from 'vs/platform/issue/electron-browser/issueService'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { WorkspacesService } from 'vs/platform/workspaces/electron-browser/workspacesService'; From 262bb4c4ad50b3cb1e4fd72db7a895a9b042a0ba Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 09:42:43 +0200 Subject: [PATCH 200/613] debt - make diagnostics service only accessible from node --- .../electron-browser/issue/issueReporterMain.ts | 2 +- .../electron-browser/issue/issueReporterModel.ts | 2 +- .../processExplorer/processExplorerMain.ts | 2 +- .../sharedProcess/sharedProcessMain.ts | 3 +-- src/vs/code/electron-main/app.ts | 2 +- .../{diagnosticsService.ts => diagnostics.ts} | 14 -------------- .../platform/diagnostics/node/diagnosticsIpc.ts | 3 ++- .../diagnostics/node/diagnosticsService.ts | 15 ++++++++++++++- .../platform/issue/electron-main/issueService.ts | 3 ++- .../launch/electron-main/launchService.ts | 2 +- .../electron-browser/remote.contribution.ts | 2 +- .../stats/electron-browser/workspaceStats.ts | 2 +- .../remote/common/abstractRemoteAgentService.ts | 2 +- .../common/remoteAgentEnvironmentChannel.ts | 2 +- .../services/remote/common/remoteAgentService.ts | 2 +- 15 files changed, 29 insertions(+), 29 deletions(-) rename src/vs/platform/diagnostics/common/{diagnosticsService.ts => diagnostics.ts} (69%) diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index b6be9c71cc9db..591cbed96f333 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -39,7 +39,7 @@ import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil'; import { Button } from 'vs/base/browser/ui/button/button'; import { withUndefinedAsNull } from 'vs/base/common/types'; -import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; import { SpdLogService } from 'vs/platform/log/node/spdlogService'; const MAX_URL_LENGTH = 2045; diff --git a/src/vs/code/electron-browser/issue/issueReporterModel.ts b/src/vs/code/electron-browser/issue/issueReporterModel.ts index 0d51dc8f300a2..6c7fa528e3770 100644 --- a/src/vs/code/electron-browser/issue/issueReporterModel.ts +++ b/src/vs/code/electron-browser/issue/issueReporterModel.ts @@ -5,7 +5,7 @@ import { assign } from 'vs/base/common/objects'; import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/node/issue'; -import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; export interface IssueReporterData { issueType: IssueType; diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts index bf7140842612e..557957dfd0b26 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts @@ -17,7 +17,7 @@ import { popup } from 'vs/base/parts/contextmenu/electron-browser/contextmenu'; import { ProcessItem } from 'vs/base/common/processes'; import { addDisposableListener } from 'vs/base/browser/dom'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; let mapPidToWindowTitle = new Map(); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 905e9fd88dbc8..8ef5cc8dd0d3f 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -48,8 +48,7 @@ import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/ import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { SpdLogService } from 'vs/platform/log/node/spdlogService'; -import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; -import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { DiagnosticsService, IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; import { DiagnosticsChannel } from 'vs/platform/diagnostics/node/diagnosticsIpc'; import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index a8787943fc936..159eb32b49c5b 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -81,8 +81,8 @@ import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; import { VSBuffer } from 'vs/base/common/buffer'; import { statSync } from 'fs'; import { ISignService } from 'vs/platform/sign/common/sign'; -import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnosticsService'; import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc'; +import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; 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 48cc4eb90817b..9adfc578f6496 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 2e2bf87472b4d..76af1758163e2 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 13552c9ab44e6..b94ff1f9145c5 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/issue/electron-main/issueService.ts b/src/vs/platform/issue/electron-main/issueService.ts index 65aa0fb4c05f3..aa31eb1913aa1 100644 --- a/src/vs/platform/issue/electron-main/issueService.ts +++ b/src/vs/platform/issue/electron-main/issueService.ts @@ -9,7 +9,8 @@ import { parseArgs } from 'vs/platform/environment/node/argv'; 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/launch/electron-main/launchService.ts b/src/vs/platform/launch/electron-main/launchService.ts index 95e56eae4dacb..c9924cf39d531 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/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index 99f4eefd121b1..00e601638ecf2 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -27,7 +27,7 @@ import { DialogChannel } from 'vs/platform/dialogs/node/dialogIpc'; import { DownloadServiceChannel } from 'vs/platform/download/common/downloadIpc'; import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc'; import { ipcRenderer as ipc } from 'electron'; -import { IDiagnosticInfoOptions, IRemoteDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { IDiagnosticInfoOptions, IRemoteDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IProgressService, IProgress, IProgressStep, ProgressLocation } from 'vs/platform/progress/common/progress'; import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; diff --git a/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts b/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts index d6d6c795e0cad..7457e3b6bcd10 100644 --- a/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts +++ b/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts @@ -15,7 +15,7 @@ import { endsWith } from 'vs/base/common/strings'; import { ITextFileService, } from 'vs/workbench/services/textfile/common/textfiles'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats'; -import { IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnostics'; const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/; const SshUrlMatcher = /^([^@:]+@)?([^:]+):(.+)$/; diff --git a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts index 3a789e23d9855..167e7a5bf145a 100644 --- a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts @@ -17,7 +17,7 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } f import { Registry } from 'vs/platform/registry/common/platform'; import { RemoteExtensionEnvironmentChannelClient } from 'vs/workbench/services/remote/common/remoteAgentEnvironmentChannel'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; import { Emitter } from 'vs/base/common/event'; import { ISignService } from 'vs/platform/sign/common/sign'; diff --git a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts index ae87e384bb406..9f0fb09442ec7 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts @@ -8,7 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; export interface IGetEnvironmentDataArguments { language: string; diff --git a/src/vs/workbench/services/remote/common/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts index bc4d49641e12f..fdd61483fd5b3 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts @@ -6,7 +6,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { RemoteAgentConnectionContext, IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnosticsService'; +import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; import { Event } from 'vs/base/common/event'; import { PersistenConnectionEvent as PersistentConnectionEvent, ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; From ce3052c94bd4126858e1cddcd32edc24c994263c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 09:54:14 +0200 Subject: [PATCH 201/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f522296d780d2..f1d6a015cff3f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "8ee1612ab2b050f366c2c4c1500fc243b407581f", + "distro": "27bb4f0e93f58466a9b7813e31d69010675d5a19", "author": { "name": "Microsoft Corporation" }, From 86aa7c850ed70837e9d9df86c7e6af398bdf33cc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 10:54:11 +0200 Subject: [PATCH 202/613] tests - add more debug for randomly failing getUntitledWorkspacesSync --- .../workspacesMainService.test.ts | 371 ++++++++---------- 1 file changed, 169 insertions(+), 202 deletions(-) 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 6d3f1024f3e04..42556b610fae2 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)); - - 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()); + 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)); - 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)); - - 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()); + 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)); - 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 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); + 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)); - 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(); - - 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'); - - 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, '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); - }); + 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 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, '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); }); - 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}`)); - - let origContent = fs.readFileSync(workspace.configPath.fsPath).toString(); - origContent = `// this is a comment\n${origContent}`; + 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 newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath); + let origContent = fs.readFileSync(workspace.configPath.fsPath).toString(); + origContent = `// this is a comment\n${origContent}`; - 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 - - const newContent = rewriteWorkspaceFileForNewLocation(origContent, workspace.configPath, workspaceConfigPath); + 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 ws = JSON.parse(newContent) as IStoredWorkspace; - assert.ok(ws.folders.every(f => (f).path.indexOf('\\') < 0)); + let origContent = fs.readFileSync(workspace.configPath.fsPath).toString(); + origContent = origContent.replace(/[\\]/g, '/'); // convert backslash to slash - 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,56 @@ 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); - - return createWorkspace([os.tmpdir(), process.cwd()]).then(untitledTwo => { - assert.ok(fs.existsSync(untitledTwo.configPath.fsPath)); - - 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); + const untitledTwo = await createWorkspace([os.tmpdir(), process.cwd()]); + assert.ok(fs.existsSync(untitledTwo.configPath.fsPath)); + untitled = service.getUntitledWorkspacesSync(); + 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); - service.deleteUntitledWorkspaceSync(untitledOne); - untitled = service.getUntitledWorkspacesSync(); - assert.equal(1, 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); }); }); From 15bf846973c7d534bd91a596fb0e211bff7a2b3e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 10:55:01 +0200 Subject: [PATCH 203/613] fix mispell --- .../contrib/relauncher/common/relauncher.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/relauncher/common/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/common/relauncher.contribution.ts index d3c0d4f064f69..2ec3089894f45 100644 --- a/src/vs/workbench/contrib/relauncher/common/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/common/relauncher.contribution.ts @@ -159,7 +159,7 @@ export class WorkspaceChangeExtHostRelauncher extends Disposable implements IWor constructor( @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IExtensionService extensionService: IExtensionService, - @IWindowService windowSevice: IWindowService, + @IWindowService windowService: IWindowService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService ) { super(); @@ -170,7 +170,7 @@ export class WorkspaceChangeExtHostRelauncher extends Disposable implements IWor } if (environmentService.configuration.remoteAuthority) { - windowSevice.reloadWindow(); // TODO@aeschli, workaround + windowService.reloadWindow(); // TODO@aeschli, workaround } else { extensionService.restartExtensionHost(); } From 2e4c7199ceb7ae88228fd552cde60e5387d64adc Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Aug 2019 10:49:13 +0200 Subject: [PATCH 204/613] :lipstick: --- src/vs/workbench/browser/web.simpleservices.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 33737a0296653..3c0cc078e0122 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -847,7 +847,7 @@ registerSingleton(ITunnelService, SimpleTunnelService); //#region workspace stats -class WorkspaceStatsService implements IWorkspaceStatsService { +class SimpleWorkspaceStatsService implements IWorkspaceStatsService { _serviceBrand: any; @@ -865,6 +865,6 @@ class WorkspaceStatsService implements IWorkspaceStatsService { } -registerSingleton(IWorkspaceStatsService, WorkspaceStatsService); +registerSingleton(IWorkspaceStatsService, SimpleWorkspaceStatsService); //#endregion From bca9d85350d757f870989ccfc76a351d32091c32 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Aug 2019 10:56:03 +0200 Subject: [PATCH 205/613] update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f1d6a015cff3f..93f89b87fccc1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "27bb4f0e93f58466a9b7813e31d69010675d5a19", + "distro": "714d51354d7a0135859224aefc1cd6893ff3bc4a", "author": { "name": "Microsoft Corporation" }, @@ -158,4 +158,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} +} \ No newline at end of file From 634e8ea93aa1431e4bb41faa1487ecab9ab04559 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 15 Aug 2019 10:57:48 +0200 Subject: [PATCH 206/613] Simplify tasks command --- .../tasks/browser/runAutomaticTasks.ts | 38 +++++++------------ .../tasks/browser/task.contribution.ts | 5 +-- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts b/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts index b3bccaf94e007..cbe96131cb7e3 100644 --- a/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts +++ b/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts @@ -12,6 +12,7 @@ import { RunOnOptions, Task, TaskRunSource } from 'vs/workbench/contrib/tasks/co import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { Action } from 'vs/base/common/actions'; +import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; const ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE = 'tasks.run.allowAutomatic'; @@ -131,38 +132,27 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut } -export class AllowAutomaticTaskRunning extends Action { +export class ManageAutomaticTaskRunning extends Action { - public static readonly ID = 'workbench.action.tasks.allowAutomaticRunning'; - public static readonly LABEL = nls.localize('workbench.action.tasks.allowAutomaticRunning', "Allow Automatic Tasks in Folder"); + public static readonly ID = 'workbench.action.tasks.manageAutomaticRunning'; + public static readonly LABEL = nls.localize('workbench.action.tasks.manageAutomaticRunning', "Manage Automatic Tasks in Folder"); constructor( id: string, label: string, - @IStorageService private readonly storageService: IStorageService + @IStorageService private readonly storageService: IStorageService, + @IQuickInputService private readonly quickInputService: IQuickInputService ) { super(id, label); } - public run(event?: any): Promise { - this.storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, true, StorageScope.WORKSPACE); - return Promise.resolve(undefined); - } -} - -export class DisallowAutomaticTaskRunning extends Action { - - public static readonly ID = 'workbench.action.tasks.disallowAutomaticRunning'; - public static readonly LABEL = nls.localize('workbench.action.tasks.disallowAutomaticRunning', "Disallow Automatic Tasks in Folder"); - - constructor( - id: string, label: string, - @IStorageService private readonly storageService: IStorageService - ) { - super(id, label); - } + public async run(event?: any): Promise { + const allowItem: IQuickPickItem = { label: nls.localize('workbench.action.tasks.allowAutomaticTasks', "Allow Automatic Tasks in Folder") }; + const disallowItem: IQuickPickItem = { label: nls.localize('workbench.action.tasks.disallowAutomaticTasks', "Disallow Automatic Tasks in Folder") }; + const value = await this.quickInputService.pick([allowItem, disallowItem], { canPickMany: false }); + if (!value) { + return; + } - public run(event?: any): Promise { - this.storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, false, StorageScope.WORKSPACE); - return Promise.resolve(undefined); + this.storageService.store(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, value === allowItem, StorageScope.WORKSPACE); } } diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index de6fbc81085e6..92d2f493108c9 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -32,7 +32,7 @@ import { QuickOpenActionContributor } from '../browser/quickOpen'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { RunAutomaticTasks, AllowAutomaticTaskRunning, DisallowAutomaticTaskRunning } from 'vs/workbench/contrib/tasks/browser/runAutomaticTasks'; +import { RunAutomaticTasks, ManageAutomaticTaskRunning } from 'vs/workbench/contrib/tasks/browser/runAutomaticTasks'; let tasksCategory = nls.localize('tasksCategory', "Tasks"); @@ -40,8 +40,7 @@ const workbenchRegistry = Registry.as(Workbench workbenchRegistry.registerWorkbenchContribution(RunAutomaticTasks, LifecyclePhase.Eventually); const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(AllowAutomaticTaskRunning, AllowAutomaticTaskRunning.ID, AllowAutomaticTaskRunning.LABEL), 'Tasks: Allow Automatic Tasks in Folder', tasksCategory); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisallowAutomaticTaskRunning, DisallowAutomaticTaskRunning.ID, DisallowAutomaticTaskRunning.LABEL), 'Tasks: Disallow Automatic Tasks in Folder', tasksCategory); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ManageAutomaticTaskRunning, ManageAutomaticTaskRunning.ID, ManageAutomaticTaskRunning.LABEL), 'Tasks: Manage Automatic Tasks in Folder', tasksCategory); export class TaskStatusBarContributions extends Disposable implements IWorkbenchContribution { private runningTasksStatusItem: IStatusbarEntryAccessor | undefined; From 88e76958cfd2d517e5c11a0655f946e391b32ba5 Mon Sep 17 00:00:00 2001 From: DiamondYuan Date: Thu, 15 Aug 2019 17:09:16 +0800 Subject: [PATCH 207/613] docs: fix type (#79129) --- src/vs/base/parts/storage/node/storage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/parts/storage/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts index 6cebac12c9feb..edf313654aa0c 100644 --- a/src/vs/base/parts/storage/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -206,7 +206,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { return this.doUpdateItems(recoveryConnection, { insert: recovery() }).then(() => closeRecoveryConnection(), error => { // In case of an error updating items, still ensure to close the connection - // to prevent SQLITE_BUSY errors when the connection is restablished + // to prevent SQLITE_BUSY errors when the connection is reestablished closeRecoveryConnection(); return Promise.reject(error); From 7c53175eef11996733e3301a3e4fce349ddd8140 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 11:11:50 +0200 Subject: [PATCH 208/613] web - add todo for potentially cyclic dependency --- src/vs/workbench/browser/web.simpleservices.ts | 2 ++ .../contrib/debug/browser/extensionHostDebugService.ts | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 3c0cc078e0122..ce9a691a28b67 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -412,6 +412,8 @@ export class SimpleWindowService extends Disposable implements IWindowService { } closeWindow(): Promise { + window.close(); + return Promise.resolve(); } diff --git a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts index 3f5b703c4ce46..5be0fa24b5c60 100644 --- a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts +++ b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts @@ -17,7 +17,7 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient { constructor( @IRemoteAgentService remoteAgentService: IRemoteAgentService, - //@IWindowService windowService: IWindowService, + // @IWindowService windowService: IWindowService, // TODO@weinand TODO@isidorn cyclic dependency? @IEnvironmentService environmentService: IEnvironmentService ) { const connection = remoteAgentService.getConnection(); @@ -30,13 +30,11 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient { this._register(this.onReload(event => { if (environmentService.isExtensionDevelopment && environmentService.debugExtensionHost.debugId === event.sessionId) { - //windowService.reloadWindow(); window.location.reload(); } })); this._register(this.onClose(event => { if (environmentService.isExtensionDevelopment && environmentService.debugExtensionHost.debugId === event.sessionId) { - //this._windowService.closeWindow(); window.close(); } })); From cdea3dcf3b6f5f1ca6dda9809f93d57eec9dde64 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 15 Aug 2019 11:15:31 +0200 Subject: [PATCH 209/613] Multi-select in custom tree view (#78625) Part of #76941 The first argument is now the element that the command is executed on. The second argurment is an array of the other selected items --- src/vs/vscode.proposed.d.ts | 9 +++ .../api/browser/mainThreadTreeViews.ts | 3 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostTreeViews.ts | 17 +++++- .../browser/parts/views/customView.ts | 57 +++++++++++++------ src/vs/workbench/common/views.ts | 2 + 6 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6328f593b3f41..0b9ad6976fa1a 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1046,6 +1046,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 diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 79968a69f71fa..3e42831d23f70 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/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e4a618d695bc2..259f0d506585f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -242,7 +242,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; diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 71c8ecf346fee..66c8e3ed7f7ef 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -52,10 +52,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; } @@ -182,10 +193,10 @@ 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; - 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/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 6c714028d8f46..9fd89851d9d1e 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -164,9 +164,11 @@ export class CustomTreeView extends Disposable implements ITreeView { private domNode!: HTMLElement; private treeContainer!: HTMLElement; private _messageValue: string | undefined; + private _canSelectMany: boolean = false; private messageElement!: HTMLDivElement; private tree: WorkbenchAsyncDataTree | undefined; private treeLabels: ResourceLabels | undefined; + private root: ITreeItem; private elementsToRefresh: ITreeItem[] = []; private menus: TitleMenus; @@ -253,6 +255,14 @@ export class CustomTreeView extends Disposable implements ITreeView { this.updateMessage(); } + get canSelectMany(): boolean { + return this._canSelectMany; + } + + set canSelectMany(canSelectMany: boolean) { + this._canSelectMany = canSelectMany; + } + get hasIconForParentNode(): boolean { return this._hasIconForParentNode; } @@ -372,12 +382,14 @@ export class CustomTreeView extends Disposable implements ITreeView { collapseByDefault: (e: ITreeItem): boolean => { return e.collapsibleState !== TreeItemCollapsibleState.Expanded; }, - multipleSelectionSupport: false - })); + multipleSelectionSupport: this.canSelectMany, + }) as WorkbenchAsyncDataTree); aligner.tree = this.tree; + const actionRunner = new MultipleSelectionActionRunner(() => this.tree!.getSelection()); + renderer.actionRunner = actionRunner; this.tree.contextKeyService.createKey(this.id, true); - this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e))); + this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e, actionRunner))); this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.elements))); this._register(this.tree.onDidChangeCollapseState(e => { if (!e.node.element) { @@ -406,7 +418,7 @@ export class CustomTreeView extends Disposable implements ITreeView { })); } - private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent): void { + private onContextMenu(treeMenus: TreeMenus, treeEvent: ITreeContextMenuEvent, actionRunner: MultipleSelectionActionRunner): void { const node: ITreeItem | null = treeEvent.element; if (node === null) { return; @@ -442,7 +454,7 @@ export class CustomTreeView extends Disposable implements ITreeView { getActionsContext: () => ({ $treeViewId: this.id, $treeItemHandle: node.handle }), - actionRunner: new MultipleSelectionActionRunner(() => this.tree!.getSelection()) + actionRunner }); } @@ -686,6 +698,8 @@ class TreeRenderer extends Disposable implements ITreeRenderer{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle }); + templateData.actionBar.context = { $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); + if (this._actionRunner) { + templateData.actionBar.actionRunner = this._actionRunner; + } this.setAlignment(templateData.container, node); templateData.elementDisposable = (this.themeService.onDidFileIconThemeChange(() => this.setAlignment(templateData.container, node))); } @@ -822,23 +843,23 @@ class Aligner extends Disposable { class MultipleSelectionActionRunner extends ActionRunner { - constructor(private getSelectedResources: (() => any[])) { + constructor(private getSelectedResources: (() => ITreeItem[])) { super(); } - runAction(action: IAction, context: any): Promise { - if (action instanceof MenuItemAction) { - const selection = this.getSelectedResources(); - const filteredSelection = selection.filter(s => s !== context); - - if (selection.length === filteredSelection.length || selection.length === 1) { - return action.run(context); - } - - return action.run(context, ...filteredSelection); + runAction(action: IAction, context: TreeViewItemHandleArg): Promise { + const selection = this.getSelectedResources(); + let selectionHandleArgs: TreeViewItemHandleArg[] | undefined = undefined; + if (selection.length > 1) { + selectionHandleArgs = []; + selection.forEach(selected => { + if (selected.handle !== context.$treeItemHandle) { + selectionHandleArgs!.push({ $treeViewId: context.$treeViewId, $treeItemHandle: selected.handle }); + } + }); } - return super.runAction(action, context); + return action.run(...[context, selectionHandleArgs]); } } diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index f5663067bf026..ab8b188c72dc0 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -310,6 +310,8 @@ export interface ITreeView extends IDisposable { showCollapseAllAction: boolean; + canSelectMany: boolean; + message?: string; readonly visible: boolean; From 35b9b7fbe3e01e801d2ab0f37aeb3117dfe85eac Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Aug 2019 11:20:09 +0200 Subject: [PATCH 210/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 93f89b87fccc1..337b97fc017be 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "714d51354d7a0135859224aefc1cd6893ff3bc4a", + "distro": "5645023d26f429c60aff97c4ce9f3192bfbf5945", "author": { "name": "Microsoft Corporation" }, From 2147b8a9d93aa44951fdbc759f4fc9bea522b0c9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 11:36:38 +0200 Subject: [PATCH 211/613] web - enable feedback contribution --- .../preferences/browser/preferencesSearch.ts | 3 +++ .../integrity/browser/integrityService.ts | 19 +++++++++++++++++++ .../integrity/node/integrityService.ts | 5 +++-- src/vs/workbench/workbench.common.main.ts | 7 ++++--- src/vs/workbench/workbench.desktop.main.ts | 3 --- src/vs/workbench/workbench.web.main.ts | 1 + 6 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 src/vs/workbench/services/integrity/browser/integrityService.ts diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index ef36014032de0..b876c396a7f42 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -25,6 +25,7 @@ import { nullRange } from 'vs/workbench/services/preferences/common/preferencesM import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStringDictionary } from 'vs/base/common/collections'; import { IProductService } from 'vs/platform/product/common/product'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export interface IEndpointDetails { urlBase?: string; @@ -564,3 +565,5 @@ export class SettingMatches { }; } } + +registerSingleton(IPreferencesSearchService, PreferencesSearchService, true); diff --git a/src/vs/workbench/services/integrity/browser/integrityService.ts b/src/vs/workbench/services/integrity/browser/integrityService.ts new file mode 100644 index 0000000000000..5ddf9d5b3f961 --- /dev/null +++ b/src/vs/workbench/services/integrity/browser/integrityService.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IIntegrityService, IntegrityTestResult } from 'vs/workbench/services/integrity/common/integrity'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; + +export class BrowserIntegrityServiceImpl implements IIntegrityService { + + _serviceBrand!: ServiceIdentifier; + + async isPure(): Promise { + return { isPure: true, proof: [] }; + } +} + +registerSingleton(IIntegrityService, BrowserIntegrityServiceImpl, true); diff --git a/src/vs/workbench/services/integrity/node/integrityService.ts b/src/vs/workbench/services/integrity/node/integrityService.ts index 00e083a9b9910..021fb9543b545 100644 --- a/src/vs/workbench/services/integrity/node/integrityService.ts +++ b/src/vs/workbench/services/integrity/node/integrityService.ts @@ -14,6 +14,7 @@ import product from 'vs/platform/product/node/product'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; interface IStorageData { dontShowPrompt: boolean; @@ -55,7 +56,7 @@ class IntegrityStorage { export class IntegrityServiceImpl implements IIntegrityService { - _serviceBrand: any; + _serviceBrand!: ServiceIdentifier; private _storage: IntegrityStorage; private _isPurePromise: Promise; @@ -159,4 +160,4 @@ export class IntegrityServiceImpl implements IIntegrityService { } } -registerSingleton(IIntegrityService, IntegrityServiceImpl, true); \ No newline at end of file +registerSingleton(IIntegrityService, IntegrityServiceImpl, true); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 59355a0c6c455..e58f7058329e6 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -123,9 +123,7 @@ import 'vs/workbench/contrib/telemetry/browser/telemetry.contribution'; // Preferences import 'vs/workbench/contrib/preferences/browser/preferences.contribution'; import 'vs/workbench/contrib/preferences/browser/keybindingsEditorContribution'; -import { IPreferencesSearchService } from 'vs/workbench/contrib/preferences/common/preferences'; -import { PreferencesSearchService } from 'vs/workbench/contrib/preferences/browser/preferencesSearch'; -registerSingleton(IPreferencesSearchService, PreferencesSearchService, true); +import 'vs/workbench/contrib/preferences/browser/preferencesSearch'; // Logs import 'vs/workbench/contrib/logs/common/logs.contribution'; @@ -231,4 +229,7 @@ import 'vs/workbench/contrib/outline/browser/outline.contribution'; // Experiments import 'vs/workbench/contrib/experiments/browser/experiments.contribution'; +// Send a Smile +import 'vs/workbench/contrib/feedback/browser/feedback.contribution'; + //#endregion diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 6da5363c04abb..f6a48a0281232 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -131,9 +131,6 @@ import 'vs/workbench/contrib/codeEditor/electron-browser/codeEditor.contribution // Execution import 'vs/workbench/contrib/externalTerminal/node/externalTerminalService'; -// Send a Smile -import 'vs/workbench/contrib/feedback/browser/feedback.contribution'; - // Update import 'vs/workbench/contrib/update/electron-browser/update.contribution'; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 169b3c07ef6cf..8d06f34a906c1 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -26,6 +26,7 @@ import 'vs/workbench/browser/web.main'; //#region --- workbench services +import 'vs/workbench/services/integrity/browser/integrityService'; import 'vs/workbench/services/textMate/browser/textMateService'; import 'vs/workbench/services/search/common/searchService'; import 'vs/workbench/services/output/common/outputChannelModelService'; From dad6117f363d06f3e13c7f0f0148a543d4de78b5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 11:39:31 +0200 Subject: [PATCH 212/613] debt - more tests diag --- .../workspaces/test/electron-main/workspacesMainService.test.ts | 2 ++ 1 file changed, 2 insertions(+) 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 42556b610fae2..395e709bd283d 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -342,7 +342,9 @@ suite('WorkspacesMainService', () => { 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'))}`); From 0a08b58f522fa794d4054bf56298e59ee1408569 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Aug 2019 11:45:27 +0200 Subject: [PATCH 213/613] do not look for executables in web --- .../electron-browser/extensionTipsService.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts index 5610f403056ca..7925515610506 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts @@ -22,7 +22,6 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionsConfiguration, ConfigurationKey, ShowRecommendationsOnlyOnDemandKey, IExtensionsViewlet, IExtensionsWorkbenchService, EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import * as os from 'os'; import { flatten, distinct, shuffle, coalesce } from 'vs/base/common/arrays'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { guessMimeTypes, MIME_UNKNOWN } from 'vs/base/common/mime'; @@ -42,6 +41,7 @@ import { extname } from 'vs/base/common/resources'; import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/product'; import { timeout } from 'vs/base/common/async'; import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; +import { Platform } from 'vs/base/common/platform'; const milliSecondsInADay = 1000 * 60 * 60 * 24; const choiceNever = localize('neverShowAgain', "Don't Show Again"); @@ -982,11 +982,13 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe /** * If user has any of the tools listed in this.productService.productConfiguration.exeBasedExtensionTips, fetch corresponding recommendations */ - private fetchExecutableRecommendations(important: boolean): Promise { - const homeDir = os.homedir(); - let foundExecutables: Set = new Set(); + private async fetchExecutableRecommendations(important: boolean): Promise { + if (Platform.Web) { + return; + } - let findExecutable = (exeName: string, tip: IExeBasedExtensionTip, path: string) => { + const foundExecutables: Set = new Set(); + const findExecutable = (exeName: string, tip: IExeBasedExtensionTip, path: string) => { return this.fileService.exists(URI.file(path)).then(exists => { if (exists && !foundExecutables.has(exeName)) { foundExecutables.add(exeName); @@ -1002,7 +1004,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe }); }; - let promises: Promise[] = []; + const promises: Promise[] = []; // Loop through recommended extensions forEach(this.productService.productConfiguration.exeBasedExtensionTips, entry => { if (typeof entry.value !== 'object' || !Array.isArray(entry.value['recommendations'])) { @@ -1025,11 +1027,11 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe promises.push(findExecutable(exeName, entry.value, windowsPath)); } else { promises.push(findExecutable(exeName, entry.value, join('/usr/local/bin', exeName))); - promises.push(findExecutable(exeName, entry.value, join(homeDir, exeName))); + promises.push(findExecutable(exeName, entry.value, join(this.environmentService.userHome, exeName))); } }); - return Promise.all(promises).then(() => undefined); + await Promise.all(promises); } /** From 29761995336a8bc70847d3a0eb53ecbbe4443cdd Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Aug 2019 11:50:36 +0200 Subject: [PATCH 214/613] Move extension tips service to web and enable extension recommendations --- .../{electron-browser => browser}/extensionTipsService.ts | 0 .../contrib/extensions/browser/extensions.contribution.ts | 4 +++- .../extensions/electron-browser/extensions.contribution.ts | 5 +---- .../test/electron-browser/extensionsActions.test.ts | 2 +- .../test/electron-browser/extensionsTipsService.test.ts | 2 +- .../extensions/test/electron-browser/extensionsViews.test.ts | 2 +- .../test/electron-browser/extensionsWorkbenchService.test.ts | 2 +- 7 files changed, 8 insertions(+), 9 deletions(-) rename src/vs/workbench/contrib/extensions/{electron-browser => browser}/extensionTipsService.ts (100%) diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts similarity index 100% rename from src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts rename to src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 32bd703fba030..8dfa256fcf631 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionsLabel, ExtensionsChannelId, PreferencesLabel, IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/contrib/output/common/output'; @@ -45,8 +45,10 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { RemoteExtensionsInstaller } from 'vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller'; +import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; // Singletons +registerSingleton(IExtensionTipsService, ExtensionTipsService); registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); Registry.as(OutputExtensions.OutputChannels) diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts index 643c6e1b4563b..4c21e1e91815e 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -7,9 +7,7 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/electron-browser/extensionTipsService'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -25,7 +23,6 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler'; // Singletons -registerSingleton(IExtensionTipsService, ExtensionTipsService); registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); @@ -134,4 +131,4 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { }, group: 'navigation', when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(RuntimeExtensionsEditor.ID)) -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index bce6e57e28702..32c9e48f8aa1d 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -16,7 +16,7 @@ import { import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/electron-browser/extensionTipsService'; +import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IURLService } from 'vs/platform/url/common/url'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts index b66c6143f2439..941a3b69a460a 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -15,7 +15,7 @@ import { DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/electron-browser/extensionTipsService'; +import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { Emitter } from 'vs/base/common/event'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index a618098b66cb1..318a7e17e450a 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -17,7 +17,7 @@ import { import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionTipsService, ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/electron-browser/extensionTipsService'; +import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IURLService } from 'vs/platform/url/common/url'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 04ed61d0d8a7c..c87fed721f4bb 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -17,7 +17,7 @@ import { import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/electron-browser/extensionTipsService'; +import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IURLService } from 'vs/platform/url/common/url'; From 7639d9b78e7d2f80aba10267b63ad7c9f053e190 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Aug 2019 12:00:12 +0200 Subject: [PATCH 215/613] use process.setImmediate --- .../contrib/extensions/browser/extensionTipsService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts index 7925515610506..6a6987a2bf856 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts @@ -41,7 +41,7 @@ import { extname } from 'vs/base/common/resources'; import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/product'; import { timeout } from 'vs/base/common/async'; import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; -import { Platform } from 'vs/base/common/platform'; +import { Platform, setImmediate } from 'vs/base/common/platform'; const milliSecondsInADay = 1000 * 60 * 60 * 24; const choiceNever = localize('neverShowAgain', "Don't Show Again"); From eed66ab862624ec8be2d6ac138f304382f02158c Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 15 Aug 2019 12:29:21 +0200 Subject: [PATCH 216/613] minor polish --- .../workbench/contrib/debug/browser/debugEditorActions.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index 645e55984a5f9..bf802266912d4 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -35,23 +35,25 @@ class ToggleBreakpointAction extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { - if (editor.hasModel() && editor.getSelections()) { + if (editor.hasModel()) { const debugService = accessor.get(IDebugService); const modelUri = editor.getModel().uri; const canSet = debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel()); // Does not account for multi line selections, Set to remove multiple cursor on the same line const lineNumbers = [...new Set(editor.getSelections().map(s => s.getPosition().lineNumber))]; + return Promise.all(lineNumbers.map(line => { const bps = debugService.getModel().getBreakpoints({ lineNumber: line, uri: modelUri }); if (bps.length) { return Promise.all(bps.map(bp => debugService.removeBreakpoints(bp.getId()))); } else if (canSet) { return (debugService.addBreakpoints(modelUri, [{ lineNumber: line }], 'debugEditorActions.toggleBreakpointAction')); - } else { //Line where a breakpoint cant be set + } else { return Promise.resolve([]); } })); } + return Promise.resolve(); } } From fd9ecea286378377978b551fb164576bb1ed9667 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 15 Aug 2019 12:34:21 +0200 Subject: [PATCH 217/613] fixes #79168 --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index a366a0555e348..97836b9a96410 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -576,7 +576,7 @@ function isDebugModel(obj: any): obj is IDebugModel { } function isDebugSession(obj: any): obj is IDebugSession { - return typeof obj.getAllThreads === 'function'; + return obj && typeof obj.getAllThreads === 'function'; } function isDeemphasized(frame: IStackFrame): boolean { From 163aed3817c0ae3829ec79ec765cb18e73333a7e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 15 Aug 2019 12:06:48 +0200 Subject: [PATCH 218/613] fix exports trap --- .../api/worker/extHostExtensionService.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index 0b49644e7ccdf..ada9786cd0f7c 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -82,36 +82,35 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { throw new Error(`Cannot load module '${mod}'`); } - const exports = Object.create(null); - patchSelf.module = { exports }; - patchSelf.exports = exports; + const moduleExportsTrap = { exports: Object.create(null) }; + patchSelf.module = moduleExportsTrap; + patchSelf.exports = moduleExportsTrap.exports; const next = joinPath(parent, '..', ensureSuffix(mod, '.js')); moduleStack.push(next); importScripts(asDomUri(next).toString(true)); moduleStack.pop(); - return exports; + return moduleExportsTrap.exports; }; try { activationTimesBuilder.codeLoadingStart(); - const exports = Object.create(null); - patchSelf.module = { exports }; - patchSelf.exports = exports; + const moduleExportsTrap = { exports: Object.create(null) }; + patchSelf.module = moduleExportsTrap; + patchSelf.exports = moduleExportsTrap.exports; module = module.with({ path: ensureSuffix(module.path, '.js') }); moduleStack.push(module); importScripts(asDomUri(module).toString(true)); moduleStack.pop(); + return Promise.resolve(moduleExportsTrap.exports); } finally { activationTimesBuilder.codeLoadingStop(); } - - return Promise.resolve(exports); } async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise { From f6484fd3478ef218afedd59b5b14df6380304fb6 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 15 Aug 2019 12:52:23 +0200 Subject: [PATCH 219/613] debug: prevent expression.value being undefined fixes #79169 --- src/vs/workbench/contrib/debug/common/debugModel.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 7166ad996a718..16a838721c4ee 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -227,14 +227,14 @@ export class Expression extends ExpressionContainer implements IExpression { const response = await session.evaluate(this.name, stackFrame ? stackFrame.frameId : undefined, context); this.available = !!(response && response.body); if (response && response.body) { - this.value = response.body.result; + this.value = response.body.result || ''; this.reference = response.body.variablesReference; this.namedVariables = response.body.namedVariables; this.indexedVariables = response.body.indexedVariables; this.type = response.body.type || this.type; } } catch (e) { - this.value = e.message; + this.value = e.message || ''; this.available = false; this.reference = 0; } @@ -256,7 +256,7 @@ export class Variable extends ExpressionContainer implements IExpression { reference: number | undefined, public name: string, public evaluateName: string | undefined, - value: string, + value: string | undefined, namedVariables: number | undefined, indexedVariables: number | undefined, public presentationHint: DebugProtocol.VariablePresentationHint | undefined, @@ -265,7 +265,7 @@ export class Variable extends ExpressionContainer implements IExpression { startOfVariables = 0 ) { super(session, reference, `variable:${parent.getId()}:${name}`, namedVariables, indexedVariables, startOfVariables); - this.value = value; + this.value = value || ''; } async setVariable(value: string): Promise { @@ -276,7 +276,7 @@ export class Variable extends ExpressionContainer implements IExpression { try { const response = await this.session.setVariable((this.parent).reference, this.name, value); if (response && response.body) { - this.value = response.body.value; + this.value = response.body.value || ''; this.type = response.body.type || this.type; this.reference = response.body.variablesReference; this.namedVariables = response.body.namedVariables; From 2b22c0a0b293644cc7484d1f53383838f6c1b3a0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 13:04:56 +0200 Subject: [PATCH 220/613] update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 337b97fc017be..e5af8e0f35165 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "5645023d26f429c60aff97c4ce9f3192bfbf5945", + "distro": "c2664514acafa174a6b0b5a960931699641d6989", "author": { "name": "Microsoft Corporation" }, @@ -158,4 +158,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} \ No newline at end of file +} From 92dafb390cdfed6abc029c833a53cb4be7caebbf Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 13:13:58 +0200 Subject: [PATCH 221/613] web - implement credentials provider and add API --- .../workbench/api/browser/mainThreadKeytar.ts | 38 ++--- src/vs/workbench/browser/web.main.ts | 7 +- .../credentials/browser/credentialsService.ts | 132 ++++++++++++++++++ .../credentials/common/credentials.ts | 6 +- .../credentials/node/credentialsService.ts | 8 +- .../environment/browser/environmentService.ts | 18 +-- .../environment/common/environmentService.ts | 3 + .../fileUserDataProvider.test.ts | 6 +- src/vs/workbench/workbench.desktop.main.ts | 4 +- src/vs/workbench/workbench.web.api.ts | 6 + src/vs/workbench/workbench.web.main.ts | 1 + tslint.json | 1 + 12 files changed, 179 insertions(+), 51 deletions(-) create mode 100644 src/vs/workbench/services/credentials/browser/credentialsService.ts rename src/vs/{platform => workbench/services}/credentials/common/credentials.ts (84%) rename src/vs/{platform => workbench/services}/credentials/node/credentialsService.ts (80%) diff --git a/src/vs/workbench/api/browser/mainThreadKeytar.ts b/src/vs/workbench/api/browser/mainThreadKeytar.ts index fff0a902e2c72..f07b7688801a3 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; - } - - dispose(): void { - // - } + @ICredentialsService private readonly _credentialsService: ICredentialsService, + ) { } async $getPassword(service: string, account: string): Promise { - if (this._credentialsService) { - return this._credentialsService.getPassword(service, account); - } - return null; + return this._credentialsService.getPassword(service, account); } async $setPassword(service: string, account: string, password: string): Promise { - if (this._credentialsService) { - return this._credentialsService.setPassword(service, account, password); - } + 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; + return this._credentialsService.deletePassword(service, account); } async $findPassword(service: string): Promise { - if (this._credentialsService) { - return this._credentialsService.findPassword(service); - } - return null; + return this._credentialsService.findPassword(service); + } + + dispose(): void { + // } } diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 909f7578e4c0a..812663e25d43c 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -117,12 +117,7 @@ class CodeRendererMain extends Disposable { const payload = await this.resolveWorkspaceInitializationPayload(); // Environment - const environmentService = new BrowserWorkbenchEnvironmentService({ - workspaceId: payload.id, - remoteAuthority: this.configuration.remoteAuthority, - webviewEndpoint: this.configuration.webviewEndpoint, - connectionToken: this.configuration.connectionToken - }); + const environmentService = new BrowserWorkbenchEnvironmentService(payload.id, this.configuration); serviceCollection.set(IWorkbenchEnvironmentService, environmentService); // Product diff --git a/src/vs/workbench/services/credentials/browser/credentialsService.ts b/src/vs/workbench/services/credentials/browser/credentialsService.ts new file mode 100644 index 0000000000000..b7613b9c23c1e --- /dev/null +++ b/src/vs/workbench/services/credentials/browser/credentialsService.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; + +export interface ICredentialsProvider { + getPassword(service: string, account: string): Promise; + setPassword(service: string, account: string, password: string): Promise; + deletePassword(service: string, account: string): Promise; + findPassword(service: string): Promise; +} + +export class BrowserCredentialsService implements ICredentialsService { + + _serviceBrand!: ServiceIdentifier; + + private credentialsProvider: ICredentialsProvider; + + constructor(@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService) { + if (environmentService.options && environmentService.options.credentialsProvider) { + this.credentialsProvider = environmentService.options.credentialsProvider; + } else { + this.credentialsProvider = new LocalStorageCredentialsProvider(); + } + } + + async getPassword(service: string, account: string): Promise { + return this.credentialsProvider.getPassword(service, account); + } + + async setPassword(service: string, account: string, password: string): Promise { + return this.credentialsProvider.setPassword(service, account, password); + } + + async deletePassword(service: string, account: string): Promise { + return this.credentialsProvider.deletePassword(service, account); + } + + async findPassword(service: string): Promise { + return this.credentialsProvider.findPassword(service); + } +} + +interface ICredential { + service: string; + account: string; + password: string; +} + +class LocalStorageCredentialsProvider implements ICredentialsProvider { + + static readonly CREDENTIALS_OPENED_KEY = 'credentials.provider'; + + private _credentials: ICredential[]; + private get credentials(): ICredential[] { + if (!this._credentials) { + try { + const serializedCredentials = window.localStorage.getItem(LocalStorageCredentialsProvider.CREDENTIALS_OPENED_KEY); + if (serializedCredentials) { + this._credentials = JSON.parse(serializedCredentials); + } + } catch (error) { + // ignore + } + + if (!Array.isArray(this._credentials)) { + this._credentials = []; + } + } + + return this._credentials; + } + + private save(): void { + window.localStorage.setItem(LocalStorageCredentialsProvider.CREDENTIALS_OPENED_KEY, JSON.stringify(this.credentials)); + } + + async getPassword(service: string, account: string): Promise { + return this.doGetPassword(service, account); + } + + private async doGetPassword(service: string, account?: string): Promise { + for (const credential of this.credentials) { + if (credential.service === service) { + if (typeof account !== 'string' || account === credential.account) { + return credential.password; + } + } + } + + return null; + } + + async setPassword(service: string, account: string, password: string): Promise { + this.deletePassword(service, account); + + this.credentials.push({ service, account, password }); + + this.save(); + } + + async deletePassword(service: string, account: string): Promise { + let found = false; + + this._credentials = this.credentials.filter(credential => { + if (credential.service === service && credential.account === account) { + found = true; + + return false; + } + + return true; + }); + + if (found) { + this.save(); + } + + return found; + } + + async findPassword(service: string): Promise { + return this.doGetPassword(service); + } +} + +registerSingleton(ICredentialsService, BrowserCredentialsService, true); diff --git a/src/vs/platform/credentials/common/credentials.ts b/src/vs/workbench/services/credentials/common/credentials.ts similarity index 84% rename from src/vs/platform/credentials/common/credentials.ts rename to src/vs/workbench/services/credentials/common/credentials.ts index dc6618d89f780..8fa520c374e74 100644 --- a/src/vs/platform/credentials/common/credentials.ts +++ b/src/vs/workbench/services/credentials/common/credentials.ts @@ -3,12 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; export const ICredentialsService = createDecorator('ICredentialsService'); export interface ICredentialsService { - _serviceBrand: any; + + _serviceBrand: ServiceIdentifier; + getPassword(service: string, account: string): Promise; setPassword(service: string, account: string, password: string): Promise; deletePassword(service: string, account: string): Promise; diff --git a/src/vs/platform/credentials/node/credentialsService.ts b/src/vs/workbench/services/credentials/node/credentialsService.ts similarity index 80% rename from src/vs/platform/credentials/node/credentialsService.ts rename to src/vs/workbench/services/credentials/node/credentialsService.ts index 282b761fe2e65..1361f169be39f 100644 --- a/src/vs/platform/credentials/node/credentialsService.ts +++ b/src/vs/workbench/services/credentials/node/credentialsService.ts @@ -3,8 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials'; import { IdleValue } from 'vs/base/common/async'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; type KeytarModule = { getPassword(service: string, account: string): Promise; @@ -15,7 +17,7 @@ type KeytarModule = { export class KeytarCredentialsService implements ICredentialsService { - _serviceBrand: any; + _serviceBrand!: ServiceIdentifier; private readonly _keytar = new IdleValue>(() => import('keytar')); @@ -39,3 +41,5 @@ export class KeytarCredentialsService implements ICredentialsService { return keytar.findPassword(service); } } + +registerSingleton(ICredentialsService, KeytarCredentialsService, true); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 250fd43582e24..c8e2227fc0d91 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IWindowConfiguration, IPath, IPathsToWaitFor } from 'vs/platform/windows/common/windows'; -import { IEnvironmentService, IExtensionHostDebugParams, IDebugParams, BACKUPS } from 'vs/platform/environment/common/environment'; +import { IExtensionHostDebugParams, IDebugParams, BACKUPS } from 'vs/platform/environment/common/environment'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; import { IProcessEnvironment } from 'vs/base/common/platform'; @@ -13,6 +13,8 @@ import { ExportData } from 'vs/base/common/performance'; import { LogLevel } from 'vs/platform/log/common/log'; import { joinPath } from 'vs/base/common/resources'; import { Schemas } from 'vs/base/common/network'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; export class BrowserWindowConfiguration implements IWindowConfiguration { @@ -66,26 +68,26 @@ export interface IBrowserWindowConfiguration { connectionToken?: string; } -export class BrowserWorkbenchEnvironmentService implements IEnvironmentService { +export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironmentService { - _serviceBrand!: ServiceIdentifier; + _serviceBrand!: ServiceIdentifier; readonly configuration: IWindowConfiguration = new BrowserWindowConfiguration(); - constructor(configuration: IBrowserWindowConfiguration) { + constructor(workspaceId: string, public readonly options: IWorkbenchConstructionOptions) { this.args = { _: [] }; this.appRoot = '/web/'; this.appNameLong = 'Visual Studio Code - Web'; - this.configuration.remoteAuthority = configuration.remoteAuthority; + this.configuration.remoteAuthority = options.remoteAuthority; this.userRoamingDataHome = URI.file('/User').with({ scheme: Schemas.userData }); this.settingsResource = joinPath(this.userRoamingDataHome, 'settings.json'); this.keybindingsResource = joinPath(this.userRoamingDataHome, 'keybindings.json'); this.keyboardLayoutResource = joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); this.localeResource = joinPath(this.userRoamingDataHome, 'locale.json'); this.backupHome = joinPath(this.userRoamingDataHome, BACKUPS); - this.configuration.backupWorkspaceResource = joinPath(this.backupHome, configuration.workspaceId); - this.configuration.connectionToken = configuration.connectionToken || this.getConnectionTokenFromLocation(); + this.configuration.backupWorkspaceResource = joinPath(this.backupHome, workspaceId); + this.configuration.connectionToken = options.connectionToken || this.getConnectionTokenFromLocation(); this.logsPath = '/web/logs'; @@ -94,7 +96,7 @@ export class BrowserWorkbenchEnvironmentService implements IEnvironmentService { break: false }; - this.webviewEndpoint = configuration.webviewEndpoint; + this.webviewEndpoint = options.webviewEndpoint; this.untitledWorkspacesHome = URI.from({ scheme: Schemas.untitled, path: 'Workspaces' }); if (document && document.location && document.location.search) { diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index fd4beaf134d76..72c98796b9ac3 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -6,6 +6,7 @@ import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; export const IWorkbenchEnvironmentService = createDecorator('environmentService'); @@ -14,4 +15,6 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { _serviceBrand: ServiceIdentifier; readonly configuration: IWindowConfiguration; + + readonly options?: IWorkbenchConstructionOptions; } diff --git a/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts b/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts index b8ba819bba8fb..854e9582f5a0a 100644 --- a/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts +++ b/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts @@ -47,7 +47,7 @@ suite('FileUserDataProvider', () => { userDataResource = URI.file(userDataPath).with({ scheme: Schemas.userData }); await Promise.all([pfs.mkdirp(userDataPath), pfs.mkdirp(backupsPath)]); - const environmentService = new BrowserWorkbenchEnvironmentService({ workspaceId: 'workspaceId' }); + const environmentService = new BrowserWorkbenchEnvironmentService('workspaceId', { remoteAuthority: 'remote' }); environmentService.userRoamingDataHome = userDataResource; const userDataFileSystemProvider = new FileUserDataProvider(URI.file(userDataPath), URI.file(backupsPath), diskFileSystemProvider, environmentService); @@ -321,7 +321,7 @@ suite('FileUserDataProvider - Watching', () => { localUserDataResource = URI.file(userDataPath); userDataResource = localUserDataResource.with({ scheme: Schemas.userData }); - const environmentService = new BrowserWorkbenchEnvironmentService({ workspaceId: 'workspaceId' }); + const environmentService = new BrowserWorkbenchEnvironmentService('workspaceId', { remoteAuthority: 'remote' }); environmentService.userRoamingDataHome = userDataResource; const userDataFileSystemProvider = new FileUserDataProvider(localUserDataResource, localBackupsResource, new TestFileSystemProvider(fileEventEmitter.event), environmentService); @@ -475,4 +475,4 @@ suite('FileUserDataProvider - Watching', () => { type: FileChangeType.DELETED }]); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index f6a48a0281232..3373be0bd3508 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -49,6 +49,7 @@ import 'vs/workbench/services/accessibility/node/accessibilityService'; import 'vs/workbench/services/remote/node/tunnelService'; import 'vs/workbench/services/backup/node/backupFileService'; import 'vs/workbench/services/opener/electron-browser/openerService'; +import 'vs/workbench/services/credentials/node/credentialsService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -74,8 +75,6 @@ import { IMenubarService } from 'vs/platform/menubar/common/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; import { IURLService } from 'vs/platform/url/common/url'; import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; -import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; registerSingleton(IClipboardService, ClipboardService, true); registerSingleton(IRequestService, RequestService, true); @@ -89,7 +88,6 @@ registerSingleton(IIssueService, IssueService); registerSingleton(IWorkspacesService, WorkspacesService); registerSingleton(IMenubarService, MenubarService); registerSingleton(IURLService, RelayURLService); -registerSingleton(ICredentialsService, KeytarCredentialsService, true); //#endregion diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 15156252d69f7..2727fa5eb5d77 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -8,6 +8,7 @@ import { main } from 'vs/workbench/browser/web.main'; import { UriComponents } from 'vs/base/common/uri'; import { IFileSystemProvider } from 'vs/platform/files/common/files'; import { IWebSocketFactory } from 'vs/platform/remote/browser/browserSocketFactory'; +import { ICredentialsProvider } from 'vs/workbench/services/credentials/browser/credentialsService'; export interface IWorkbenchConstructionOptions { @@ -53,6 +54,11 @@ export interface IWorkbenchConstructionOptions { * Experimental: Whether to enable the smoke test driver. */ driver?: boolean; + + /** + * Experimental: The credentials provider to store and retrieve secrets. + */ + credentialsProvider?: ICredentialsProvider; } /** diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 8d06f34a906c1..40384b249b094 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -36,6 +36,7 @@ import 'vs/workbench/services/extensions/browser/extensionService'; import 'vs/workbench/services/extensionManagement/common/extensionManagementServerService'; import 'vs/workbench/services/telemetry/browser/telemetryService'; import 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; +import 'vs/workbench/services/credentials/browser/credentialsService'; import 'vs/workbench/browser/web.simpleservices'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; diff --git a/tslint.json b/tslint.json index 548b9a5119103..4b23918f0fcdf 100644 --- a/tslint.json +++ b/tslint.json @@ -436,6 +436,7 @@ "**/vs/base/**/common/**", "**/vs/platform/**/common/**", "**/vs/editor/common/**", + "**/vs/workbench/workbench.web.api", "**/vs/workbench/common/**", "**/vs/workbench/services/**/common/**", "**/vs/workbench/api/**/common/**", From 40b5ba9cf24e344db916a5ab65e79ca4b3b636fa Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 13:57:15 +0200 Subject: [PATCH 222/613] web - workaround clipboard issue with selection type --- src/vs/platform/clipboard/browser/clipboardService.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vs/platform/clipboard/browser/clipboardService.ts b/src/vs/platform/clipboard/browser/clipboardService.ts index 8ea9e8e026df2..c5e8a2676d28f 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(); } From 032ffe6ae1e81c344377cafde58d47784aefdd9c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 15 Aug 2019 14:03:14 +0200 Subject: [PATCH 223/613] better exports trapping --- .../api/worker/extHostExtensionService.ts | 82 +++++++++++++++---- 1 file changed, 68 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index ada9786cd0f7c..d3d36327339a1 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -43,6 +43,69 @@ class ApiInstances { } } +class ExportsTrap { + + static readonly Instance = new ExportsTrap(); + + private readonly _names: string[] = []; + private readonly _exports = new Map(); + + private constructor() { + + const exportsProxy = new Proxy({}, { + set: (target: any, p: PropertyKey, value: any, receiver: any) => { + // store in target + target[p] = value; + // store in named-bucket + const name = this._names[this._names.length - 1]; + this._exports.get(name)![p] = value; + return true; + } + }); + + + const moduleProxy = new Proxy({}, { + + get: (target: any, p: PropertyKey) => { + if (p === 'exports') { + return exportsProxy; + } + + return target[p]; + }, + + set: (target: any, p: PropertyKey, value: any, receiver: any) => { + // store in target + target[p] = value; + + // override bucket + if (p === 'exports') { + const name = this._names[this._names.length - 1]; + this._exports.set(name, value); + } + return true; + } + }); + + (self).exports = exportsProxy; + (self).module = moduleProxy; + } + + add(name: string) { + this._exports.set(name, Object.create(null)); + this._names.push(name); + + return { + claim: () => { + const result = this._exports.get(name); + this._exports.delete(name); + this._names.pop(); + return result; + } + }; + } +} + export class ExtHostExtensionService extends AbstractExtHostExtensionService { private _apiInstances?: ApiInstances; @@ -57,7 +120,6 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { protected _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { - interface FakeCommonJSSelf { module?: object; exports?: object; @@ -82,38 +144,30 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { throw new Error(`Cannot load module '${mod}'`); } - const moduleExportsTrap = { exports: Object.create(null) }; - patchSelf.module = moduleExportsTrap; - patchSelf.exports = moduleExportsTrap.exports; - const next = joinPath(parent, '..', ensureSuffix(mod, '.js')); moduleStack.push(next); + const trap = ExportsTrap.Instance.add(next.toString()); importScripts(asDomUri(next).toString(true)); moduleStack.pop(); - return moduleExportsTrap.exports; + return trap.claim(); }; try { activationTimesBuilder.codeLoadingStart(); - - const moduleExportsTrap = { exports: Object.create(null) }; - patchSelf.module = moduleExportsTrap; - patchSelf.exports = moduleExportsTrap.exports; - module = module.with({ path: ensureSuffix(module.path, '.js') }); moduleStack.push(module); - + const trap = ExportsTrap.Instance.add(module.toString()); importScripts(asDomUri(module).toString(true)); moduleStack.pop(); - return Promise.resolve(moduleExportsTrap.exports); + return Promise.resolve(trap.claim()); } finally { activationTimesBuilder.codeLoadingStop(); } } - async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise { + async $setRemoteEnvironment(_env: { [key: string]: string | null }): Promise { throw new Error('Not supported'); } } From f5ade17659504c1b305959b265fbe0712b757e1a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 15 Aug 2019 14:37:08 +0200 Subject: [PATCH 224/613] fix process layer-breaker --- src/vs/workbench/contrib/extensions/browser/extensionEditor.ts | 3 ++- .../contrib/extensions/browser/extensionTipsService.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 2a19adea3df60..e31c90cb95be3 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -53,6 +53,7 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/common/webview'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { generateUuid } from 'vs/base/common/uuid'; +import { platform } from 'vs/base/common/process'; function removeEmbeddedSVGs(documentContent: string): string { const newDocument = new DOMParser().parseFromString(documentContent, 'text/html'); @@ -1275,7 +1276,7 @@ export class ExtensionEditor extends BaseEditor { private resolveKeybinding(rawKeyBinding: IKeyBinding): ResolvedKeybinding | null { let key: string | undefined; - switch (process.platform) { + switch (platform) { case 'win32': key = rawKeyBinding.win; break; case 'linux': key = rawKeyBinding.linux; break; case 'darwin': key = rawKeyBinding.mac; break; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts index 6a6987a2bf856..4597b0270e75b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts @@ -42,6 +42,7 @@ import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/comm import { timeout } from 'vs/base/common/async'; import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; import { Platform, setImmediate } from 'vs/base/common/platform'; +import { platform } from 'vs/base/common/process'; const milliSecondsInADay = 1000 * 60 * 60 * 24; const choiceNever = localize('neverShowAgain', "Don't Show Again"); @@ -1014,7 +1015,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return; } const exeName = entry.key; - if (process.platform === 'win32') { + if (platform === 'win32') { let windowsPath = entry.value['windowsPath']; if (!windowsPath || typeof windowsPath !== 'string') { return; From 5d4a2514c9db09909bc06b34033a4f0117c1ba72 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 15 Aug 2019 14:57:21 +0200 Subject: [PATCH 225/613] debt - avoid process dependency in common --- src/vs/base/parts/ipc/common/ipc.net.ts | 9 ++------- src/vs/base/test/common/path.test.ts | 1 + .../contrib/search/test/common/queryBuilder.test.ts | 3 ++- .../contrib/search/test/common/searchModel.test.ts | 1 + 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index 459a5b94bd8dd..207fceb65b964 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -8,8 +8,7 @@ import { IMessagePassingProtocol, IPCClient } from 'vs/base/parts/ipc/common/ipc import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; import * as platform from 'vs/base/common/platform'; - -declare var process: any; +import * as process from 'vs/base/common/process'; export interface ISocket extends IDisposable { onData(listener: (e: VSBuffer) => void): IDisposable; @@ -434,11 +433,7 @@ export function createBufferedEvent(source: Event): Event { // it is important to deliver these messages after this call, but before // other messages have a chance to be received (to guarantee in order delivery) // that's why we're using here nextTick and not other types of timeouts - if (typeof process !== 'undefined') { - process.nextTick(deliverMessages); - } else { - platform.setImmediate(deliverMessages); - } + process.nextTick(deliverMessages); }, onLastListenerRemove: () => { hasListeners = false; diff --git a/src/vs/base/test/common/path.test.ts b/src/vs/base/test/common/path.test.ts index cfceaedc9e9cb..f5230c4a35c73 100644 --- a/src/vs/base/test/common/path.test.ts +++ b/src/vs/base/test/common/path.test.ts @@ -30,6 +30,7 @@ import * as assert from 'assert'; import * as path from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; +import * as process from 'vs/base/common/process'; suite('Paths (Node Implementation)', () => { test('join', () => { diff --git a/src/vs/workbench/contrib/search/test/common/queryBuilder.test.ts b/src/vs/workbench/contrib/search/test/common/queryBuilder.test.ts index 0c14fd010f700..ed8fd45887718 100644 --- a/src/vs/workbench/contrib/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/contrib/search/test/common/queryBuilder.test.ts @@ -14,6 +14,7 @@ import { IFolderQuery, IPatternInfo, QueryType, ITextQuery, IFileQuery } from 'v import { IWorkspaceContextService, toWorkspaceFolder, Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { ISearchPathsInfo, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; import { TestContextService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices'; +import { isWindows } from 'vs/base/common/platform'; const DEFAULT_EDITOR_CONFIG = {}; const DEFAULT_USER_CONFIG = { useRipgrep: true, useIgnoreFiles: true, useGlobalIgnoreFiles: true }; @@ -1032,7 +1033,7 @@ function getUri(...slashPathParts: string[]): uri { } function fixPath(...slashPathParts: string[]): string { - if (process.platform === 'win32' && slashPathParts.length && !slashPathParts[0].match(/^c:/i)) { + if (isWindows && slashPathParts.length && !slashPathParts[0].match(/^c:/i)) { slashPathParts.unshift('c:'); } diff --git a/src/vs/workbench/contrib/search/test/common/searchModel.test.ts b/src/vs/workbench/contrib/search/test/common/searchModel.test.ts index ba00d3b20a13f..7ad9e679d1f2b 100644 --- a/src/vs/workbench/contrib/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/common/searchModel.test.ts @@ -18,6 +18,7 @@ import { IFileMatch, IFileSearchStats, IFolderQuery, ISearchComplete, ISearchPro import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { SearchModel } from 'vs/workbench/contrib/search/common/searchModel'; +import * as process from 'vs/base/common/process'; const nullEvent = new class { id: number; From 985c775b38a7a29907e050ab37e5daef8e2e87eb Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 15 Aug 2019 15:23:35 +0200 Subject: [PATCH 226/613] rawDebugSession: do not use process #79210 --- src/vs/workbench/contrib/debug/browser/rawDebugSession.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index 39bb171c4ecc0..0d1772ff111dd 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -16,6 +16,7 @@ import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { URI } from 'vs/base/common/uri'; import { IProcessEnvironment } from 'vs/base/common/platform'; +import { env as processEnv } from 'vs/base/common/process'; /** * This interface represents a single command line argument split into a "prefix" and a "path" half. @@ -594,10 +595,7 @@ export class RawDebugSession { let env: IProcessEnvironment = {}; if (vscodeArgs.env) { // merge environment variables into a copy of the process.env - if (typeof process === 'object' && process.env) { - env = objects.mixin(env, process.env); - } - env = objects.mixin(env, vscodeArgs.env); + env = objects.mixin(processEnv, vscodeArgs.env); // and delete some if necessary Object.keys(env).filter(k => env[k] === null).forEach(key => delete env[key]); } From 21de711cdfb32d5798abbd60fc4ef92e640d23dd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 15 Aug 2019 15:22:45 +0200 Subject: [PATCH 227/613] remove proposed API `vscode.commands.onDidExecuteCommand` --- src/vs/vscode.proposed.d.ts | 16 -------------- .../api/browser/mainThreadCommands.ts | 14 ------------ .../workbench/api/common/extHost.api.impl.ts | 6 +---- .../workbench/api/common/extHost.protocol.ts | 5 +---- .../workbench/api/common/extHostCommands.ts | 22 ++----------------- 5 files changed, 4 insertions(+), 59 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 0b9ad6976fa1a..328fcae58961a 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 diff --git a/src/vs/workbench/api/browser/mainThreadCommands.ts b/src/vs/workbench/api/browser/mainThreadCommands.ts index 25dc11380552e..2f4d043f04875 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/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 0c4b489095d5b..5370f6a01babb 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -223,11 +223,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 259f0d506585f..1cc498a9ca48a 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'; @@ -115,8 +115,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; @@ -735,7 +733,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 1e229fc3dad8d..8111a7687cf94 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 From bc433d65c4a4c7ce776895d924c5d8f8da121fad Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Aug 2019 15:36:57 +0200 Subject: [PATCH 228/613] Fix #79206 --- .../browser/extensionTipsService.ts | 30 +++++++++++++------ .../browser/extensions.contribution.ts | 4 +-- .../browser/extensions.web.contribution.ts | 11 +++++++ .../electron-browser/extensionTipsService.ts | 15 ++++++++++ .../extensions.contribution.ts | 3 ++ src/vs/workbench/workbench.web.main.ts | 3 ++ 6 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts create mode 100644 src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts index 4597b0270e75b..c080f868407b2 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts @@ -23,7 +23,6 @@ import { IExtensionsConfiguration, ConfigurationKey, ShowRecommendationsOnlyOnDe import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { flatten, distinct, shuffle, coalesce } from 'vs/base/common/arrays'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { guessMimeTypes, MIME_UNKNOWN } from 'vs/base/common/mime'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IRequestService, asJson } from 'vs/platform/request/common/request'; @@ -41,8 +40,9 @@ import { extname } from 'vs/base/common/resources'; import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/product'; import { timeout } from 'vs/base/common/async'; import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; -import { Platform, setImmediate } from 'vs/base/common/platform'; +import { Platform, setImmediate, IProcessEnvironment } from 'vs/base/common/platform'; import { platform } from 'vs/base/common/process'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; const milliSecondsInADay = 1000 * 60 * 60 * 24; const choiceNever = localize('neverShowAgain', "Don't Show Again"); @@ -66,7 +66,7 @@ function caseInsensitiveGet(obj: { [key: string]: T }, key: string): T | unde return undefined; } -export class ExtensionTipsService extends Disposable implements IExtensionTipsService { +export abstract class BaseExtensionTipsService extends Disposable implements IExtensionTipsService { _serviceBrand: any; @@ -99,7 +99,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IConfigurationService private readonly configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IExtensionService private readonly extensionService: IExtensionService, @IRequestService private readonly requestService: IRequestService, @IViewletService private readonly viewletService: IViewletService, @@ -1020,11 +1020,12 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (!windowsPath || typeof windowsPath !== 'string') { return; } - windowsPath = windowsPath.replace('%USERPROFILE%', process.env['USERPROFILE']!) - .replace('%ProgramFiles(x86)%', process.env['ProgramFiles(x86)']!) - .replace('%ProgramFiles%', process.env['ProgramFiles']!) - .replace('%APPDATA%', process.env['APPDATA']!) - .replace('%WINDIR%', process.env['WINDIR']!); + const processEnv = this.getProcessEnvironment(); + windowsPath = windowsPath.replace('%USERPROFILE%', processEnv['USERPROFILE']!) + .replace('%ProgramFiles(x86)%', processEnv['ProgramFiles(x86)']!) + .replace('%ProgramFiles%', processEnv['ProgramFiles']!) + .replace('%APPDATA%', processEnv['APPDATA']!) + .replace('%WINDIR%', processEnv['WINDIR']!); promises.push(findExecutable(exeName, entry.value, windowsPath)); } else { promises.push(findExecutable(exeName, entry.value, join('/usr/local/bin', exeName))); @@ -1144,4 +1145,15 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe private isExtensionAllowedToBeRecommended(id: string): boolean { return this._allIgnoredRecommendations.indexOf(id.toLowerCase()) === -1; } + + protected abstract getProcessEnvironment(): IProcessEnvironment; +} + + +export class ExtensionTipsService extends BaseExtensionTipsService implements IExtensionTipsService { + + protected getProcessEnvironment(): IProcessEnvironment { + return {}; + } + } diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 8dfa256fcf631..32bd703fba030 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionsLabel, ExtensionsChannelId, PreferencesLabel, IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/contrib/output/common/output'; @@ -45,10 +45,8 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { RemoteExtensionsInstaller } from 'vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; // Singletons -registerSingleton(IExtensionTipsService, ExtensionTipsService); registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); Registry.as(OutputExtensions.OutputChannels) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts new file mode 100644 index 0000000000000..439f218cfb3d9 --- /dev/null +++ b/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; + +// Singletons +registerSingleton(IExtensionTipsService, ExtensionTipsService); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts new file mode 100644 index 0000000000000..f866ccbb7a828 --- /dev/null +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; +import { IProcessEnvironment } from 'vs/base/common/platform'; + +export class ExtensionTipsService extends BaseExtensionTipsService { + + protected getProcessEnvironment(): IProcessEnvironment { + return process.env as IProcessEnvironment; + } + +} diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts index 4c21e1e91815e..74f15f54393e8 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -21,8 +21,11 @@ import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron import { URI } from 'vs/base/common/uri'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler'; +import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/electron-browser/extensionTipsService'; // Singletons +registerSingleton(IExtensionTipsService, ExtensionTipsService); registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 40384b249b094..0c80d322d2aca 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -87,6 +87,9 @@ import 'vs/workbench/contrib/debug/browser/extensionHostDebugService'; import 'vs/workbench/contrib/webview/browser/webviewService'; import 'vs/workbench/contrib/webview/browser/webviewEditorService'; +// Extensions Management +import 'vs/workbench/contrib/extensions/browser/extensions.web.contribution'; + // Terminal import 'vs/workbench/contrib/terminal/browser/terminalNativeService'; import 'vs/workbench/contrib/terminal/browser/terminalInstanceService'; From 998a6d7cf965800bc1c298f3714b63d3a9806413 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 15 Aug 2019 15:48:52 +0200 Subject: [PATCH 229/613] callStack view: do not show thread when there is only one to be compact fixes #79121 --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 97836b9a96410..bc5758380cd1f 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -608,6 +608,10 @@ class CallStackDataSource implements IAsyncDataSource s.parentSession === element); const threads: CallStackItem[] = element.getAllThreads(); + if (threads.length === 1 && childSessions.length === 0) { + // Do not show thread when there is only one to be compact. + return this.getThreadChildren(threads[0]); + } return Promise.resolve(threads.concat(childSessions)); } else { From 926e27cb4f60a08d4b18d4f27d0de06df0853119 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 15 Aug 2019 16:04:50 +0200 Subject: [PATCH 230/613] fix compile error --- .../src/singlefolder-tests/commands.test.ts | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts index 9ea76e6a1193b..7dba2cfa93c30 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/commands.test.ts @@ -113,31 +113,4 @@ suite('commands namespace tests', () => { return Promise.all([a, b, c, d]); }); - - test('onDidExecuteCommand', async function () { - let args: any[]; - let d1 = commands.registerCommand('t1', function () { - args = [...arguments]; - }); - - - const p = new Promise((resolve, reject) => { - - let d2 = commands.onDidExecuteCommand(event => { - d2.dispose(); - d1.dispose(); - - try { - assert.equal(event.command, 't1'); - assert.deepEqual(args, event.arguments); - resolve(); - } catch (e) { - reject(e); - } - }); - }); - - await commands.executeCommand('t1', { foo: 1 }); - await p; - }); }); From 8095541d7d617e3071b397598530756e9fce8544 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Aug 2019 16:03:41 +0200 Subject: [PATCH 231/613] inline product configuration in produt service --- .../sharedProcess/sharedProcessMain.ts | 3 +-- src/vs/code/node/cliProcessMain.ts | 3 +-- .../common/extensionGalleryService.ts | 14 +++++----- .../product/browser/productService.ts | 26 ------------------- src/vs/platform/product/common/product.ts | 9 +++---- src/vs/platform/product/node/product.ts | 3 +++ .../platform/product/node/productService.ts | 23 ---------------- .../api/browser/mainThreadWebview.ts | 2 +- src/vs/workbench/browser/web.main.ts | 17 +++++++++--- .../workbench/browser/web.simpleservices.ts | 8 +++--- .../contrib/debug/browser/debugSession.ts | 2 +- .../experiments/common/experimentService.ts | 4 +-- .../browser/extensionTipsService.ts | 26 +++++++++---------- .../extensions/browser/extensionsActions.ts | 4 +-- .../browser/extensionsWorkbenchService.ts | 8 +++--- .../contrib/feedback/browser/feedback.ts | 4 +-- .../feedback/browser/feedbackStatusbarItem.ts | 2 +- .../preferences/browser/preferencesSearch.ts | 4 +-- .../browser/terminalProcessManager.ts | 2 +- .../electron-browser/desktop.main.ts | 5 ++++ .../extensionEnablementService.test.ts | 4 +-- .../browser/webWorkerExtensionHostStarter.ts | 8 +++--- .../common/abstractExtensionService.ts | 6 ++--- .../extensions/common/extensionsUtil.ts | 2 +- .../common/remoteExtensionHostClient.ts | 10 +++---- .../remoteExtensionManagementIpc.ts | 2 +- .../remote/browser/remoteAgentServiceImpl.ts | 2 +- .../telemetry/browser/telemetryService.ts | 6 ++--- .../electron-browser/telemetryService.ts | 4 +-- .../workbench/test/workbenchTestServices.ts | 4 +++ src/vs/workbench/workbench.desktop.main.ts | 3 --- 31 files changed, 94 insertions(+), 126 deletions(-) delete mode 100644 src/vs/platform/product/browser/productService.ts delete mode 100644 src/vs/platform/product/node/productService.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 8ef5cc8dd0d3f..c5514f4eff7ca 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -55,7 +55,6 @@ import { IFileService } from 'vs/platform/files/common/files'; import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/product'; -import { ProductService } from 'vs/platform/product/node/productService'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -111,10 +110,10 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat await configurationService.initialize(); services.set(IEnvironmentService, environmentService); + services.set(IProductService, { _serviceBrand: undefined, ...product }); services.set(ILogService, logService); services.set(IConfigurationService, configurationService); services.set(IRequestService, new SyncDescriptor(RequestService)); - services.set(IProductService, new SyncDescriptor(ProductService)); const mainProcessService = new MainProcessService(server, mainRouter); services.set(IMainProcessService, mainProcessService); diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index d9ed94ff68e50..32005dd153ea0 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -47,7 +47,6 @@ import { IFileService } from 'vs/platform/files/common/files'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IProductService } from 'vs/platform/product/common/product'; -import { ProductService } from 'vs/platform/product/node/productService'; const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id); const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id); @@ -325,7 +324,7 @@ export async function main(argv: ParsedArgs): Promise { services.set(ILogService, logService); services.set(IConfigurationService, configurationService); services.set(IStateService, new SyncDescriptor(StateService)); - services.set(IProductService, new SyncDescriptor(ProductService)); + services.set(IProductService, { _serviceBrand: undefined, ...product }); // Files const fileService = new FileService(logService); diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 42fbff47fe24f..2d39c51297528 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -342,10 +342,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 { @@ -358,7 +358,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { getCompatibleExtension(arg1: IExtensionIdentifier | IGalleryExtension, version?: string): Promise { const extension: IGalleryExtension | null = isIExtensionIdentifier(arg1) ? null : arg1; - if (extension && extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.productConfiguration.version)) { + if (extension && extension.properties.engine && isEngineValid(extension.properties.engine, this.productService.version)) { return Promise.resolve(extension); } const { id, uuid } = extension ? extension.identifier : arg1; @@ -384,7 +384,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; } } @@ -619,7 +619,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 }))); @@ -705,7 +705,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); } } @@ -737,7 +737,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/product/browser/productService.ts b/src/vs/platform/product/browser/productService.ts deleted file mode 100644 index fae6e1d5fa7c0..0000000000000 --- a/src/vs/platform/product/browser/productService.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. 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 } - }; - } - -} diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index ec42094d94cd7..3797e18a25ea4 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -3,19 +3,18 @@ * Licensed under the MIT License. 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; diff --git a/src/vs/platform/product/node/product.ts b/src/vs/platform/product/node/product.ts index 08610d71a1a2b..3d13f26c57605 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 f3986577355c8..0000000000000 --- a/src/vs/platform/product/node/productService.ts +++ /dev/null @@ -1,23 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. 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 } - }; - } - -} diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index f2c3141d06399..90bdeb5ea748a 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/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 812663e25d43c..c28e693fc47cb 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -14,7 +14,7 @@ import { Workbench } from 'vs/workbench/browser/workbench'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteExtensionsFileSystemProvider } from 'vs/platform/remote/common/remoteAgentFileSystemChannel'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService, IProductConfiguration } from 'vs/platform/product/common/product'; import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteAgentServiceImpl'; import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; @@ -33,7 +33,6 @@ import { ISignService } from 'vs/platform/sign/common/sign'; import { SignService } from 'vs/platform/sign/browser/signService'; import { hash } from 'vs/base/common/hash'; import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; -import { ProductService } from 'vs/platform/product/browser/productService'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { BACKUPS } from 'vs/platform/environment/common/environment'; import { joinPath } from 'vs/base/common/resources'; @@ -121,7 +120,7 @@ class CodeRendererMain extends Disposable { serviceCollection.set(IWorkbenchEnvironmentService, environmentService); // Product - const productService = new ProductService(); + const productService = this.createProductService(); serviceCollection.set(IProductService, productService); // Remote @@ -187,6 +186,18 @@ class CodeRendererMain extends Disposable { return { serviceCollection, logService, storageService: services[1] }; } + private createProductService(): IProductService { + const element = document.getElementById('vscode-remote-product-configuration'); + const productConfiguration: IProductConfiguration = { + ...element ? JSON.parse(element.getAttribute('data-settings')!) : { + version: '1.38.0-unknown', + nameLong: 'Unknown', + extensionAllowedProposedApi: [], + }, ...{ urlProtocol: '', enableTelemetry: false } + }; + return { _serviceBrand: undefined, ...productConfiguration }; + } + private async createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: IFileService, logService: ILogService): Promise { const storageService = new BrowserStorageService(environmentService, fileService); diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index ce9a691a28b67..ce24f3df7bf3b 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -743,13 +743,13 @@ export class SimpleWindowsService implements IWindowsService { async openAboutDialog(): Promise { const detail = localize('aboutDetail', "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", - this.productService.productConfiguration.version || 'Unknown', - this.productService.productConfiguration.commit || 'Unknown', - this.productService.productConfiguration.date || 'Unknown', + this.productService.version || 'Unknown', + this.productService.commit || 'Unknown', + this.productService.date || 'Unknown', navigator.userAgent ); - const result = await this.dialogService.show(Severity.Info, this.productService.productConfiguration.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail }); + const result = await this.dialogService.show(Severity.Info, this.productService.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail }); if (result === 0) { this.clipboardService.writeText(detail); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 6fdc99451a6b8..d454894401968 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -175,7 +175,7 @@ export class DebugSession implements IDebugSession { return this.raw!.initialize({ clientID: 'vscode', - clientName: this.productService.productConfiguration.nameLong, + clientName: this.productService.nameLong, adapterID: this.configuration.type, pathFormat: 'path', linesStartAt1: true, diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index bef55ac4b26b8..5707377ce4b1b 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -170,10 +170,10 @@ export class ExperimentService extends Disposable implements IExperimentService } protected getExperiments(): Promise { - if (!this.productService.productConfiguration.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) { + if (!this.productService.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) { return Promise.resolve([]); } - return this.requestService.request({ type: 'GET', url: this.productService.productConfiguration.experimentsUrl }, CancellationToken.None).then(context => { + return this.requestService.request({ type: 'GET', url: this.productService.experimentsUrl }, CancellationToken.None).then(context => { if (context.res.statusCode !== 200) { return Promise.resolve(null); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts index c080f868407b2..12a05302a752b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts @@ -116,8 +116,8 @@ export abstract class BaseExtensionTipsService extends Disposable implements IEx return; } - if (this.productService.productConfiguration.extensionsGallery && this.productService.productConfiguration.extensionsGallery.recommendationsUrl) { - this._extensionsRecommendationsUrl = this.productService.productConfiguration.extensionsGallery.recommendationsUrl; + if (this.productService.extensionsGallery && this.productService.extensionsGallery.recommendationsUrl) { + this._extensionsRecommendationsUrl = this.productService.extensionsGallery.recommendationsUrl; } this.sessionSeed = +new Date(); @@ -243,7 +243,7 @@ export abstract class BaseExtensionTipsService extends Disposable implements IEx } getKeymapRecommendations(): IExtensionRecommendation[] { - return (this.productService.productConfiguration.keymapExtensionTips || []) + return (this.productService.keymapExtensionTips || []) .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)) .map(extensionId => ({ extensionId, sources: ['application'] })); } @@ -600,10 +600,10 @@ export abstract class BaseExtensionTipsService extends Disposable implements IEx return Object.keys(this._fileBasedRecommendations) .sort((a, b) => { if (this._fileBasedRecommendations[a].recommendedTime === this._fileBasedRecommendations[b].recommendedTime) { - if (!this.productService.productConfiguration.extensionImportantTips || caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, a)) { + if (!this.productService.extensionImportantTips || caseInsensitiveGet(this.productService.extensionImportantTips, a)) { return -1; } - if (caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, b)) { + if (caseInsensitiveGet(this.productService.extensionImportantTips, b)) { return 1; } } @@ -614,11 +614,11 @@ export abstract class BaseExtensionTipsService extends Disposable implements IEx } /** - * Parse all file based recommendations from this.productService.productConfiguration.extensionTips - * Retire existing recommendations if they are older than a week or are not part of this.productService.productConfiguration.extensionTips anymore + * Parse all file based recommendations from this.productService.extensionTips + * Retire existing recommendations if they are older than a week or are not part of this.productService.extensionTips anymore */ private fetchFileBasedRecommendations() { - const extensionTips = this.productService.productConfiguration.extensionTips; + const extensionTips = this.productService.extensionTips; if (!extensionTips) { return; } @@ -635,7 +635,7 @@ export abstract class BaseExtensionTipsService extends Disposable implements IEx } }); - forEach(this.productService.productConfiguration.extensionImportantTips, entry => { + forEach(this.productService.extensionImportantTips, entry => { let { key: id, value } = entry; const { pattern } = value; let ids = this._availableRecommendations[pattern]; @@ -697,7 +697,7 @@ export abstract class BaseExtensionTipsService extends Disposable implements IEx let { key: pattern, value: ids } = entry; if (match(pattern, model.uri.toString())) { for (let id of ids) { - if (caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, id)) { + if (caseInsensitiveGet(this.productService.extensionImportantTips, id)) { recommendationsToSuggest.push(id); } const filedBasedRecommendation = this._fileBasedRecommendations[id.toLowerCase()] || { recommendedTime: now, sources: [] }; @@ -751,7 +751,7 @@ export abstract class BaseExtensionTipsService extends Disposable implements IEx } const id = recommendationsToSuggest[0]; - const entry = caseInsensitiveGet(this.productService.productConfiguration.extensionImportantTips, id); + const entry = caseInsensitiveGet(this.productService.extensionImportantTips, id); if (!entry) { return false; } @@ -981,7 +981,7 @@ export abstract class BaseExtensionTipsService extends Disposable implements IEx } /** - * If user has any of the tools listed in this.productService.productConfiguration.exeBasedExtensionTips, fetch corresponding recommendations + * If user has any of the tools listed in this.productService.exeBasedExtensionTips, fetch corresponding recommendations */ private async fetchExecutableRecommendations(important: boolean): Promise { if (Platform.Web) { @@ -1007,7 +1007,7 @@ export abstract class BaseExtensionTipsService extends Disposable implements IEx const promises: Promise[] = []; // Loop through recommended extensions - forEach(this.productService.productConfiguration.exeBasedExtensionTips, entry => { + forEach(this.productService.exeBasedExtensionTips, entry => { if (typeof entry.value !== 'object' || !Array.isArray(entry.value['recommendations'])) { return; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 05bef903d9e00..ec19e774ab1f6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -76,10 +76,10 @@ export function toExtensionDescription(local: ILocalExtension): IExtensionDescri const promptDownloadManually = (extension: IGalleryExtension | undefined, message: string, error: Error, instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService, productService: IProductService) => { - if (!extension || error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS || !productService.productConfiguration.extensionsGallery) { + if (!extension || error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS || !productService.extensionsGallery) { return Promise.reject(error); } else { - const downloadUrl = `${productService.productConfiguration.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; + const downloadUrl = `${productService.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; notificationService.prompt(Severity.Error, message, [{ label: localize('download', "Download Manually"), run: () => openerService.open(URI.parse(downloadUrl)).then(() => { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 99ec538b4426e..b64689e02c6c9 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -114,11 +114,11 @@ class Extension implements IExtension { } get url(): string | undefined { - if (!this.productService.productConfiguration.extensionsGallery || !this.gallery) { + if (!this.productService.extensionsGallery || !this.gallery) { return undefined; } - return `${this.productService.productConfiguration.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`; + return `${this.productService.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`; } get iconUrl(): string { @@ -615,7 +615,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension text = text.replace(extensionRegex, (m, ext) => { // Get curated keywords - const lookup = this.productService.productConfiguration.extensionKeywords || {}; + const lookup = this.productService.extensionKeywords || {}; const keywords = lookup[ext] || []; // Get mode name @@ -1022,7 +1022,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension get allowedBadgeProviders(): string[] { if (!this._extensionAllowedBadgeProviders) { - this._extensionAllowedBadgeProviders = (this.productService.productConfiguration.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase()); + this._extensionAllowedBadgeProviders = (this.productService.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase()); } return this._extensionAllowedBadgeProviders; } diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.ts b/src/vs/workbench/contrib/feedback/browser/feedback.ts index 130c7fd3a0441..618dc7edaa9d5 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.ts @@ -73,8 +73,8 @@ export class FeedbackDropdown extends Dropdown { this.feedbackDelegate = options.feedbackService; this.maxFeedbackCharacters = this.feedbackDelegate.getCharacterLimit(this.sentiment); - if (productService.productConfiguration.sendASmile) { - this.requestFeatureLink = productService.productConfiguration.sendASmile.requestFeatureUrl; + if (productService.sendASmile) { + this.requestFeatureLink = productService.sendASmile.requestFeatureUrl; } this.integrityService.isPure().then(result => { diff --git a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts index 458dc93b0b688..c609aa4132fa3 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts @@ -58,7 +58,7 @@ export class FeedbackStatusbarConribution extends Disposable implements IWorkben ) { super(); - if (productService.productConfiguration.sendASmile) { + if (productService.sendASmile) { this.entry = this._register(statusbarService.addEntry(this.getStatusEntry(), 'status.feedback', localize('status.feedback', "Tweet Feedback"), StatusbarAlignment.RIGHT, -100 /* towards the end of the right hand side */)); CommandsRegistry.registerCommand('_feedback.open', () => this.toggleFeedback()); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index b876c396a7f42..37de5e7766e9f 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -74,7 +74,7 @@ export class PreferencesSearchService extends Disposable implements IPreferences }; } else { return { - urlBase: this.productService.productConfiguration.settingsSearchUrl + urlBase: this.productService.settingsSearchUrl }; } } @@ -365,7 +365,7 @@ class RemoteSearchProvider implements ISearchProvider { const extensions = await this.installedExtensions; const filters = this.options.newExtensionsOnly ? [`diminish eq 'latest'`] : - this.getVersionFilters(extensions, this.productService.productConfiguration.settingsSearchBuildId); + this.getVersionFilters(extensions, this.productService.settingsSearchBuildId); const filterStr = filters .slice(filterPage * RemoteSearchProvider.MAX_REQUEST_FILTERS, (filterPage + 1) * RemoteSearchProvider.MAX_REQUEST_FILTERS) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index b1ecf1026f3c4..b3ff7db5e88e2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -226,7 +226,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(); this._configHelper.showRecommendations(shellLaunchConfig); const baseEnv = this._configHelper.config.inheritEnv ? process.env as platform.IProcessEnvironment : await this._terminalInstanceService.getMainProcessParentEnv(); - const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.productConfiguration.version, this._configHelper.config.setLocaleVariables, baseEnv); + const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.setLocaleVariables, baseEnv); const useConpty = this._configHelper.config.windowsEnableConpty && !isScreenReaderModeEnabled; return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty); diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index 40ea992a2a152..a6836c38075f7 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -51,6 +51,8 @@ import { SignService } from 'vs/platform/sign/node/signService'; import { ISignService } from 'vs/platform/sign/common/sign'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { basename } from 'vs/base/common/resources'; +import { IProductService } from 'vs/platform/product/common/product'; +import product from 'vs/platform/product/node/product'; class CodeRendererMain extends Disposable { @@ -177,6 +179,9 @@ class CodeRendererMain extends Disposable { // Environment serviceCollection.set(IWorkbenchEnvironmentService, this.environmentService); + // Product + serviceCollection.set(IProductService, { _serviceBrand: undefined, ...product }); + // Log const logService = this._register(this.createLogService(mainProcessService, this.environmentService)); serviceCollection.set(ILogService, logService); diff --git a/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index 5852ce2d53ce7..4536a5ad507ab 100644 --- a/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -16,12 +16,12 @@ import { IExtensionContributions, ExtensionType, IExtension } from 'vs/platform/ import { isUndefinedOrNull } from 'vs/base/common/types'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ProductService } from 'vs/platform/product/node/productService'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { assign } from 'vs/base/common/objects'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { productService } from 'vs/workbench/test/workbenchTestServices'; function storageService(instantiationService: TestInstantiationService): IStorageService { let service = instantiationService.get(IStorageService); @@ -46,7 +46,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { instantiationService.get(IExtensionManagementService) || instantiationService.stub(IExtensionManagementService, { onDidInstallExtension: new Emitter().event, onDidUninstallExtension: new Emitter().event } as IExtensionManagementService), instantiationService.get(IConfigurationService), instantiationService.get(IExtensionManagementServerService), - new ProductService() + productService ); } diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index 7848fdee12777..5794b25906167 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -117,15 +117,15 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { const [telemetryInfo, extensionDescriptions] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._extensions]); const workspace = this._contextService.getWorkspace(); return { - commit: this._productService.productConfiguration.commit, - version: this._productService.productConfiguration.version, + commit: this._productService.commit, + version: this._productService.version, parentPid: -1, environment: { isExtensionDevelopmentDebug: false, appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, appSettingsHome: this._environmentService.appSettingsHome ? this._environmentService.appSettingsHome : undefined, - appName: this._productService.productConfiguration.nameLong, - appUriScheme: this._productService.productConfiguration.urlProtocol, + appName: this._productService.nameLong, + appUriScheme: this._productService.urlProtocol, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 51f991375a6a3..6445a03a6e631 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -462,12 +462,12 @@ class ProposedApiController { } this.enableProposedApiForAll = !environmentService.isBuilt || - (!!environmentService.extensionDevelopmentLocationURI && productService.productConfiguration.nameLong !== 'Visual Studio Code') || + (!!environmentService.extensionDevelopmentLocationURI && productService.nameLong !== 'Visual Studio Code') || (this.enableProposedApiFor.length === 0 && 'enable-proposed-api' in environmentService.args); this.productAllowProposedApi = new Set(); - if (isNonEmptyArray(productService.productConfiguration.extensionAllowedProposedApi)) { - productService.productConfiguration.extensionAllowedProposedApi.forEach((id) => this.productAllowProposedApi.add(ExtensionIdentifier.toKey(id))); + if (isNonEmptyArray(productService.extensionAllowedProposedApi)) { + productService.extensionAllowedProposedApi.forEach((id) => this.productAllowProposedApi.add(ExtensionIdentifier.toKey(id))); } } diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts index 2f7d4e01039ac..a1496708db6d1 100644 --- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -24,7 +24,7 @@ export function isUIExtension(manifest: IExtensionManifest, productService: IPro case 'workspace': return false; default: { // Tagged as UI extension in product - if (isNonEmptyArray(productService.productConfiguration.uiExtensions) && productService.productConfiguration.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { + if (isNonEmptyArray(productService.uiExtensions) && productService.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { return true; } // Not an UI extension if it has main diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts index ca657398990c7..47aaf4a22bb3b 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts @@ -71,7 +71,7 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH public start(): Promise { const options: IConnectionOptions = { - commit: this._productService.productConfiguration.commit, + commit: this._productService.commit, socketFactory: this._socketFactory, addressProvider: { getAddress: async () => { @@ -181,15 +181,15 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH const hostExtensions = allExtensions.filter(extension => extension.main && extension.api === 'none').map(extension => extension.identifier); const workspace = this._contextService.getWorkspace(); const r: IInitData = { - commit: this._productService.productConfiguration.commit, - version: this._productService.productConfiguration.version, + commit: this._productService.commit, + version: this._productService.version, parentPid: remoteExtensionHostData.pid, environment: { isExtensionDevelopmentDebug, appRoot: remoteExtensionHostData.appRoot, appSettingsHome: remoteExtensionHostData.appSettingsHome, - appName: this._productService.productConfiguration.nameLong, - appUriScheme: this._productService.productConfiguration.urlProtocol, + appName: this._productService.nameLong, + appUriScheme: this._productService.urlProtocol, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, diff --git a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts index 75787071f8516..f94d89adb16b2 100644 --- a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts +++ b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts @@ -69,7 +69,7 @@ export class RemoteExtensionManagementChannelClient extends ExtensionManagementC const installed = await this.getInstalled(ExtensionType.User); const compatible = await this.galleryService.getCompatibleExtension(extension); if (!compatible) { - return Promise.reject(new Error(localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extension.identifier.id, this.productService.productConfiguration.version))); + return Promise.reject(new Error(localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extension.identifier.id, this.productService.version))); } const manifest = await this.galleryService.getManifest(compatible, CancellationToken.None); if (manifest) { diff --git a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts index db4d2038ca3e0..fd979ca094c62 100644 --- a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts @@ -28,7 +28,7 @@ export class RemoteAgentService extends AbstractRemoteAgentService implements IR super(environmentService); this.socketFactory = new BrowserSocketFactory(webSocketFactory); - this._connection = this._register(new RemoteAgentConnection(environmentService.configuration.remoteAuthority!, productService.productConfiguration.commit, this.socketFactory, remoteAuthorityResolverService, signService)); + this._connection = this._register(new RemoteAgentConnection(environmentService.configuration.remoteAuthority!, productService.commit, this.socketFactory, remoteAuthorityResolverService, signService)); } getConnection(): IRemoteAgentConnection | null { diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index 2632d935d80e4..eac9dcfb1d0a7 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -79,11 +79,11 @@ export class TelemetryService extends Disposable implements ITelemetryService { ) { super(); - const aiKey = productService.productConfiguration.aiConfig && productService.productConfiguration.aiConfig.asimovKey; - if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.productConfiguration.enableTelemetry && !!aiKey) { + const aiKey = productService.aiConfig && productService.aiConfig.asimovKey; + if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.enableTelemetry && !!aiKey) { const config: ITelemetryServiceConfig = { appender: combinedAppender(new WebTelemetryAppender(aiKey, logService), new LogAppender(logService)), - commonProperties: resolveWorkbenchCommonProperties(storageService, productService.productConfiguration.commit, productService.productConfiguration.version, environmentService.configuration.machineId, environmentService.configuration.remoteAuthority), + commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.configuration.remoteAuthority), piiPaths: [environmentService.appRoot] }; diff --git a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts index 002b6ae4ce369..c5561e4429580 100644 --- a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts @@ -34,11 +34,11 @@ export class TelemetryService extends Disposable implements ITelemetryService { ) { super(); - if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.productConfiguration.enableTelemetry) { + if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.enableTelemetry) { const channel = sharedProcessService.getChannel('telemetryAppender'); const config: ITelemetryServiceConfig = { appender: combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)), - commonProperties: resolveWorkbenchCommonProperties(storageService, productService.productConfiguration.commit, productService.productConfiguration.version, environmentService.configuration.machineId, environmentService.installSourcePath, environmentService.configuration.remoteAuthority), + commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.installSourcePath, environmentService.configuration.remoteAuthority), piiPaths: environmentService.extensionsPath ? [environmentService.appRoot, environmentService.extensionsPath] : [environmentService.appRoot] }; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 0ca22ba62b88d..576df9cb63c53 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -84,6 +84,8 @@ import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/n import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer'; import { NodeTextFileService } from 'vs/workbench/services/textfile/node/textFileService'; import { Schemas } from 'vs/base/common/network'; +import { IProductService } from 'vs/platform/product/common/product'; +import product from 'vs/platform/product/node/product'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined); @@ -1631,3 +1633,5 @@ export class RemoteFileSystemProvider implements IFileSystemProvider { private toFileResource(resource: URI): URI { return resource.with({ scheme: Schemas.file, authority: '' }); } } + +export const productService: IProductService = { _serviceBrand: undefined, ...product }; diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 3373be0bd3508..90f3e4c63d142 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -61,8 +61,6 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { LocalizationsService } from 'vs/platform/localizations/electron-browser/localizationsService'; import { ISharedProcessService, SharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { IProductService } from 'vs/platform/product/common/product'; -import { ProductService } from 'vs/platform/product/node/productService'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { WindowsService } from 'vs/platform/windows/electron-browser/windowsService'; import { IUpdateService } from 'vs/platform/update/common/update'; @@ -81,7 +79,6 @@ registerSingleton(IRequestService, RequestService, true); registerSingleton(ILifecycleService, LifecycleService); registerSingleton(ILocalizationsService, LocalizationsService); registerSingleton(ISharedProcessService, SharedProcessService, true); -registerSingleton(IProductService, ProductService, true); registerSingleton(IWindowsService, WindowsService); registerSingleton(IUpdateService, UpdateService); registerSingleton(IIssueService, IssueService); From 28c375c701ffb85c8d3cc617165e2aa8bf55f395 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Aug 2019 16:14:54 +0200 Subject: [PATCH 232/613] update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e5af8e0f35165..09b59b17c095f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "c2664514acafa174a6b0b5a960931699641d6989", + "distro": "a8a3be6f7445a6f3ce736a0ba3cbca717a2372cb", "author": { "name": "Microsoft Corporation" }, @@ -158,4 +158,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} +} \ No newline at end of file From 221a6ca43c6230180be895c9b9382fca0ea7ac25 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 07:15:59 -0700 Subject: [PATCH 233/613] Fix strict error --- test/smoke/src/vscode/puppeteerDriver.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index ba2c8359de12e..2e2f2280dd6f4 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -6,7 +6,6 @@ import * as puppeteer from 'puppeteer'; import { ChildProcess, spawn } from 'child_process'; import { join } from 'path'; -import { tmpdir } from 'os'; import { mkdir } from 'fs'; import { promisify } from 'util'; @@ -226,7 +225,7 @@ export function connect(headless: boolean, outPath: string, handle: string): Pro const page = (await browser.pages())[0]; await page.setViewport({ width, height }); const endpointSplit = endpoint!.split('#'); - await page.goto(`${endpointSplit[0]}?folder=${args[1]}#${endpointSplit[1]}`); + await page.goto(`${endpointSplit[0]}?folder=${args![1]}#${endpointSplit[1]}`); const result = { client: { dispose: () => teardown }, driver: buildDriver(browser, page) From 790cb4e284d0e3d29170f80c780c5c4fad9eaf07 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Aug 2019 16:19:48 +0200 Subject: [PATCH 234/613] fix tests --- .../extensionsTipsService.test.ts | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts index 941a3b69a460a..83231f515ea17 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -22,7 +22,7 @@ import { Emitter } from 'vs/base/common/event'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { TestContextService, TestLifecycleService, TestSharedProcessService } from 'vs/workbench/test/workbenchTestServices'; +import { TestContextService, TestLifecycleService, TestSharedProcessService, productService } from 'vs/workbench/test/workbenchTestServices'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { URI } from 'vs/base/common/uri'; @@ -36,7 +36,6 @@ import { ConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensi import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test'; import { IURLService } from 'vs/platform/url/common/url'; -import product from 'vs/platform/product/node/product'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; @@ -202,23 +201,22 @@ suite('ExtensionsTipsService Test', () => { instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IURLService, URLService); - instantiationService.stub(IProductService, >{ - productConfiguration: { - ...product, ...{ - extensionTips: { - 'ms-vscode.csharp': '{**/*.cs,**/project.json,**/global.json,**/*.csproj,**/*.sln,**/appsettings.json}', - 'msjsdiag.debugger-for-chrome': '{**/*.ts,**/*.tsx**/*.js,**/*.jsx,**/*.es6,**/.babelrc}', - 'lukehoban.Go': '**/*.go' + instantiationService.set(IProductService, { + ...productService, + ...{ + extensionTips: { + 'ms-vscode.csharp': '{**/*.cs,**/project.json,**/global.json,**/*.csproj,**/*.sln,**/appsettings.json}', + 'msjsdiag.debugger-for-chrome': '{**/*.ts,**/*.tsx**/*.js,**/*.jsx,**/*.es6,**/.babelrc}', + 'lukehoban.Go': '**/*.go' + }, + extensionImportantTips: { + 'ms-python.python': { + 'name': 'Python', + 'pattern': '{**/*.py}' }, - extensionImportantTips: { - 'ms-python.python': { - 'name': 'Python', - 'pattern': '{**/*.py}' - }, - 'ms-vscode.PowerShell': { - 'name': 'PowerShell', - 'pattern': '{**/*.ps,**/*.ps1}' - } + 'ms-vscode.PowerShell': { + 'name': 'PowerShell', + 'pattern': '{**/*.ps,**/*.ps1}' } } } From 40921e097e5bfd61ea9a489db837c0548c95f144 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 15 Aug 2019 16:19:53 +0200 Subject: [PATCH 235/613] Do not expand session tree node when selecting it fixes #79184 --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index bc5758380cd1f..4786ae44870e8 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -144,7 +144,8 @@ export class CallStackView extends ViewletPanel { return nls.localize('showMoreStackFrames2', "Show More Stack Frames"); } - } + }, + expandOnlyOnTwistieClick: true }); this.tree.setInput(this.debugService.getModel()).then(undefined, onUnexpectedError); From 0e56130bdf2a4cb3f79f9ec4a52e480b4937149f Mon Sep 17 00:00:00 2001 From: Gabriel DeBacker <6741868+GabeDeBacker@users.noreply.github.com> Date: Thu, 15 Aug 2019 07:24:53 -0700 Subject: [PATCH 236/613] Fix issue with CustomExecutions not working through tasks.executeTask (#79132) * Do not clear out the map, and track the provided execution during execute task * Clear provided custom executions map on execution complete * Save last custom execution --- src/vs/workbench/api/node/extHostTask.ts | 25 +++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 647a2be3f08db..cf9b522ef1113 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -444,6 +444,14 @@ export class ExtHostTask implements ExtHostTaskShape { if (dto === undefined) { return Promise.reject(new Error('Task is not valid')); } + + // If this task is a custom execution, then we need to save it away + // in the provided custom execution map that is cleaned up after the + // task is executed. + if (CustomExecution2DTO.is(dto.execution)) { + await this.addCustomExecution2(dto, task); + } + return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task)); } } @@ -529,11 +537,6 @@ export class ExtHostTask implements ExtHostTaskShape { return Promise.reject(new Error('no handler found')); } - // For custom execution tasks, we need to store the execution objects locally - // since we obviously cannot send callback functions through the proxy. - // So, clear out any existing ones. - this._providedCustomExecutions2.clear(); - // Set up a list of task ID promises that we can wait on // before returning the provided tasks. The ensures that // our task IDs are calculated for any custom execution tasks. @@ -692,5 +695,17 @@ export class ExtHostTask implements ExtHostTaskShape { if (extensionCallback2) { this._activeCustomExecutions2.delete(execution.id); } + + const lastCustomExecution = this._providedCustomExecutions2.get(execution.id); + // Technically we don't really need to do this, however, if an extension + // is executing a task through "executeTask" over and over again + // with different properties in the task definition, then this list + // could grow indefinitely, something we don't want. + this._providedCustomExecutions2.clear(); + // We do still need to hang on to the last custom execution so that the + // Rerun Task command doesn't choke when it tries to rerun a custom execution + if (lastCustomExecution) { + this._providedCustomExecutions2.set(execution.id, lastCustomExecution); + } } } From b303e7acb5be641f0d7440c327be198436327061 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 15 Aug 2019 16:25:46 +0200 Subject: [PATCH 237/613] use safeprocess env --- src/vs/base/common/process.ts | 4 ++-- .../extensions/browser/extensionTipsService.ts | 17 +++-------------- .../browser/extensions.contribution.ts | 4 +++- .../electron-browser/extensionTipsService.ts | 15 --------------- .../electron-browser/extensions.contribution.ts | 3 --- src/vs/workbench/workbench.web.main.ts | 3 --- 6 files changed, 8 insertions(+), 38 deletions(-) delete mode 100644 src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts diff --git a/src/vs/base/common/process.ts b/src/vs/base/common/process.ts index a8447d58eea2c..7b9edd30963f5 100644 --- a/src/vs/base/common/process.ts +++ b/src/vs/base/common/process.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isWindows, isMacintosh, setImmediate } from 'vs/base/common/platform'; +import { isWindows, isMacintosh, setImmediate, IProcessEnvironment } from 'vs/base/common/platform'; interface IProcess { platform: string; - env: object; + env: IProcessEnvironment; cwd(): string; nextTick(callback: (...args: any[]) => void): number; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts index 12a05302a752b..ec9b7e82c0fbb 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts @@ -40,8 +40,8 @@ import { extname } from 'vs/base/common/resources'; import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/product'; import { timeout } from 'vs/base/common/async'; import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; -import { Platform, setImmediate, IProcessEnvironment } from 'vs/base/common/platform'; -import { platform } from 'vs/base/common/process'; +import { Platform, setImmediate } from 'vs/base/common/platform'; +import { platform, env as processEnv } from 'vs/base/common/process'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; const milliSecondsInADay = 1000 * 60 * 60 * 24; @@ -66,7 +66,7 @@ function caseInsensitiveGet(obj: { [key: string]: T }, key: string): T | unde return undefined; } -export abstract class BaseExtensionTipsService extends Disposable implements IExtensionTipsService { +export class ExtensionTipsService extends Disposable implements IExtensionTipsService { _serviceBrand: any; @@ -1020,7 +1020,6 @@ export abstract class BaseExtensionTipsService extends Disposable implements IEx if (!windowsPath || typeof windowsPath !== 'string') { return; } - const processEnv = this.getProcessEnvironment(); windowsPath = windowsPath.replace('%USERPROFILE%', processEnv['USERPROFILE']!) .replace('%ProgramFiles(x86)%', processEnv['ProgramFiles(x86)']!) .replace('%ProgramFiles%', processEnv['ProgramFiles']!) @@ -1146,14 +1145,4 @@ export abstract class BaseExtensionTipsService extends Disposable implements IEx return this._allIgnoredRecommendations.indexOf(id.toLowerCase()) === -1; } - protected abstract getProcessEnvironment(): IProcessEnvironment; -} - - -export class ExtensionTipsService extends BaseExtensionTipsService implements IExtensionTipsService { - - protected getProcessEnvironment(): IProcessEnvironment { - return {}; - } - } diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 32bd703fba030..b7bed063f07e2 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionsLabel, ExtensionsChannelId, PreferencesLabel, IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/contrib/output/common/output'; @@ -45,9 +45,11 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { RemoteExtensionsInstaller } from 'vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller'; +import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); +registerSingleton(IExtensionTipsService, ExtensionTipsService); Registry.as(OutputExtensions.OutputChannels) .registerChannel({ id: ExtensionsChannelId, label: ExtensionsLabel, log: false }); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts deleted file mode 100644 index f866ccbb7a828..0000000000000 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts +++ /dev/null @@ -1,15 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { BaseExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; -import { IProcessEnvironment } from 'vs/base/common/platform'; - -export class ExtensionTipsService extends BaseExtensionTipsService { - - protected getProcessEnvironment(): IProcessEnvironment { - return process.env as IProcessEnvironment; - } - -} diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts index 74f15f54393e8..4c21e1e91815e 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -21,11 +21,8 @@ import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron import { URI } from 'vs/base/common/uri'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler'; -import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/electron-browser/extensionTipsService'; // Singletons -registerSingleton(IExtensionTipsService, ExtensionTipsService); registerSingleton(IExtensionHostProfileService, ExtensionHostProfileService, true); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 0c80d322d2aca..40384b249b094 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -87,9 +87,6 @@ import 'vs/workbench/contrib/debug/browser/extensionHostDebugService'; import 'vs/workbench/contrib/webview/browser/webviewService'; import 'vs/workbench/contrib/webview/browser/webviewEditorService'; -// Extensions Management -import 'vs/workbench/contrib/extensions/browser/extensions.web.contribution'; - // Terminal import 'vs/workbench/contrib/terminal/browser/terminalNativeService'; import 'vs/workbench/contrib/terminal/browser/terminalInstanceService'; From 520fac5336782b8eee87870873f8057d14b2d4b8 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 07:49:06 -0700 Subject: [PATCH 238/613] Remove click code from puppeteer driver, move interfaces to common --- src/vs/platform/driver/browser/baseDriver.ts | 33 +++++- src/vs/platform/driver/common/driver.ts | 7 +- .../driver/electron-browser/driver.ts | 26 ----- .../platform/driver/electron-main/driver.ts | 8 +- src/vs/platform/driver/node/driver.ts | 60 ++-------- test/smoke/src/vscode/puppeteerDriver.ts | 109 ++---------------- test/smoke/tools/copy-driver-definition.js | 4 +- 7 files changed, 64 insertions(+), 183 deletions(-) diff --git a/src/vs/platform/driver/browser/baseDriver.ts b/src/vs/platform/driver/browser/baseDriver.ts index df59dd6e879f3..38b5626a500dc 100644 --- a/src/vs/platform/driver/browser/baseDriver.ts +++ b/src/vs/platform/driver/browser/baseDriver.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getTopLeftOffset } from 'vs/base/browser/dom'; +import { getTopLeftOffset, getClientArea } from 'vs/base/browser/dom'; import { coalesce } from 'vs/base/common/arrays'; import { IElement, IWindowDriver } from 'vs/platform/driver/common/driver'; @@ -45,7 +45,6 @@ export abstract class BaseWindowDriver implements IWindowDriver { constructor() { } - // TODO: This doesn't work in browser driver abstract click(selector: string, xoffset?: number, yoffset?: number): Promise; abstract doubleClick(selector: string): Promise; @@ -101,6 +100,11 @@ export abstract class BaseWindowDriver implements IWindowDriver { 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); @@ -159,5 +163,30 @@ export abstract class BaseWindowDriver implements IWindowDriver { 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/common/driver.ts b/src/vs/platform/driver/common/driver.ts index 299628846a450..29fde87599441 100644 --- a/src/vs/platform/driver/common/driver.ts +++ b/src/vs/platform/driver/common/driver.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// TODO: Change smoketest build to read off common instead +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; // !! Do not remove the following START and END markers, they are parsed by the smoketest build @@ -32,12 +32,16 @@ export interface IDriver { 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; @@ -45,6 +49,7 @@ export interface IWindowDriver { 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 931b898d55f87..09e2d6b3379db 100644 --- a/src/vs/platform/driver/electron-browser/driver.ts +++ b/src/vs/platform/driver/electron-browser/driver.ts @@ -7,7 +7,6 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; 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 { timeout } from 'vs/base/common/async'; @@ -30,31 +29,6 @@ class WindowDriver extends BaseWindowDriver { 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); diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index 2b341fb2ed6f7..8096b8f6abda7 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 MIT License. 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 e77790a51832d..2c525c0e69421 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/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 2e2f2280dd6f4..7205ce266a653 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -26,7 +26,7 @@ const vscodeToPuppeteerKey = { }; function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver { - return { + const driver = { _serviceBrand: undefined, getWindowIds: () => { return Promise.resolve([1]); @@ -57,121 +57,25 @@ function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver await timeout(100); }, click: async (windowId, selector, xoffset, yoffset) => { - const { x, y } = await page.evaluate(` - (function() { - function convertToPixels(element, value) { - return parseFloat(value) || 0; - } - function getDimension(element, cssPropertyName, jsPropertyName) { - let computedStyle = getComputedStyle(element); - let value = '0'; - if (computedStyle) { - if (computedStyle.getPropertyValue) { - value = computedStyle.getPropertyValue(cssPropertyName); - } else { - // IE8 - value = (computedStyle).getAttribute(jsPropertyName); - } - } - return convertToPixels(element, value); - } - function getBorderLeftWidth(element) { - return getDimension(element, 'border-left-width', 'borderLeftWidth'); - } - function getBorderRightWidth(element) { - return getDimension(element, 'border-right-width', 'borderRightWidth'); - } - function getBorderTopWidth(element) { - return getDimension(element, 'border-top-width', 'borderTopWidth'); - } - function getBorderBottomWidth(element) { - return getDimension(element, 'border-bottom-width', 'borderBottomWidth'); - } - function getClientArea(element) { - // Try with DOM clientWidth / clientHeight - if (element !== document.body) { - return { width: element.clientWidth, height: element.clientHeight }; - } - - // Try innerWidth / innerHeight - if (window.innerWidth && window.innerHeight) { - return { width: window.innerWidth, height: window.innerHeight }; - } - - // Try with document.body.clientWidth / document.body.clientHeight - if (document.body && document.body.clientWidth && document.body.clientHeight) { - return { width: document.body.clientWidth, height: document.body.clientHeight }; - } - - // Try with document.documentElement.clientWidth / document.documentElement.clientHeight - if (document.documentElement && document.documentElement.clientWidth && document.documentElement.clientHeight) { - return { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight }; - } - - throw new Error('Unable to figure out browser width and height'); - } - function getTopLeftOffset(element) { - // Adapted from WinJS.Utilities.getPosition - // and added borders to the mix - - let offsetParent = element.offsetParent, top = element.offsetTop, left = element.offsetLeft; - - while ((element = element.parentNode) !== null && element !== document.body && element !== document.documentElement) { - top -= element.scrollTop; - let c = getComputedStyle(element); - if (c) { - left -= c.direction !== 'rtl' ? element.scrollLeft : -element.scrollLeft; - } - - if (element === offsetParent) { - left += getBorderLeftWidth(element); - top += getBorderTopWidth(element); - top += element.offsetTop; - left += element.offsetLeft; - offsetParent = element.offsetParent; - } - } - - return { - left: left, - top: top - }; - } - const element = document.querySelector('${selector}'); - - if (!element) { - throw new Error('Element not found: ${selector}'); - } - - const { left, top } = getTopLeftOffset(element); - const { width, height } = getClientArea(element); - let x, y; - - x = left + (width / 2); - y = top + (height / 2); - - x = Math.round(x); - y = Math.round(y); - - return { x, y }; - })(); - `); + const { x, y } = await driver.getElementXY(windowId, selector, xoffset, yoffset); await page.mouse.click(x + (xoffset ? xoffset : 0), y + (yoffset ? yoffset : 0)); }, doubleClick: async (windowId, selector) => { - await this.click(windowId, selector, 0, 0); + await driver.click(windowId, selector, 0, 0); await timeout(60); - await this.click(windowId, selector, 0, 0); + await driver.click(windowId, selector, 0, 0); await timeout(100); }, setValue: async (windowId, selector, text) => page.evaluate(`window.driver.setValue('${selector}', '${text}')`), getTitle: (windowId) => page.evaluate(`window.driver.getTitle()`), isActiveElement: (windowId, selector) => page.evaluate(`window.driver.isActiveElement('${selector}')`), getElements: (windowId, selector, recursive) => page.evaluate(`window.driver.getElements('${selector}', ${recursive})`), + getElementXY: (windowId, selector, xoffset?, yoffset?) => page.evaluate(`window.driver.getElementXY('${selector}', ${xoffset}, ${yoffset})`), typeInEditor: (windowId, selector, text) => page.evaluate(`window.driver.typeInEditor('${selector}', '${text}')`), getTerminalBuffer: (windowId, selector) => page.evaluate(`window.driver.getTerminalBuffer('${selector}')`), writeInTerminal: (windowId, selector, text) => page.evaluate(`window.driver.writeInTerminal('${selector}', '${text}')`) }; + return driver; } function timeout(ms: number): Promise { @@ -275,6 +179,7 @@ export interface IDriver { getTitle(windowId: number): Promise; isActiveElement(windowId: number, selector: string): Promise; getElements(windowId: number, selector: string, recursive?: boolean): Promise; + getElementXY(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; diff --git a/test/smoke/tools/copy-driver-definition.js b/test/smoke/tools/copy-driver-definition.js index 2af7a3acd2ae5..fdebfcc6b0a5b 100644 --- a/test/smoke/tools/copy-driver-definition.js +++ b/test/smoke/tools/copy-driver-definition.js @@ -7,7 +7,7 @@ const fs = require('fs'); const path = require('path'); const root = path.dirname(path.dirname(path.dirname(__dirname))); -const driverPath = path.join(root, 'src/vs/platform/driver/node/driver.ts'); +const driverPath = path.join(root, 'src/vs/platform/driver/common/driver.ts'); let contents = fs.readFileSync(driverPath, 'utf8'); contents = /\/\/\*START([\s\S]*)\/\/\*END/mi.exec(contents)[1].trim(); @@ -47,4 +47,4 @@ export function connect(outPath: string, handle: string): Promise<{ client: IDis const srcPath = path.join(path.dirname(__dirname), 'src/vscode'); const outDriverPath = path.join(srcPath, 'driver.d.ts'); -fs.writeFileSync(outDriverPath, contents); \ No newline at end of file +fs.writeFileSync(outDriverPath, contents); From 173b8c5cc6c6f6bda577cb1b020fbfef0254841b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 07:52:23 -0700 Subject: [PATCH 239/613] Reduce diff --- test/smoke/src/vscode/code.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/smoke/src/vscode/code.ts b/test/smoke/src/vscode/code.ts index 8a7ffc3d7b169..44cfcfe419f4b 100644 --- a/test/smoke/src/vscode/code.ts +++ b/test/smoke/src/vscode/code.ts @@ -227,7 +227,7 @@ export class Code { private driver: IDriver; constructor( - private client: IDisposable | undefined, + private client: IDisposable, driver: IDriver, readonly logger: Logger ) { @@ -343,9 +343,7 @@ export class Code { } dispose(): void { - if (this.client) { - this.client.dispose(); - } + this.client.dispose(); } } From 14ee0218838a3eeac988b40c8c68b7419adf40dd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 15 Aug 2019 16:06:50 +0200 Subject: [PATCH 240/613] fix typos --- .../api/node/extHostRequireInterceptor.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/node/extHostRequireInterceptor.ts b/src/vs/workbench/api/node/extHostRequireInterceptor.ts index 876d6716a676a..2b0839e319f67 100644 --- a/src/vs/workbench/api/node/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/node/extHostRequireInterceptor.ts @@ -22,7 +22,7 @@ interface LoadFunction { interface INodeModuleFactory { readonly nodeModuleName: string | string[]; load(request: string, parent: { filename: string; }, isMain: any, original: LoadFunction): any; - alternaiveModuleName?(name: string): string | undefined; + alternativeModuleName?(name: string): string | undefined; } export class NodeModuleRequireInterceptor { @@ -63,9 +63,9 @@ 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); }); } } @@ -155,7 +155,7 @@ export class KeytarNodeModuleFactory implements INodeModuleFactory { 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. @@ -194,7 +194,7 @@ export class OpenNodeModuleFactory implements INodeModuleFactory { private _original?: IOriginalOpen; private _impl: IOpenModule; - constructor(mainThreadWindow: MainThreadWindowShape, private _mainThreadTelemerty: MainThreadTelemetryShape, private readonly _extensionPaths: TernarySearchTree) { + constructor(mainThreadWindow: MainThreadWindowShape, private _mainThreadTelemetry: MainThreadTelemetryShape, private readonly _extensionPaths: TernarySearchTree) { this._impl = (target, options) => { const uri: URI = URI.parse(target); // If we have options use the original method. @@ -234,7 +234,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 +244,6 @@ 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 }); } } From 5e63f6e00fc9b8b049b1145ca88845853c70050d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 15 Aug 2019 16:55:56 +0200 Subject: [PATCH 241/613] web - reuse require interceptor logic --- .../extHostRequireInterceptor.ts | 113 ++++++++++++------ .../api/node/extHostExtensionService.ts | 38 ++++-- .../api/worker/extHostExtensionService.ts | 76 ++++++------ 3 files changed, 135 insertions(+), 92 deletions(-) rename src/vs/workbench/api/{node => common}/extHostRequireInterceptor.ts (67%) diff --git a/src/vs/workbench/api/node/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts similarity index 67% rename from src/vs/workbench/api/node/extHostRequireInterceptor.ts rename to src/vs/workbench/api/common/extHostRequireInterceptor.ts index 2b0839e319f67..ad16a75c56bde 100644 --- a/src/vs/workbench/api/node/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -5,14 +5,19 @@ 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'; interface LoadFunction { @@ -21,40 +26,43 @@ interface LoadFunction { interface INodeModuleFactory { readonly nodeModuleName: string | string[]; - load(request: string, parent: { filename: string; }, isMain: any, original: LoadFunction): any; + 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: IExtensionApiFactory, + 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, extensionPaths, this._extensionRegistry, configProvider)); + 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) { @@ -71,7 +79,9 @@ export class NodeModuleRequireInterceptor { } } -export class VSCodeNodeModuleFactory implements INodeModuleFactory { +//#region --- vscode-module + +class VSCodeNodeModuleFactory implements INodeModuleFactory { public readonly nodeModuleName = 'vscode'; private readonly _extApiImpl = new Map(); @@ -85,10 +95,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 +112,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 +131,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,7 +173,7 @@ export class KeytarNodeModuleFactory implements INodeModuleFactory { }; } - public load(request: string, parent: { filename: string; }): any { + public load(_request: string, _parent: URI): any { return this._impl; } @@ -173,6 +195,11 @@ export class KeytarNodeModuleFactory implements INodeModuleFactory { } } +//#endregion + + +//#region --- opn/open-module + interface OpenOptions { wait: boolean; app: string | string[]; @@ -186,15 +213,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 _mainThreadTelemetry: 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 +245,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; } @@ -247,3 +282,5 @@ export class OpenNodeModuleFactory implements INodeModuleFactory { this._mainThreadTelemetry.$publicLog2<{ extension: string }, ShimmingOpenCallNoForwardClassification>('shimming.open.call.noForward', { extension: this._extensionId }); } } + +//#endregion diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 5b03a318564da..10161ac3355ef 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl'; -import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor'; +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,6 +14,28 @@ import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; +class NodeModuleRequireInterceptor extends RequireInterceptor { + + protected _installInterceptor(): void { + const that = this; + 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 that._alternatives) { + let alternative = alternativeModuleName(request); + if (alternative) { + request = alternative; + break; + } + } + if (!that._factories.has(request)) { + return original.apply(this, arguments); + } + return that._factories.get(request)!.load(request, URI.file(parent.filename), isMain, original); + }; + } +} + export class ExtHostExtensionService extends AbstractExtHostExtensionService { protected async _beforeAlmostReadyToRunExtensions(): Promise { @@ -30,19 +52,11 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { } // Module loading tricks - const configProvider = await this._extHostConfiguration.getConfigProvider(); - const extensionPaths = await this.getExtensionPathIndex(); - NodeModuleRequireInterceptor.INSTANCE.register(new VSCodeNodeModuleFactory(extensionApiFactory, extensionPaths, this._registry, configProvider)); - NodeModuleRequireInterceptor.INSTANCE.register(new KeytarNodeModuleFactory(this._extHostContext.getProxy(MainContext.MainThreadKeytar), this._initData.environment)); - if (this._initData.remote.isRemote) { - NodeModuleRequireInterceptor.INSTANCE.register(new OpenNodeModuleFactory( - this._extHostContext.getProxy(MainContext.MainThreadWindow), - this._extHostContext.getProxy(MainContext.MainThreadTelemetry), - extensionPaths - )); - } + const interceptor = this._instaService.createInstance(NodeModuleRequireInterceptor, extensionApiFactory, this._registry); + await interceptor.install(); // Do this when extension service exists, but extensions are not being activated yet. + const configProvider = await this._extHostConfiguration.getConfigProvider(); await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._logService, this._mainThreadTelemetryProxy); // Use IPC messages to forward console-calls, note that the console is diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index d3d36327339a1..3dc4c2a97cb72 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -3,45 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createApiFactoryAndRegisterActors, IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl'; +import { createApiFactoryAndRegisterActors } from 'vs/workbench/api/common/extHost.api.impl'; import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator'; import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { endsWith, startsWith } from 'vs/base/common/strings'; -import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; -import * as vscode from 'vscode'; -import { TernarySearchTree } from 'vs/base/common/map'; -import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { joinPath } from 'vs/base/common/resources'; - -class ApiInstances { - - private readonly _apiInstances = new Map(); - - constructor( - private readonly _apiFactory: IExtensionApiFactory, - private readonly _extensionPaths: TernarySearchTree, - private readonly _extensionRegistry: ExtensionDescriptionRegistry, - private readonly _configProvider: ExtHostConfigProvider, - ) { - // - } - - get(modulePath: string): typeof vscode { - const extension = this._extensionPaths.findSubstr(modulePath) || nullExtensionDescription; - const id = ExtensionIdentifier.toKey(extension.identifier); - - let apiInstance = this._apiInstances.get(id); - if (!apiInstance) { - apiInstance = this._apiFactory(extension, this._extensionRegistry, this._configProvider); - this._apiInstances.set(id, apiInstance); - } - return apiInstance; - } -} +import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor'; class ExportsTrap { @@ -53,7 +22,7 @@ class ExportsTrap { private constructor() { const exportsProxy = new Proxy({}, { - set: (target: any, p: PropertyKey, value: any, receiver: any) => { + set: (target: any, p: PropertyKey, value: any) => { // store in target target[p] = value; // store in named-bucket @@ -74,7 +43,7 @@ class ExportsTrap { return target[p]; }, - set: (target: any, p: PropertyKey, value: any, receiver: any) => { + set: (target: any, p: PropertyKey, value: any) => { // store in target target[p] = value; @@ -106,16 +75,35 @@ class ExportsTrap { } } +class WorkerRequireInterceptor extends RequireInterceptor { + + _installInterceptor() { } + + getModule(request: string, parent: URI): undefined | any { + for (let alternativeModuleName of this._alternatives) { + let alternative = alternativeModuleName(request); + if (alternative) { + request = alternative; + break; + } + } + + if (this._factories.has(request)) { + return this._factories.get(request)!.load(request, parent, false, () => { throw new Error(); }); + } + return undefined; + } +} + export class ExtHostExtensionService extends AbstractExtHostExtensionService { - private _apiInstances?: ApiInstances; + private _fakeModules: WorkerRequireInterceptor; protected async _beforeAlmostReadyToRunExtensions(): Promise { // initialize API and register actors const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors); - const configProvider = await this._extHostConfiguration.getConfigProvider(); - const extensionPath = await this.getExtensionPathIndex(); - this._apiInstances = new ApiInstances(apiFactory, extensionPath, this._registry, configProvider); + this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, this._registry); + await this._fakeModules.install(); } protected _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { @@ -135,11 +123,15 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { // FAKE require function that only works for the vscode-module const moduleStack: URI[] = []; - patchSelf.require = (mod: string) => { + (self).require = (mod: string) => { + const parent = moduleStack[moduleStack.length - 1]; - if (mod === 'vscode') { - return this._apiInstances!.get(parent.fsPath); + const result = this._fakeModules.getModule(mod, parent); + + if (result !== undefined) { + return result; } + if (!startsWith(mod, '.')) { throw new Error(`Cannot load module '${mod}'`); } From 15496555ea85233b48205f56604730ac2f1b16d8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 15 Aug 2019 16:57:41 +0200 Subject: [PATCH 242/613] remove FakeCommonJSSelf --- .../workbench/api/worker/extHostExtensionService.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index 3dc4c2a97cb72..fdc57cbb2ec1e 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -108,18 +108,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { protected _loadCommonJSModule(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { - interface FakeCommonJSSelf { - module?: object; - exports?: object; - require?: (module: string) => any; - window?: object; - __dirname: never; - __filename: never; - } - - // FAKE commonjs world that only collects exports - const patchSelf: FakeCommonJSSelf = self; - patchSelf.window = self; // <- that's improper but might help extensions that aren't authored correctly + (self).window = self; // <- that's improper but might help extensions that aren't authored correctly // FAKE require function that only works for the vscode-module const moduleStack: URI[] = []; From 6e8d4cf70f05e932248f544ddb1b324edced90f2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 07:58:24 -0700 Subject: [PATCH 243/613] Fix dispatch keybinding when called multiple times --- test/smoke/src/vscode/puppeteerDriver.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 7205ce266a653..6b1f51d8ce8d0 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -36,8 +36,9 @@ function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver exitApplication: () => browser.close(), dispatchKeybinding: async (windowId, keybinding) => { const chords = keybinding.split(' '); - chords.forEach(async (chord, index) => { - if (index > 0) { + for (let i = 0; i < chords.length; i++) { + const chord = chords[i]; + if (i > 0) { await timeout(100); } const keys = chord.split('+'); @@ -52,7 +53,7 @@ function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver while (keysDown.length > 0) { await page.keyboard.up(keysDown.pop()!); } - }); + } await timeout(100); }, From 8e55695fd55f96e491a2f7079972b8c24fb0a25c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 08:09:50 -0700 Subject: [PATCH 244/613] Fix layer breakage Part of #79210 --- .../contrib/terminal/browser/terminalProcessManager.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index b3ff7db5e88e2..d99d8de0ab15f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -5,6 +5,7 @@ import * as platform from 'vs/base/common/platform'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; +import { env as processEnv } from 'vs/base/common/process'; import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent, ITerminalEnvironment, ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal'; import { ILogService } from 'vs/platform/log/common/log'; import { Emitter, Event } from 'vs/base/common/event'; @@ -225,7 +226,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce const envFromConfigValue = this._workspaceConfigurationService.inspect(`terminal.integrated.env.${platformKey}`); const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(); this._configHelper.showRecommendations(shellLaunchConfig); - const baseEnv = this._configHelper.config.inheritEnv ? process.env as platform.IProcessEnvironment : await this._terminalInstanceService.getMainProcessParentEnv(); + const baseEnv = this._configHelper.config.inheritEnv ? processEnv : await this._terminalInstanceService.getMainProcessParentEnv(); const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.setLocaleVariables, baseEnv); const useConpty = this._configHelper.config.windowsEnableConpty && !isScreenReaderModeEnabled; From 2bceb25547ab539f565d68b6dfcef96e32da455f Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 15 Aug 2019 17:25:50 +0200 Subject: [PATCH 245/613] Fixes #78975: Look for autoclosing pairs in edits coming in from suggestions --- src/vs/editor/browser/editorBrowser.ts | 4 +- .../editor/browser/widget/codeEditorWidget.ts | 18 +-- src/vs/editor/common/controller/cursor.ts | 119 +++++++++++++++--- .../editor/contrib/snippet/snippetSession.ts | 14 +-- .../test/browser/controller/cursor.test.ts | 22 ++++ src/vs/monaco.d.ts | 2 +- 6 files changed, 140 insertions(+), 39 deletions(-) diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 91e6ea3864b27..a5e31344d3c2d 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -13,7 +13,7 @@ import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel, ICursorStateComputer } from 'vs/editor/common/model'; import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; @@ -612,7 +612,7 @@ export interface ICodeEditor extends editorCommon.IEditor { * @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. diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index e6a4fd0597778..0ac879ee78a59 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -33,7 +33,7 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { InternalEditorAction } from 'vs/editor/common/editorAction'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { EndOfLinePreference, IIdentifiedSingleEditOperation, IModelDecoration, IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { EndOfLinePreference, IIdentifiedSingleEditOperation, IModelDecoration, IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, ICursorStateComputer } from 'vs/editor/common/model'; import { ClassName } from 'vs/editor/common/model/intervalTree'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents'; @@ -980,7 +980,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return true; } - public executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: Selection[]): boolean { + public executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: ICursorStateComputer | Selection[]): boolean { if (!this._modelData) { return false; } @@ -989,14 +989,16 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return false; } - this._modelData.model.pushEditOperations(this._modelData.cursor.getSelections(), edits, () => { - return endCursorState ? endCursorState : null; - }); - - if (endCursorState) { - this._modelData.cursor.setSelections(source, endCursorState); + let cursorStateComputer: ICursorStateComputer; + if (!endCursorState) { + cursorStateComputer = () => null; + } else if (Array.isArray(endCursorState)) { + cursorStateComputer = () => endCursorState; + } else { + cursorStateComputer = endCursorState; } + this._modelData.cursor.executeEdits(source, edits, cursorStateComputer); return true; } diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 789d3a97b021c..fefca3cd9b55f 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -15,7 +15,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ISelection, Selection, SelectionDirection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness, IModelDeltaDecoration } from 'vs/editor/common/model'; +import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, ICursorStateComputer } from 'vs/editor/common/model'; import { RawContentChangedType } from 'vs/editor/common/model/textModelEvents'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; @@ -429,6 +429,31 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { // ------ auxiliary handling logic + private _pushAutoClosedAction(autoClosedCharactersRanges: Range[], autoClosedEnclosingRanges: Range[]): void { + let autoClosedCharactersDeltaDecorations: IModelDeltaDecoration[] = []; + let autoClosedEnclosingDeltaDecorations: IModelDeltaDecoration[] = []; + + for (let i = 0, len = autoClosedCharactersRanges.length; i < len; i++) { + autoClosedCharactersDeltaDecorations.push({ + range: autoClosedCharactersRanges[i], + options: { + inlineClassName: 'auto-closed-character', + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges + } + }); + autoClosedEnclosingDeltaDecorations.push({ + range: autoClosedEnclosingRanges[i], + options: { + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges + } + }); + } + + const autoClosedCharactersDecorations = this._model.deltaDecorations([], autoClosedCharactersDeltaDecorations); + const autoClosedEnclosingDecorations = this._model.deltaDecorations([], autoClosedEnclosingDeltaDecorations); + this._autoClosedActions.push(new AutoClosedAction(this._model, autoClosedCharactersDecorations, autoClosedEnclosingDecorations)); + } + private _executeEditOperation(opResult: EditOperationResult | null): void { if (!opResult) { @@ -446,32 +471,19 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { this._interpretCommandResult(result); // Check for auto-closing closed characters - let autoClosedCharactersRanges: IModelDeltaDecoration[] = []; - let autoClosedEnclosingRanges: IModelDeltaDecoration[] = []; + let autoClosedCharactersRanges: Range[] = []; + let autoClosedEnclosingRanges: Range[] = []; for (let i = 0; i < opResult.commands.length; i++) { const command = opResult.commands[i]; if (command instanceof TypeWithAutoClosingCommand && command.enclosingRange && command.closeCharacterRange) { - autoClosedCharactersRanges.push({ - range: command.closeCharacterRange, - options: { - inlineClassName: 'auto-closed-character', - stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges - } - }); - autoClosedEnclosingRanges.push({ - range: command.enclosingRange, - options: { - stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges - } - }); + autoClosedCharactersRanges.push(command.closeCharacterRange); + autoClosedEnclosingRanges.push(command.enclosingRange); } } if (autoClosedCharactersRanges.length > 0) { - const autoClosedCharactersDecorations = this._model.deltaDecorations([], autoClosedCharactersRanges); - const autoClosedEnclosingDecorations = this._model.deltaDecorations([], autoClosedEnclosingRanges); - this._autoClosedActions.push(new AutoClosedAction(this._model, autoClosedCharactersDecorations, autoClosedEnclosingDecorations)); + this._pushAutoClosedAction(autoClosedCharactersRanges, autoClosedEnclosingRanges); } this._prevEditOperationType = opResult.type; @@ -563,6 +575,75 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { // ----------------------------------------------------------------------------------------------------------- // ----- handlers beyond this point + private _findAutoClosingPairs(edits: IIdentifiedSingleEditOperation[]): [number, number][] | null { + if (!edits.length) { + return null; + } + + let indices: [number, number][] = []; + for (let i = 0, len = edits.length; i < len; i++) { + const edit = edits[i]; + if (!edit.text || edit.text.indexOf('\n') >= 0) { + return null; + } + + const m = edit.text.match(/([)\]}>'"`])([^)\]}>'"`]*)$/); + if (!m) { + return null; + } + const closeChar = m[1]; + + const openChar = this.context.config.autoClosingPairsClose[closeChar]; + if (!openChar) { + return null; + } + + const closeCharIndex = edit.text.length - m[2].length - 1; + const openCharIndex = edit.text.lastIndexOf(openChar, closeCharIndex - 1); + if (openCharIndex === -1) { + return null; + } + + indices.push([openCharIndex, closeCharIndex]); + } + + return indices; + } + + public executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void { + let autoClosingIndices: [number, number][] | null = null; + if (source === 'snippet') { + autoClosingIndices = this._findAutoClosingPairs(edits); + } + + if (autoClosingIndices) { + edits[0]._isTracked = true; + } + let autoClosedCharactersRanges: Range[] = []; + let autoClosedEnclosingRanges: Range[] = []; + const selections = this._model.pushEditOperations(this.getSelections(), edits, (undoEdits) => { + if (autoClosingIndices) { + for (let i = 0, len = autoClosingIndices.length; i < len; i++) { + const [openCharInnerIndex, closeCharInnerIndex] = autoClosingIndices[i]; + const undoEdit = undoEdits[i]; + const lineNumber = undoEdit.range.startLineNumber; + const openCharIndex = undoEdit.range.startColumn - 1 + openCharInnerIndex; + const closeCharIndex = undoEdit.range.startColumn - 1 + closeCharInnerIndex; + + autoClosedCharactersRanges.push(new Range(lineNumber, closeCharIndex + 1, lineNumber, closeCharIndex + 2)); + autoClosedEnclosingRanges.push(new Range(lineNumber, openCharIndex + 1, lineNumber, closeCharIndex + 2)); + } + } + return cursorStateComputer(undoEdits); + }); + if (selections) { + this.setSelections(source, selections); + } + if (autoClosedCharactersRanges.length > 0) { + this._pushAutoClosedAction(autoClosedCharactersRanges, autoClosedEnclosingRanges); + } + } + public trigger(source: string, handlerId: string, payload: any): void { const H = editorCommon.Handler; diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 689b5567ed17b..63aa8a230eb92 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -489,21 +489,18 @@ export class SnippetSession { return; } - const model = this._editor.getModel(); - // make insert edit and start with first selections const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, this._template, this._options.overwriteBefore, this._options.overwriteAfter, false, this._options.adjustWhitespace, this._options.clipboardText); this._snippets = snippets; - const selections = model.pushEditOperations(this._editor.getSelections(), edits, undoEdits => { + this._editor.executeEdits('snippet', edits, undoEdits => { if (this._snippets[0].hasPlaceholder) { return this._move(true); } else { return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition())); } - })!; - this._editor.setSelections(selections); - this._editor.revealRange(selections[0]); + }); + this._editor.revealRange(this._editor.getSelections()[0]); } merge(template: string, options: ISnippetSessionInsertOptions = _defaultOptions): void { @@ -513,8 +510,7 @@ export class SnippetSession { this._templateMerges.push([this._snippets[0]._nestingLevel, this._snippets[0]._placeholderGroupsIdx, template]); const { edits, snippets } = SnippetSession.createEditsAndSnippets(this._editor, template, options.overwriteBefore, options.overwriteAfter, true, options.adjustWhitespace, options.clipboardText); - this._editor.setSelections(this._editor.getModel().pushEditOperations(this._editor.getSelections(), edits, undoEdits => { - + this._editor.executeEdits('snippet', edits, undoEdits => { for (const snippet of this._snippets) { snippet.merge(snippets); } @@ -525,7 +521,7 @@ export class SnippetSession { } else { return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition())); } - })!); + }); } next(): void { diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index a42bd6d06c303..e925406c1b4d7 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -4692,6 +4692,28 @@ suite('autoClosingPairs', () => { mode.dispose(); }); + test('issue #78975 - Parentheses swallowing does not work when parentheses are inserted by autocomplete', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + '
{ + 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 b44b3ae08ad3b..222b9e8466640 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4054,7 +4054,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. From 6bf9508af6186b6206a428ce622f6e0d0ea8d9d0 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 08:36:02 -0700 Subject: [PATCH 246/613] Remove smoke tests from CI for now --- build/azure-pipelines/web/product-build-web.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 8dde071b48572..fe5231dacc1b8 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -88,15 +88,6 @@ steps: yarn gulp vscode-web-min-ci displayName: Build -- script: | - set -e - cd test/smoke - yarn compile - cd - - yarn smoketest --web --headless - continueOnError: true - displayName: Smoke tests - # upload only the workbench.web.api.js source maps because # we just compiled these bits in the previous step and the # general task to upload source maps has already been run From d8cb87bd2125665fd24c2cb48658afbc025c541a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 08:45:30 -0700 Subject: [PATCH 247/613] Reduce diff --- src/vs/platform/driver/browser/driver.ts | 13 ------------- test/smoke/src/main.ts | 5 ----- 2 files changed, 18 deletions(-) diff --git a/src/vs/platform/driver/browser/driver.ts b/src/vs/platform/driver/browser/driver.ts index 72cf321f07611..f13d45a5f13d7 100644 --- a/src/vs/platform/driver/browser/driver.ts +++ b/src/vs/platform/driver/browser/driver.ts @@ -20,20 +20,7 @@ class BrowserWindowDriver extends BaseWindowDriver { export async function registerWindowDriver(): Promise { (window).driver = new BrowserWindowDriver(); - // const windowDriverChannel = new WindowDriverChannel(windowDriver); - // mainProcessService.registerChannel('windowDriver', windowDriverChannel); - // const windowDriverRegistryChannel = mainProcessService.getChannel('windowDriverRegistry'); - // const windowDriverRegistry = new WindowDriverRegistryChannelClient(windowDriverRegistryChannel); - - // await windowDriverRegistry.registerWindowDriver(windowService.windowId); - // const options = await windowDriverRegistry.registerWindowDriver(windowId); - - // if (options.verbose) { - // windowDriver.openDevTools(); - // } - - // return toDisposable(() => windowDriverRegistry.reloadWindowDriver(windowService.windowId)); return toDisposable(() => { return { dispose: () => { } }; }); diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 8427b54080974..432528f7546a2 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -247,11 +247,6 @@ describe('Running Code', () => { const app = new Application(this.defaultOptions); await app!.start(opts.web ? false : undefined); this.app = app; - - // TODO: User data dir is not cleared for web yet - if (opts.web) { - await app.workbench.settingsEditor.clearUserSettings(); - } }); after(async function () { From 75af7716f8ab132ddf32c082850d38c8b53665f4 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 15 Aug 2019 18:25:04 +0200 Subject: [PATCH 248/613] Add setting to toggle new octicon style --- .../browser/ui/octiconLabel/octiconLabel.ts | 4 +- .../octiconLabel/octicons/octicons-main.css | 24 + .../ui/octiconLabel/octicons/octicons.css | 31 +- .../ui/octiconLabel/octicons/octicons.svg | 22 +- .../ui/octiconLabel/octicons/octicons.ttf | Bin 37504 -> 37448 bytes .../ui/octiconLabel/octicons/octicons2.css | 251 ++++++++ .../ui/octiconLabel/octicons/octicons2.svg | 570 ++++++++++++++++++ .../ui/octiconLabel/octicons/octicons2.ttf | Bin 0 -> 35152 bytes src/vs/workbench/browser/layout.ts | 29 + 9 files changed, 904 insertions(+), 27 deletions(-) create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg create mode 100644 src/vs/base/browser/ui/octiconLabel/octicons/octicons2.ttf diff --git a/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts b/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts index 0b1154a98fdb2..70c4a9e8cd2e4 100644 --- a/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts +++ b/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./octicons/octicons'; +import 'vs/css!./octicons/octicons-main'; import 'vs/css!./octicons/octicons-animations'; import { escape } from 'vs/base/common/strings'; @@ -30,4 +30,4 @@ export class OcticonLabel { set title(title: string) { this._container.title = title; } -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css new file mode 100644 index 0000000000000..d06eca484d13d --- /dev/null +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css @@ -0,0 +1,24 @@ +@import 'octicons.css'; +@import 'octicons2.css'; + +body[data-octicons-update="enabled"] { + --version: octicons2; +} + +body { + --version: octicons; +} + +.octicon, .mega-octicon { + font-family: var(--version); +} + +body[data-octicons-update="enabled"] .monaco-workbench .part.statusbar > .items-container > .statusbar-item span.octicon { + font-size: 16px; +} + +body[data-octicons-update="enabled"] .monaco-workbench .part.statusbar > .items-container > .statusbar-item > a { + display: flex; + align-items: center; + justify-content: center; +} diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css index d9cc1b7a4fa28..29ed93db1b12b 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.css @@ -1,7 +1,7 @@ @font-face { font-family: "octicons"; - src: url("./octicons.ttf?1b0f2a9535896866c74dd24eedeb4374") format("truetype"), -url("./octicons.svg?1b0f2a9535896866c74dd24eedeb4374#octicons") format("svg"); + src: url("./octicons.ttf?dda6b6d46f87b1fa91a76fc0389eeb1d") format("truetype"), +url("./octicons.svg?dda6b6d46f87b1fa91a76fc0389eeb1d#octicons") format("svg"); } .octicon, .mega-octicon { @@ -169,7 +169,7 @@ url("./octicons.svg?1b0f2a9535896866c74dd24eedeb4374#octicons") format("svg"); .octicon-person-outline:before { content: "\f018" } .octicon-pin:before { content: "\f041" } .octicon-plug:before { content: "\f0d4" } -.octicon-plus-small:before { content: "\f28a" } +.octicon-plus-small:before { content: "\f05d" } .octicon-plus:before { content: "\f05d" } .octicon-primitive-dot:before { content: "\f052" } .octicon-primitive-square:before { content: "\f053" } @@ -233,16 +233,19 @@ url("./octicons.svg?1b0f2a9535896866c74dd24eedeb4374#octicons") format("svg"); .octicon-watch:before { content: "\f0e0" } .octicon-x:before { content: "\f081" } .octicon-zap:before { content: "\26a1" } +.octicon-error:before { content: "\26a2" } +.octicon-eye-closed:before { content: "\26a3" } +.octicon-fold-down:before { content: "\26a4" } +.octicon-fold-up:before { content: "\26a5" } +.octicon-github-action:before { content: "\26a6" } +.octicon-info-outline:before { content: "\26a7" } +.octicon-play:before { content: "\26a8" } +.octicon-remote:before { content: "\26a9" } +.octicon-request-changes:before { content: "\26aa" } +.octicon-smiley-outline:before { content: "\f27d" } +.octicon-warning:before { content: "\f02d" } +.octicon-controls:before { content: "\26ad" } +.octicon-event:before { content: "\26ae" } +.octicon-record-keys:before { content: "\26af" } .octicon-archive:before { content: "\f101" } .octicon-arrow-both:before { content: "\f102" } -.octicon-error:before { content: "\f103" } -.octicon-eye-closed:before { content: "\f104" } -.octicon-fold-down:before { content: "\f105" } -.octicon-fold-up:before { content: "\f106" } -.octicon-github-action:before { content: "\f107" } -.octicon-info-outline:before { content: "\f108" } -.octicon-play:before { content: "\f109" } -.octicon-remote:before { content: "\f10a" } -.octicon-request-changes:before { content: "\f10b" } -.octicon-smiley-outline:before { content: "\f10c" } -.octicon-warning:before { content: "\f10d" } diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg index 3f4ab4f180795..48f7d1b2220f3 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons.svg @@ -167,10 +167,10 @@ unicode="" horiz-adv-x="750" d=" M687.5 507.5H62.5C28.125 507.5 0 479.375 0 445V195C0 160.625 28.125 132.5 62.5 132.5H687.5C721.875 132.5 750 160.625 750 195V445C750 479.375 721.875 507.5 687.5 507.5zM250 257.5H125V382.5H250V257.5zM437.5 257.5H312.5V382.5H437.5V257.5zM625 257.5H500V382.5H625V257.5z" /> +=y8Xq&=d?#f{lO&{&SxnNTqzo2QWTde0wPB&=c^Z}) zGrkr!ZzBsS7PGTuqe+s5Vlnm(H|O84bGx_B@1FIPl1?hI7VklIVFjRNfc&?a$ntb_ zFdGJn27qXGHat1y`ntR~f~|_4HnuBuKcJM(&qWtsJhwhzCum%?xP11`hY6?u8+qzG zxSm$qF8{lw(Wlzr2K`>BWl;Z4jofqD>V~F#@IwgQu%TRp#f9m%4U=L_Y==0P@IN3fB#2x6j-?#4TQ8x6HLBNd`~c5JTv-4B delta 487 zcmXw#-7CXU7{|ZQ@3$8-^DmxL&@a^ceW z3wEQ_Qf}l*?i3|g?sPQb2G4f#eEamA&gneg?>RSTO8i_o^whNnOQ!+dGhlHBL(8+_ z!E691j|1WCTwrp>QSYlw_}dgK5<4|z5kRpmE`%d5?kg{_yPvr5IDXCb^AjbVA26%0 z;CirqKmGT#j^5NJ4V2qZT~PC@X6}XTszIk@`Ct@1(8I0;v{cfmA0|sOq`w{;#}p#0 z4Qyc>d$`3N?(vBq0vX6k6=b7ImYwAwC%LFGuU3-sPzS4vdTD@$XojK`WiLN6h#{Un zF?3Uv7e4f%A0r%L69+iPDK621RQl1_K&G!;YOYMO*yOgv04*iq`$wiF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.ttf b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.ttf new file mode 100644 index 0000000000000000000000000000000000000000..97d221792967c3dc9182078b5514358637c80843 GIT binary patch literal 35152 zcmeFad3+nyxj%f)jP`w(EZZ8%vLxGDY)h8pB^f!kv+tX;JNrTiWCM}_3Q$5IVF_JG zSjo&d6;%*}3;ybr(Ucbp(dHNLY{W++x4JY&={tq}F z1vPHF8p}+P732}ZM=XPDPe*Z4|6N>w305^p%U4O~upZ@00 z(+D~EE!^L*YvkC3zMD_-oniL?=sa0Aok zEFrcX>o2>M1c^qus$QRXOm|$gbv?>guTb2_u2P(#L^pnx{}sKRXz|U&u^zic{X{L= zDQdtGEswMR;I&}O)J>!}zWLOxN`I_GTb{#h6MBQS`hVzu`rv;z9srlK`NHN!3WPbO z$*6qKH{mRs0V#Z9=Hk4DJA>~-q!+&l#W%+vjz1QEQsN|=& zTdL(Bm+q2=rTdlgpQ@Js9q9zhe^Yu#dRO}SNA%RyDF0?w{{O-kMIQ~3ZDa?zlSxmk{;zU5ZTx31DnH(h@q>D6?>&U0TF-dY2SxKHCUnX0~wd6~rfn0?C z*h&tQuaPLZ1AXBo+es_Ql6I0K%ZQQeCVsLL^4~%3B28o|*-S2j46Z}peSv(D%p!y2 z0pcOuq?z0at&t~}ljYz_J>-8abW9C-lFTJjz?t`vh2&|niyRq#7g!0Cj+BSx4DM1u5Ugjqo(P9@9_ z$P<+?Q;397WrR6HB)v+QJw!67gn2|HAT&mpQAC0yWQ4gzB(qAGWkf>HFv5Hzl2s+8 zJ~kYKFawF?P)Rv<;TVK@NhG&Qn4v`SsD!ynB(F-CwM6o%g!xM(zebTSBN7`A5ab$>Sf7F*;fTb> z0|Z$|BsK;hhA$=7*C3`LrCybo=9H$W1er)AHhv&TMAjkyu$E$XFuHQVG(QNNij|kh?^hs}dwIk=VF`Ad88__!R`H zOr%9BK|T|S)d>ViO{77UnD&>JsRZdwq~$6>juUBxN|5M8suE;7kyfb$DNiKEHz3G+ zA~C)Jk!$feq!M%hk=CjNZ9t@TDnTz0iM0&`4MC(0DnVBeX`@Qe8bsQp67&a=Hmd|p zLL~M)5OfNW*l$44E<|EwfuLuIv|S}=93rtX20`}_X@^SCLPWYmCFmm}U8)i^6OneQ z1RX`B-6}y_5$ST3ptp#0g-Xz1MB1kkbQzHtFM^=eh{XC41pP*&t5t%gBNF2q5Of}q z4ypw0N2F_1f*vH&wJJd)5{Zo;h}kF7A(fydiFAWX(3eEIQ6*?jBHg4CbSRN-Q3=|V zNVlp4y-K8GDv<~9`DvA)Yl-wZm7sNr#QGEj{Y#|Js{~C^?dQc^1M@p;>AZU9cJ*E=$K9Lyz zfq(#r#O65&Sb#`Rt3-P7`HV`y2Sj3h2Le(c(hDj9GZ5*UDgiwZ={qU`M-b^1m4GOS zbV4Oy3nIOy5>N(_zN-@O29aJ@3CM#;Z>j_gLZp9H321~!|E3af36XxF5)cZJey9?# z3X$GY38;lg@2CX)LZlz51SCVGAFBjRL!@_A0=gm6NtJ+ei1eOHKs-cxUnO84BK=e) zpdcdsTqWQkBK<-oAS3b-Rf$>fA61E23#Y)tjDVZSDex~NASiMQJj@7Kikw2d839$1 zQ`f2ld__)OrxK7BIrS-(fVs%2>s12!BBu_i1RO?Afwvg}k&#o71&kPvodTaTBJ1#Z zL?z%ga_VN4fZWKbTT}vuBd5Ugj2N0zUU5jYD-!s%11`Jmi4jF^SqsDI; z|6p2V+Hbns^c~ah%t7-c^9J*<`CUuOvc&R&)nwgneZ=}}n`rx{-Dclrf67tkSnjNI z_BuB?54(tKhwD4;LHDpH?)kcR(0jM9(>KR=+)w;7{8#$F6^I8`2Oh4m)QB~A*SryI z3T_GhxVEqMncCBJ4R!a`CD|3N4mniJY1bO=Mj-C;|3QutHh6Ja?rBeK6i*Lb+` z+p$z^QS4vhns_L_I(}LF%kdNOza{DtpGmyaw4>>^rstXq&9}AqS`N4TI@z1NEcsNb zF109ib?R%WQ|aFHQ|U9AYctNbz0kg{{i*hU&2rgL_EXv4v=K!LPQh%fvGjcg^)C=Mq zMR`jtpQ}CdTe_KUlFK4q-02F0{3BMA-B6MAEEoW)8P@J@&{b>2qVMg za^(&Bv23CHl;?o=VHzK!e`bi#?bj#rMZZ3hDEhtnL~-Q5K zNb2xmyp5(9i!uy!_AY({>S-jTNOLqnolz%!g?6TBS4ysw)~=P7NowmbTKs@NjAi zT`hlcD=Q!OeuJK2_r}y-@J9Uf`0#Lgt8&Zd?@ivD!mq~5t=>zOoDSuJrm8}@*B`X^o7z9^l{p$ z9IFjxa_a0+eh2#B3mq9o-|+cdKAP8p+jw1w%SEyxM~luP_(orh`1wtL7Df5h(w_~S zv(CZo#?<8GeqH%W`S$D9)2G(g(jSkBl!||q8*Sk_hode`->^~*mH+y?Pew-+FAAQw zjr#=XG|Q|V>JiYj9vjz?;qvI>!erVZa{dsnr_l*tytqy4@ns8nJ{gFGPP(I+_DrMu z-B2u$-*B+6=c0MdRNncPzcx8h^dVS~0ojvj$Qx^>$&99pg(dyGPR<<4-Tr{;5Y-|j&e!+PD3H)LM78Wq3&Ro{# z_2~2=iuVNX&7m$G(S-65Pg2_TgKgV$>HMT!TaVt-GxgwY*LL=crGo};_tHhFez$FY zamjj08-FpWV|QO6)6t6J!6|2d0G@mvGu@B=XaM4E0V2;bi!kQT6){s25j$ z5og5jO!!L_2X+c^poc*4Vt(6bVMkB;dYGFylKiZ{yFb6R?`#C-helak8 zq}0p^{@@c)y)(jFD{lw|%2BRZ`c>vv^kc}5GjDmRApgS4rz){CXPDDj%lcMnw-q|P z7QNeylA`$p1QbeP0xE{4XubM8O7$kdj9^HoAJVIs$D@wE81hO z2_xY#`dnFlbxar)#<=bw0rFk8pz$9MmBhANgm73mmL3(wF*@a#PY^t}$=_CFq{{mR zMaw~-QjzAeA1(e`*EKAAQo z+VS3j`+I2C=l9i9kKXOk~o3D)tskYjV8OzqJTHKd%85?^NMpG@npeK>{nUmA#Vxz@n>8HG1W3(8Jye{IM z?2YJnTy9_#yXZnWKom*lZjhGDFb3(Z5qNB4qFJ3*}tLORJ zxX~L*T~^AhoI5FMa;N$htzI)YBdFIJYjtWgM(e#sqvfE< z=&q^p7)%E(M%tEb2}N~IPuJAc;!*>hbMSCGr)>;bZLJ%oyW@+q!B{L8iv`tr{~E^p zG?1)W(G_53C<><^8Yl+wH-y0iuYmbJthf|99j>^bbUu6+%I0Gc;EBnq2l$6jMiJO` zF-D8^s*TpuXhQGT>%1Pnq9xJCdVkR$qS-=;`iqb$B?_6w2BDP}6Z%NLL!{XdekV{& zX+4uYG!f5tlqmN|`;v`I+B>$zHq$V@_C`mFyDE0Mrg__*EzO#Jv8%bXgWmKhO2gcS z#%=QP=%neBqHDQ2pLgb}^{ZxjeRUjH+n8P4PP2=%jVOd)Jmhlm9o+KiJS(TQBb(9e zj~(P^tmHa)muu}3enV_)V{}p!e~o#5){>e1y=$}CwY~i_m&{@^2pAXglJ%Rts>cP` zbP3p>t+2g{@RO8)1*gMrF<+TOQEwz49iK%!Bt@R7#{WcExN+V;L3m93&f`3PbWKUN zh?qc_Ln2fnKZ zIE`jh<--ByRWQ;7UvyLcvC2#7v#r1565LFg@>FP%|4x6W*g|JN0A_j~_nIJ)T#(lF zu;!CMBTQiz1*YfOKW&xx99RJSqFnV?zx^&XC6!v9TCE@(hHfU%UsF4ADRqf@%RTBh zuC6cHvOMJ~mC@R=NSEWDWV$jXom`%h-)3J@)poSdH(5Jk(8f_9l|NHQLVhnHzaRf+ zcJw}Ymo-<_^VdV)C~ejQq4?=ub+-A0+%TFY7-E@z(AXlOv)x{ zS7Qon9cRdgl?!=|iL;zmP%dm3^d>NtzWRYyzLuhM*`NH{xkLH_JDev!lcL4(BW3O# z=AQr``oMo}sBlPY(_}PxkGU;!%VUwo%?p0#%65Zf88~ER!lbxI{Vm&9jYtlpkH#<84imx4RsFO&7E#I^W#W_Z~8;!H{7q~uP{{N zu#L$l?9MUBWrwZuCb!Gxl-Iply3tVYR7>3Bw96;PYz~S$&`yTuZ|3hs{|Dg*$ub|O zHtLLcb5N1_Xh(*(agcpXd*+-X2km3y5*aXt9%vEFo&&V5LdOJsZj^pp3a=>vLuE5j zci&1uI0;n{HCwgel-ptB=tzI5KYbv*U*3>9kQxvNQUxws9uA8&nW&56XEkCNIDz}b zU8}b_T9g8aY6dV)M5H!eaF<^*4~yil(1i<9H{Pi7%kTL+75T;xB5W_k zu#DitUu1$YLE{fSlo}bKzf7^u$}mELo%BZ!J(M0zji!|k`CE0|?mDGk4lDZ8%TPRR z0FKj$ntUO{@lNm{`shChs~GKvuwrNojKiJ2fc;dl)PLrO+=sY$9QUa8{jBl~7f?JT zbcw?Hg)!rV!Q_OQ?acYLsCbUSdCbrvv%DFw9V^YqL%DJv3Swo!kU)=IR#*e%4%j;O zE`4@Ln6am{V!Ce_AQoqTJ0{N#zk8Vv_jTgy2|*ki6S5dL+*K@@_k|0_l&^hDt|N`-W8@FqddqOH0m+f zy*$vG>N^0e@IsW*NQ2JnguIW4G~tA3WGLa{bI8EOWEb~gFh(2SE|=+wQ%9n@eX88`V^Q+qc22c_ia8i3 zev}<;rBRsmei(^>P%v%cdM%6;8<)Yc$Q7&S6OiTHy?<0}CE<^G->6Uaavz@b<^L(~ zJ2_P48neqvYvQGPdw%w`7DX2grF;u_UertL@>973p;$HqdzD4Jn8@_L8S z>ZW4LwAp!keM!+|rQVBnF7%~_x?)rFbrZ*f*&{^7SwJ4JI`WFV85cElIZ(z3U8dR% zOwt?^N}^ao_g9|aa%^mwJ$F>`B~)!fTD1w?tfwJaXpJKIEJBGsE6;}OQE?}*u|4x= zn25(9E0~Q$P{#9$UFjdM5tDq*h&IQ3rf?oa^EtRCj$zDpqdsqNS^0!GI*LEFUe!94 zMoJ?i@M@~{8n;!Uf7}!5g)-r)xWHPaAHz?2Vcp8?%=tEsmxZXzcv;NurBKZos3f=o z{1Zx)kK_ikG&i_KdC#3|H0&s}`i(5RpuE9oP}_fTwf)QoJE5)=dgi3U?ut{V@_qW8 z{OScwh9|G|@7n9Sundet8lY!F^(wCND03o?o~sxe4SDwH*w_e@-KZ4vv#Mjte9uhp zs?*5H16L;iTD2GvCs6nJh|{;_SK+MbVoD7TooCs*{M)LcJIAnTIdF2p441cBvUMxghD8`bx2yHx|EnmFg8}mvqCS?oX%unvit-ba$pF6a<%aB@e^{4mWb!Y0XyHa=FmAvy# z?pa+ilRr9p%2k{CYh2>G{humdG$UNpPsxj4ST^U_OB2<&=4mA#xJMXu{9as5{EjH#^_xII=S>_iws>PUj!KMqm8k zjW^!rei+^O&UU$ThP1L){u+G^gFc%LFYp246ig;tk=u}AaYVSfpmkbl0r;7EV{pYV zOkaTg4eSQ>;>|H-<5$l^zclC*%+tp7kvH1Glo9t7Uo!MGl>z}Uzx-q@JD7|121i>e zu9hwB!Mc*)*Zd7{LsvtpVcx~7TJz=peOoqm_lUXd(q)u$+Pe}(ZI>>)EW5K^%q`2- z_`aOsp2`e$PoFivzci`+(QrJ{-9T&UPr6c#iAmiXx9r=pd}(_}b^b7Z4WKO$OEI-N z4NNJbYE0XplBg!*9{7n=*O4E%ksV{^dilpntQM~?Tbj*@Jt+D^Rx|lTL)So8gYs5V z=CU$nU*5uQWA~#58@ne__Je(BSM~0O3#Pv6-#E%&jIlGqyAgnIBZ2sPkD@^Ys(8?f z5iMj%LhDgrPqabj@5p)@{LG5ddyBwlm}_}`zUa*rFT91jnd%>sBr|QaLbX@>PA4mpu|| z`JOjvJ}%$aa@?HsesA7><^13yO!tBJ)cRYXF@h}n2iT-qbA2uWIqi%l@;r37*I(k4 zaL9z3*+xg@+1&1v!e95KZn`P8KXv3tYR`mn=v%4ualQo}Q0+fsWxGgS!xTT!9iL$RJ-H7?}$2Me->i<%0YdYRUUb z@&}arqrAVs-6hYJSMSnDcC|WTq(m3-}l<7XpB#drK1!d1 z-Lp*fZHG%+QZsKzHB((~-Ddf9@xH69u+M`o7x(;0K|JDr!&r5xj*JTO5ma}dFt%yd z;g&=4U$X1gl!i~baBmPYL5W4E<77jvYhcGD!Dk};ic2wGN(3SYcY{)WecrECP>~a1 z5T<6CimgURbO}iHYzGi`q61M8Zw_cOfs4%A2PjR%lzX9+bNmLhs2-{vtrN#aD?6c` z5q$(s;C9OvPqHuCXfg#F9FB(fY&M$B_Vsz!QW zV9Ut)hRP=pwHOsg!XazG;9lL`eT}WrlSz+ItIe>_;P6?3Zi5lNg^>mpi(xWN#hE-f zB?*j1m>&Z1hAPWMss>dT`>X~z>mWN)`?&y zC`jC*NP&~1C&9X?^sgWZhUD4k(Pz<{h)av5htdyU`i-hA5Sb7dqc!2N5g7q}*4f6Ky^yKT2&S?Pw~I^qR(XW)9;j@S4quL_8ZIEp_=P+0qQUZ{340t7PEQbXVe`T@6Fz`8|*A_6{;LkdDoc@udJyBv#d({>=$j^mtn#*!ek(r%{!_HO>O1#6^ZfD^##A7@H z=M7OrPj&i+c%mKmX`S@esjSA^#qt8t>S_B~t8l#=#v>KgJZ_${vTAj(DN$Cx| z&u%jsYtp6JEvr{0=Z1R&7OpO3jOB#(-NnXIF6OaXI4!TU)Wy86h8j#Atj5b0@i)!J;v#2 z(&#jqtPX3i-qsjy$k~jV0dZbWsusMZI7m*!9iz+jYezyI6c6V!sg$}i8#3Hd|(argi2Z*lo!oLV5u z3F?wRJSUG&gOiK!Ys>|IC14-Jmu;tBCBP?8$Xv;Yf+LYjIZ18J& zN_9yA!PtP7H*oxEmUbhu^ce2OP_~}8nv(VPNt2bY&xV>}%yG!nRV<*_xuXR$baDvB zlyc}0+%*Wtip(>@_*2yb%m#KNc9BN(2u@QKQ}~%$RdI#SpXc!nS{D!-Xo3aMb4+w1S)997ilf721Wy!z05GTRf}2FB z@x|`1TrZ3%xBETO))J1#MuBDJ*`-pA602rdU>3iF??7yIHnLhbqP7L*PDJ0bp+I=Z zDe@V_I=f+edlR|t z&Wxaw59vbb&aRGTKksa8@9Jug8in|@F21%U|D*a=uepg8Q#nwF5}P_Z!bN&%xTDyN zXW~IsnRAr;6Z*)_@}g;5yAE=j8GRxLscT0+y5SX5KRDcZT>L7@);Gi}yCf^Ui8E2dA%& zh2E#5P9-{`m>)%?^Z2|@rKYM6E)k#TPCnT-FXrycP!`L%Y(eUV8&V4{OBC(4q*Bjn z?}sTZa>>92w}gU;Mpyru<@taw@~HrZO&vhKBfo4-zfpdO1&B~y>W1|EVuG&XweERs zLRTs3$}!|p9Q@!_A~O&{lA^qO|U>2Ei&q4d*y z%4*X#PhHV=uKG&r=zut@&1S1l1@g<+VBizMPzE2f&UXVxCv;uqPvIw87;!3<^68ts!_O`0O}Eh&wcX?&HuNe zf)MtiE0-Uo>oQw%9bfG_)^+Twmz|XF-2Qmv*&i$|-}SP{oa|xwolEbnyzvvd?c!DI zV)BXQbl<4_SqY{c{J$mz`#gnu_@OmZ&@9ss|6hu=IP2It3#9oVYYRiExZ^ZhFgnmE zqC<(O&x_MKeXfw`$QSTkhfoo|uxSvDH`plzTC`BKQ9IRn{5fO?!7hhLiDTkKeMOxf z+Lm)q-n(h8KV~rWM22?mzhe7fXTXw~vw1JQG_+A{aXmti?(&?MxY7sXI0jqV`$_GE!cyR8dYbJ-k5k4tQo>y+B*(q z#<6K@9gF*Cty;Be!(zu;gt`@+#(cH(MR>vV=qu=1_K$z;%pLs2jOdFnIaGfJ{C$x1 zIwiv<2=4&PR3v^_zOZ`07*UA(9{oKNd9{b7@bRSsZ2pk6RJu8^^>+tP-9d+qL-IE{Bjx2@Y2@;?MU?>MHo< zV|2H_&^4*&-<)ph_Igu3FFu_Ch_%5%_U=ryq+1eB8JV#vZoUC+`aAlz9iB*$DtR4x z=uYM=Vi^ZWnBWrF@Br==z+0T>=VaP4%k~{TcKDK*D4aYZ3|&?}uw{0fPtD(c<<)yu z))yPLAO7rZS3i_`Ml@!-kACK+Jqr?s{2K8%{3XRjt@?)PoA;G3U*A_}$q#ncPuV=# z7F}>f`k6nD`IJ6>4RQFN!6Ssw53Gs=MwvYp?@oP!@-T=o#ITeToQd9+Dj`w43@WAQ zEx#%w6SF+V{Zu}l>XNs?bA^OW`gx>vZRpPjG6syw z(Dzhu7Hc11C~LG5)MuUmfK>dkz#Nh{k;SQZPX^$Ia}2S(gifE=#W6@oKZNvy*A<=u z&NU`P1pK``To@JrP{WZiyNe|Pxoq#rmf`R&-!q3Av&{?ZQ{knnmW0oi5R58u-NH4( zWc1^geaD-=ildu+(^-O$nt&v~IQbg+(}pA0nC@vbQ9~e<4o9Qm$KWYITUZQB&9^iH zp}`x&vTAumGkKP6nS&RLRh0!C;mFFnmGsLe>g81ZlgQI`-QQYE>+6zVpkJ*`{=K#K z=`YepkVqG#KlOR#cKMy~3-j!bxuRq4JUd69Rcy8MF_1Ll-Tx{Ig8qbKI+y={838fE z`|pmB6-2uBTc9z|DxqPC`SYPR{Z}KOwdqEBKXoTgLxzPno=J<0+vEW#5 zC^Hiei6Mpsg~}&#dH8M+n})Gn{(j)o?wK3+UVhP{cAcm#t~qeijr&#xv)xPRZsqAu zF6qt%SM9&)rUPpV8nI>WMfQarZW68zc0|z%x@zxA%>%B7Y z-N)g>6(4@BuH3tAAmHuWa?Mm>((W~ryq-yGI=j1sqQ)N+f+T12Z7LF*fgb?%OYmtC}nNy5`SDUo#)kv)x)6Op*gGA%&stMwvj;48+w{F~ z|EYX)g3EC)0Hf8gRmcixPbj9W&t1i_4Cy-0AJczxC~;6VCYl|OdY+Ge#!}{3&=s~D z5E!M6N*I-ePLbWCgiaAtMd(z0_EG+6o?=`|N=7E@D8R62LjrxI%2NnZ3TGaN%${Nn z8CkgOg_jyp169Iet|F$DU^$t{zk^9#;9glWDc5W_<>stvZ*Iy4CWY5P-@vzIqjdd! z57##ts99KWac5(1Yc1!{$=@l%2S2YEjW@@q`^~W$ci6@vx}4;koI5(LZIKwU^qAM6 zknru8@-6AXTYKenpf^_BF+XfJB)X@?lT8isE1QN;Ovz{uzHCSDc%$*40ikxRe?UW7 z?2xa{%rK(QO+cY-$a3sdq|N|X6mA$m4VE^A)ii}X(xw0qFpY{;fXYHRg-78L9)Cwr919#qL~#^eT%2dF8~Pm6 zBJynHUO(WcYT#0A9%)yvkxQcQK0SdN5KXhI~$ zvg}!CLP$nCTcHLe;+%LzYXc(7AsAF-R}tp&>!R)O{N{9e{xh3J%xh#|Yz`Rhi#9EC z80)vaTn>~2Tn=yLm$%g$9Sb)vbQmkc-qsfDutkgf>Z5bz*>mYpv3!v?5NNgzn|<^e zEOjf1W2Lt4?zYmHY_Z&%&ECtBanv(w%{1WgEIux`4-L`pA0M&=yk3hkk5BNeXkUzB z4S3G5OoQb~BC8XNHyAjC|4EY#Y1qWT0uLV+a}@I2PlN`mFN8&iA)hr8Zcce(xzdbz zGnlhD_2^NvVUxj3{leVzCTG3RYW3ASH>KwaO&hxt>=w71-JR&(c+XM2#R90UVm&pl z--4`I;s#S8S3jqOau}vYiX*`F5Um;GJ}j{y9)EF3UYVv}lAoo=xxY|k<0C;L9A+U( zI$K_mrVlG}oIA&pZLolVUSI%KTL|O?QO%Z2ptUx37Ry;Oyt3XZr_Dpqdu{x9M~F(Q ztBuE9EbA4&WW7V(FSX2@6b?_C*Afba3M~jyv=l=xg^Dfy>4n5VM;*IDPbQ}3>L_K$ zxV)=`F!W@<_<(~*2+pNv9nfI1}n^3Tx=9IjomX6%EdD!ZDaS$ghhF5&%72J zI}oWaSqxdKKdHABmN33m=HmHv0G({V0XLgZc`RI0O&hib0J(5Vt}!eH=mvBHb2wsg zCiTKK0^c&&ngIZoL9N#%bQ#TLn)8M@^Z-%{nFXsHaY_%uBMEmOn8lA(B=lHKGVOm1 z`AtBa=ct#{@EYFWFk3@5vmItIuhqiUs?+qAyAm2+%W0_2VYb`NZktiZ;j%`n(Qv$m zvzrlv;5po`;b{(m1e?d_wwrVu)o=#xLC?rhmzHHLm8cW<7%e)Bfh;`Eq#p2v?+ zwoYf&&S*%z#A|d0lh@H!}^w`=KBd9WQe6MB$uU3ku_* z=qfC|T{SJiHGQYyJ55~TCt7W+pYdntfd@jA*5&$VpBL);!%EVdngGY$5=|tgt;)`r zB=4rjCe6sMYNeD58#?PZeSV`OsnLL#RnZFdh}XEZ2_Z-4$lR!e+Qljj5Y} z+QMiB(&W_q5wDgVu&Fto23P|%!967(QL;qj+5LNO+S`w4U*#AC_-=ZpTt;q7c3JM~ za{t}|+P}BIj2I{|-M*4Mi-lm6A;R+*=KTRi$pVc;XcVjFfcs+rMvx~6O#=0Eny|bI zamFxyxNflWheg~EE3T> zBF{?lqMPNgMQKD^V&zp+4dmfCom;tuc5^Eb14o`DJte2<59L46>?u|5jpt)y4f?r! zY)BSU7p5BIGF!OLBW~w7o--OO2r-)5>jBRwCY6Tky=Z#S zJ7vqk!w0ua@dl?~)cX(f$@|XyhS%yfTir3M#%gH|$C~Ooye6O7R%fFIvrQKaTJ;FA z;H%kZ@^;iE;zFCn3V*oGL<26p&8&x)2^G*lWSMO?QxG}Ed`V+~pz$o;bLi0J%QBf| zm)~&xp2gq_rUTh{Vy!Y;cM2WhjJo+K?{l|9iK9DXQ1np*>u$Y%m1gIhT^wC>xUHSz z3|Ag_?s%WPM&=3ImYV>k8FZ$PwB>At_j6amT1Sj9W}^_<2oYlqtD0TL1T%cw zI1m3n0?LIP^0|sRy$;Y);Vu|`;2$h?F8YcImLOFuD5tpFk{hz!I~F%-`1W-Nr_Pz& z+ZYc77H0=IS5Kt1n2N>Ip`5|ml{FcCLSDFP>w@0N1C$TT@TOk<7yObvv38M;5oP=#OYL z$jX5x z>f4ytt)`MhVEieI{J8+V-rgo(DPP%Yqt_20C+SgSxzOeN(nvepBp=zBLI&bidNp@? zEKqqVI5rmKN`bM;EAP|4oP6f1@5`2-QkqO6Ninr)6W;DgZeuJ|<)Z_r3B%Ha#vsJ7 zYk>}v9dTcTeBr?>>Jb<0U;$5f8=<)qX{zNPlb8DBLGku>N?m^oo_5i;AHMhQNgD8d z!T*3S@UHwA9i+?X55Dvz`7+woHEk)BTg}ehHML8pE&RQ9<0g)49H0E56}j zZe9A4OVT@bq}U(*MDk%B^ha2_A;0$>?v9s@W2bRp{0=+}F-JCkFehzjc^%uA$zAZP z%N0E9x)B5M~ zi&_5iIZY1?Ffl>+n7ToPnnN-+b}r)>L1Qjgu4a#@K8Kf3{t@{7W@C3W#O>Hp>G2d2 z=E#K!jSmFj+I0|2(C`eHg_((;?@&!^Wlm#OW41%KoSXD8zIhhrXW{?&W}q&OxyfyX_?^@ft(|l6jq_&; zwY9>``8QrH?=e_oEsKTjfLpKE;HB5Qv!zAJ7}im{x)yew`(xdXy?oAoNwLGMu-%J@ zPF;%q*N$Ufv9FMCkRM^w85>R00>JECx&oOQpQcaH@6&ha&(Md6!z#f`7}S8Z>H>m- zKWYQO*8%b~lg7vJJeTYB z76E$YewOAZQdXW~UBt#$wQI+7EfW7zqyM&#=*JV+YgGL%-o{P$c$-2F%Ef9^!a7I2 z-&_k%i!0pnzTWCGect4@8AJemev8(ye~(sA#X8SIQ(&*JS-|?qSTH5*;rO!Oyuwqb z+_rO1C<#}MGt?w&i}Q;3qsJ5Mqxi%BgP+cwrY+NE6>M1X639e7rSoe8-la~V$@frg z6a9>kq84Ah{dU?^`;Z^qEB{}Qo18X1)mz=B$KOPf8HY5nH}5uRpSZO%?qhuS-0}p53DYtu?Be3JH~N|c=T(V90bPHHz0E6({ZA#TJ;CH3%fWdA^6vm5 zybdLcT16$=7(*p~y#UEaflBb_1Q^J3LAtd$AYUh6=a17Zma&F+MtdsHFvtVp*mP=t&OvY^DUUk8iPeY zcdomk)@lnzUEllXy18|~vf6AY;;5}YVCAdJtOQw*lz-j71^>-c%}74wwkttH54{ZvvXDDjOpMDYd=j-@03lUWuG;1npHb1 zXKW9yeW6P^r;N*&xTjH;1zD!uZ0|9)l?GEXY()oFCRbOlWjHJ>QF?1!=fH200Ax$B zZjACw=^)--RXPalF`Yv{9v6IUvFJpRH1}@EG>0MvH`(iaW?CD=+-&xJS8sC}?>4$@ zdeNZWq}6lJpP#*@y zu33JQ4+D3xeZUCA;n?5-Y>>{i16c*>z3oBnxq$p&j!vq)L=g~D?I7f1VO(kz)C>y_ z`L{}N0v;`BW~QLoRsr6qo25g-5f}l8gnxKQh(|-So5kkYp=ewf63Z{wPMrlPiT$*& zWN1`4nOz;Nk7-|gQ5&m|uFjqmupr~QU>z0`#zseDQ}JvTgJF4#Y<&^)aln1a5(9Eh z{$ltj7zz~WHA>1hlKtVTP%u7LA9pKpi3ap81GNapV+O%yM{pPM2-Q!LV1BuFT%#Wk z?Cn>aBK>=Ro0`3Rc1n4BuyuYWGk;q30$e6zsp^%~>}xagTXA{Z-nDTn74Ovr-iwo1 zTa9c%iy+eH5N%br40;=$k>}7Z;NR*xj`LoxbNH97QN&&gQRet|<8vbZ#jYS&v{7cS z3)aZNn}ei(mR)UOV}^cH@bv*@RTdvLQ>@it3kux1T8fB2_siYlWiuzueC9PFGrjln zRiU3_XQh4edYYI2EczdnzfHd`zvnNh{xW5~G+To2W)8q`**3{NNG*g+EU*oUfz7H#j*8f_eAQ3OnO!p6>Mzs%K_!D`qcTekI=+Zfu>k;^?(c9lCbq-v4-p&C9d* zVBeWf6dsOa<=AxWNVJ^o4eW-O0saO|z9RUxm{;=NAsjp6+6fmrV#W6JSM=b0IIVar z+V>ZE#k`46$WeqNTAxEsZ{o##Te?#eJJW4>ZU(l65W7-s9UXGO8A>IR$wXRcpnGxD z(v%V+kr9>+=;7Y!YNM;#x_)HPA%Dy0(DOFr9#QJn8VrT2??g^^Cw*_=V0)_TjxXGS z;ttXR_Q0zi-*oWOCKSDO*VaTzSg`C;9PZqT!WS%C5JF;@hm-4rms}F0zcd(G3R+oR z7;ncC`94ao6w7eItFq~7J^}flZ756<$Foln>NRNKTF`+V1~-gs>|^quN@C#Dmdl_=L#WJ%72m9>N)_?Jpv~9{WdP6N?1a z#3!P^<2JBGX``c+?~lH2a$4bt)LWgV*I#FiasWJUN?rNI^nUT0)Z=ArmI-l)&7jzZ zN!VaXG+3ReK7LeEYtKM2m4BGJa@7};`!V!CJDB|P(yLQXgNGO%WP7!ZV5XI@Gxthx zfwIK|9Ke|QN;+2&dQQqlQtKVt5?_AuR z>u#JZ=+phvIt+bAyJqEbjl4F{Vn4d zsIl@vHD2vCIsJ%iDvwj5?a*YbKfVb5Z8;Zj?qj=ZW34uHZ^VmT&cMGAgnHe-=U=$j z!N17Vo4}udg76-)RB0Ig$aMjyvW4=y*}fx{7mY@hTa8AC!`mEkQiEOqyKSiKLI7HM z(Z6BD_R(VplTTNko#PsHaqq0b0)UekYs&v8Y+Cs?{a3r<4Wi-H{?awz54n42M9GCy zbuYsdZ0#gl@mfOLBH&8I(HU3fS+t;-hohGrgJ-LaDLSbVy9OYQBj0O zb!*4!U|cMq_RS%KflVogkf4bhkvw(4K@lGW`a_IT6n6mn+=}IcC*1g50+kf1vY3?9?WtZ)R?;$UUUKT zCabKlVUq;@bO;Q8J3P!qtP&{XDN+>@G*Kvef%BCSQsxqpF@d2ef>ZFWDaKBLKYGtS zS6?BY5sXMi@Qs8oDa}3e)?fA^QW@Nt#?ouac-uY zu~Y-ThqRxkvu(!sS8vo}f7Qp&4(_pL^w;O{V)EIIw&As8}AM zKl9hSqCqrd5Zo`$WgMf4@Ytd%@)Gw)@qZgU-qcGP>-a0tGF>1bCO3y{h? z-@I?Ilx_XWuFI}jGIM%!veePObh!bSxcQ~Q``+Aj?Pa^Z(wZGBC7Y+uT(Wt5z5Ll9 zDt0+rkB*hYZtN$=A`o_pEF8oju(3di-k+xT|Eo`4?|%~;cii?AJ@k}(+atRXi`WbF zdVl&&lj%(ei#<<2y?eL(M`gY+TkCnWL|^rP!{#b7gbkqttA$1JSr|fP2sS4oID>V? zqb$-6tSuiIMfU3`gx1KrLMi+%7KLL5f3aTwiXnz zY)H(Umqovzs(ElYcpM2YI7r0UR+VTTOMW>yj8V*$gnLIz<$EjHGLqEY%Em7&s~O(z zZk!I6?-fdwCvc}&9u|fW1jU|BYk-EB-d)JQgQiROKE}Lx68?1Ro+=Gq4Ib!KR%p_K> zDa*Pcz$RSBW{1j-u59Bg)|VI1Fw+0A*!yI4_ebnBUBWUA<(!UkMQB2-QKN5{v3UvfjnE?qJ)B!UGFE2u_v|qLIC3xJu-<@d zv>6Rk4A=pHg-#2Ax!4aG^+GIDgX}oCoPbB6#gT`~ww;Y4Cmerqq|dG@QH%wOs!|)PEg0oTzUz zJy?}vXD{KO!1%Bg%rVT4ykB7<=mss@J`HKZQ7p-h@Cla6fbF%FNF?{PC_uwtTe!}q z_XP1p@2}{ypNx&YL<{oko7gTW+zcw-ip3GMQm)$(5`|k!*wn9Xexs@@0s01Hj zODf}V;q$#J3!i&F7JgTs?`IKL1pP2A{*&j^&&#jTJPKzOSTF9KP=Vd0+s{|vopTj9 z?{8psd={8z26*A$rTHqRi2uTqHkJh8Q?|Ke)&~ND>Ka$J)*9haI3$rGK|em9{(Ipb zBy2krWzQ1Ij$*JqI$|-p2-$+zH271~t_T+c%Xh2{bP5NG=QcVycW`dUlS$``d6sSZ zJN`E|qen;=yW47e5udNx9Unl(T*mUVn9qj=^}yPSVnPl!Vrod2RpWk0E>yLy&dU-# zifAR1moSH!#fOw1wvQ)kJ65UTEc?W^XNu7VYZ;w{g+5Ss*lk(qAvT$qFG<~W*`wn= zgi}ecx7F1oW0^!4Pc<3zMqY2!>vRT-&E`SouSM4piPqJau%z2yciLR$q}lE81p){F z8=bYDhPHyoY0!BcUbpD8oApSlqWeC(V9@2P3ve9bs_syvzQOJ9-&@%ZQ!eOsbEmO4 zaMRT3$r(#GE}E84*Z7dWq0{K}Mr4uDMxEYb_XfkUwAsNUMayb(*z|S{Qo+0$ox8@R z*IHZ#tGA&p7HSCi-6n(8WHDxoI}^#KSU6nUY_PlZMw2;`xMA;r{3?5%vb~4mqf@aD zq#)L%j$pLydAI`6IOcs z4PVB{9%po5gEWsZb2#75^uGqU_s$+_y zXa4FnOClNXlEm%F%PU+@?xL2vqU$`ZktIW`=l9tA9Md-J*>`bE7kzu_U~fy#9O_@+ zm73Y^H8)3EOZ{zoHMgG{jNWCqC_AOp*3fMBW@o0m*7w(&vq>yqR^`RL{5z0WK3HLF zO>+d&82}P&2#;$!%UcHX<@GA37PEdY_}R`Gv4sfSnmTZ4UI=x5fH`jq8Gq z@(YEo{G7WM+&N!9TcSjiKPA8M;utnh{^aBF@lxji! zJ3Bp|L-IOt$N@Gv2uVb+F@}&pAc<2z5(-h+NdXT@@c<9xA(bLkp(+%W2Ojc}%Hb+2 z`MP%`*}wzQG4pp%-~IRB$A2_<9MbtWML0zKpv|z(jzb2r4cOU0J5c)mc>f6Gr=aN5 z4y8x*XbkNbx<~(gVfd|W>cVXccl12aM^pQr*$?^3rF%w3LbXe8JounkwH`c3=ihm! zxw(1i4H$97Pkk+4K^~%v;yhg(rQ=_`FSoQQOr%3B5JKP5(wtaVz9!euJz`H2-2e(X z0aA!PvQ+f#6XwpPpfk+YhlRkC_!YSP;GoW*V~aA*>Y;b)lFxHj3jPuw?6eC(I3@g)8dO zoyvD#dMl_0RXk(R%6`{2om6m^BRuekVc4=QCKB9f$ii#{fnGp4O zI|bDMxt4e>?1o-NFJ2{A`t9*;p|SRs5tuYVGY8Uo_b+)YS_4`x!u0i9yV^ywJ+`4GC+TQb|ITRn{B3CwyUSJyR{{>4#DZEUDndM z(W&;_O~x?4m3K7bEy8bpZXptSG&eXjnC*g?a15LWvCe;8+FcCGU4C6_0l2)jzYp>r zo6&waGs2oz3Xj5AANKt)HZ9p4V)+aG+Y7i5VpHdZu8kLgk|Eg1p2|j-XBv zr0|Rg8}!$*ow633IAQIH9uhXQ*TOPa-NYy?4`ZUkxf(|N4(v-oz;J2<3AFxQ^v8+3 zqqxfWCxSQa$-VbpZt3FrmoL2f%F;iDgHo@YzwpX=dhFtO{)n5O_|MA#YzF{+ZgTNA zlM|;-O-#P{+sX0Mr^h8NeH`Oi@N9Dpy^xUFiFW$1{F&H4O5Or+2#X`=EDVh96pOxi zYaMF)fLPl1x;wWi1A~K`^V>SRkN2%t@Uy<}_`0EVdT3oJ2067qwEi6?qL$^%X7G?6WNoC979lMmFj?vB`IcJNrqqE^NxO=7@dKCv74ub}P zAi{put^w?u1dRlneyzO2qaWu55aR=*3&a7(E|A#8LT>V;R%WogW1eK<7|qTK4eo)>;yr^WdwNbTgDC9mD&rV^1A2i1MMJmAxm7M% z(b9qy^^bS+?=JsQ>oNo{M94P$t{xLlte*oo1lpbq;4q+U07n3K25=PlYzW{O{FMMs zVMLDxusDlQfi)BnsWoF~REI-23p)|OVfa53z!AVt2XGX9_fi1I;QwX-r*LcGI{}<- z*i55b^9+q!?)KYR$v0JJ#k~R?V2*2Wt9N8*c*SoIvzViLOz$n!dfmCwcF!yJ79C#g zo#K|qOq2K84xeS3H&pgK`?_u0ih=)-#;dsDQA2GEJUQeSUF>OZ$8C)z+<0iB#?D84 z|8R^0h~XfvA=BsuFJK7zbsrA52jMIkRz;o+x7+r@>PZbx7dPIOkhh25BHTrpv$*+r zN~S47oA~AMvE{c}_=u7Z;l51~%Z8*2E_FjpQIFN|L$YR7Uf?ttM&YQ^@U4GZ=TSU&E8wNhLt02Wpc^%67)p=q&^ zHsLnx0o;bYl?I^^lZOnbFuOWTxN!ytu{*&~9R(ls8oCy@^j(h|vV~OU1WnSXa7XqH zbR+GiDcVDO>C?22Zo;kEx6ppNl|Dna(e3nEx`XbNLeigyOzB;8H*V8DMAI}whe@RZ zX{6)QxgwRQOa{$Tg-oiFMVxFp0!s-ldF0a^1g?%!jgHY5=}UB+zD)PfSFm;Y8hxGa zrTgdvouvEe8}v>37JZu@pzqLw^j*lWexDZTA^HJ038&~ZJwj*bQ94VH(c|<(dV+pL zKc*+?Df$ULP0!Fz>1XtFTxIqP>{p+q=jeI*CH;ziO)tP6=ZmyRFEvC}lQ~{ob+ocE z$5N`}@Oh=cy>bBReBKH`lNG&mJ%D4B%3ih(yHynu+9KQ)9wKz^@(?mk)8#(Cov1r~ zJ62$7g*lM|hDxNsO&t$jNr*>Pbt=h%W3Zy8x(pCkbwFMm(7el>aKSGnG*rXVRVS*I znN~^4r)jueBcM5lTe5sB4pFwu@)udI)UwOB8#TkxOs2S|>XtLY;Ts;BM0NC-W*W9# zi)tpR33QcVyM`NOKpH2?=1{>D ztD#gTcbOgrm@XnWrC?a9Q%g!%G)$9(bi)BTes7bs{ALNL>P^C@w=! zc&U=;Y)7@UvIIph86pIFh*xdZVMzg_*-I=dAlnD_JM4(hTrUHUvR_bC%pu&0mP}q? z@sgt!3e1V(ZI`2EWa`Ds$PhJ;lzEk{DRaj-hS@+xI;d+DDR3`pSOtDGVpv7qXt=Jw z+^T_t+n8|lrur9$)txZ|X&72;784Y!HC0$aEhuvs-Ufz8xWZ}){7cLVo9cYZ6oXhP z_+}v^o0T2)m}9ACM7SEo=ux<%GvGy&bojs#=}Z9qRZ+u;7zfF!YKRs9)(a>t#;a0q zh=hOzWk^<83AN`Ii;H1^sL3k&2aRB{Sk(~t(^%z>r#kh47ODCkOIcj8*gQ(ZA{O^p z0<$eIsv2wLPD!#~IGSkaqDe3T*WsJg)wkIyqFCWeN`q7JjoS_)4t zFyCTpBv|AQ2B_`3<@B<9oLOG zexa6jfuzh*ibx?7z~~l`;Ft@{(#m)_=<b%7 literal 0 HcmV?d00001 diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 5fe9f43d192fd..357a3488de125 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -46,6 +46,9 @@ enum Settings { PANEL_POSITION = 'workbench.panel.defaultLocation', ZEN_MODE_RESTORE = 'zenMode.restore', + + // TODO @misolori update this when finished + OCTICONS_UPDATE_ENABLED = 'workbench.octiconsUpdate.enabled', } enum Storage { @@ -173,6 +176,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi wasSideBarVisible: false, wasPanelVisible: false, transitionDisposables: new DisposableStore() + }, + + // TODO @misolori update this when finished + octiconsUpdate: { + enabled: false } }; @@ -314,6 +322,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const newMenubarVisibility = this.configurationService.getValue(Settings.MENUBAR_VISIBLE); this.setMenubarVisibility(newMenubarVisibility, !!skipLayout); + // TODO @misolori update this when finished + const newOcticonsUpdate = this.configurationService.getValue(Settings.OCTICONS_UPDATE_ENABLED); + this.setOcticonsUpdate(newOcticonsUpdate); + } private setSideBarPosition(position: Position): void { @@ -426,6 +438,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Zen mode enablement this.state.zenMode.restore = this.storageService.getBoolean(Storage.ZEN_MODE_ENABLED, StorageScope.WORKSPACE, false) && this.configurationService.getValue(Settings.ZEN_MODE_RESTORE); + // TODO @misolori update this when finished + this.state.octiconsUpdate.enabled = this.configurationService.getValue(Settings.OCTICONS_UPDATE_ENABLED); + this.setOcticonsUpdate(this.state.octiconsUpdate.enabled); + } private resolveEditorsToOpen(fileService: IFileService): Promise | IResourceEditor[] { @@ -729,6 +745,19 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } + // TODO @misolori update this when finished + private setOcticonsUpdate(enabled: boolean): void { + this.state.octiconsUpdate.enabled = enabled; + + // Update DOM + if (enabled) { + document.body.dataset.octiconsUpdate = 'enabled'; + } else { + document.body.dataset.octiconsUpdate = ''; + } + + } + protected createWorkbenchLayout(instantiationService: IInstantiationService): void { const titleBar = this.getPart(Parts.TITLEBAR_PART); const editorPart = this.getPart(Parts.EDITOR_PART); From d343bb0ca6fad99a06cac83bd68de951a6f118c7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 09:34:59 -0700 Subject: [PATCH 249/613] Update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 09b59b17c095f..77a001fc40431 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "a8a3be6f7445a6f3ce736a0ba3cbca717a2372cb", + "distro": "4365d6c41f9a5e75730c78c937b68583cf6f0e4a", "author": { "name": "Microsoft Corporation" }, @@ -158,4 +158,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} \ No newline at end of file +} From af224bcae1660bdceb3276a0200680730646e253 Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Thu, 15 Aug 2019 18:35:07 +0100 Subject: [PATCH 250/613] Fix duplicated '(read-only)' suffix on titlebar name --- .../contrib/files/common/editors/fileEditorInput.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 2d25923a2c273..3aae29a21be2e 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -203,17 +203,20 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { switch (verbosity) { case Verbosity.SHORT: title = this.shortTitle; + // already decorated by getName() break; default: case Verbosity.MEDIUM: title = this.mediumTitle; + title = this.decorateLabel(title); break; case Verbosity.LONG: title = this.longTitle; + title = this.decorateLabel(title); break; } - return this.decorateLabel(title); + return title; } private decorateLabel(label: string): string { From 22edad13ff5701fc1d5e07f0a7311881709c55bf Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 11:28:53 -0700 Subject: [PATCH 251/613] Move puppeteer to dev deps --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 77c377f7bde48..294df311260e2 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "node-pty": "0.9.0-beta19", "nsfw": "1.2.5", "onigasm-umd": "^2.2.2", - "puppeteer": "^1.17.0", "semver-umd": "^5.5.3", "spdlog": "^0.9.0", "sudo-prompt": "9.0.0", @@ -125,6 +124,7 @@ "optimist": "0.3.5", "p-all": "^1.0.0", "pump": "^1.0.1", + "puppeteer": "^1.19.0", "queue": "3.0.6", "rcedit": "^1.1.0", "rimraf": "^2.2.8", diff --git a/yarn.lock b/yarn.lock index 0c86a6fc52416..7f3c5365e14a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7368,10 +7368,10 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.17.0.tgz#371957d227a2f450fa74b78e78a2dadb2be7f14f" - integrity sha512-3EXZSximCzxuVKpIHtyec8Wm2dWZn1fc5tQi34qWfiUgubEVYHjUvr0GOJojqf3mifI6oyKnCdrGxaOI+lWReA== +puppeteer@^1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.19.0.tgz#e3b7b448c2c97933517078d7a2c53687361bebea" + integrity sha512-2S6E6ygpoqcECaagDbBopoSOPDv0pAZvTbnBgUY+6hq0/XDFDOLEMNlHF/SKJlzcaZ9ckiKjKDuueWI3FN/WXw== dependencies: debug "^4.1.0" extract-zip "^1.6.6" From 562e4cb004f650e84bca197207acd3a184954abc Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 11:35:39 -0700 Subject: [PATCH 252/613] Run smoke tests for darwin/linux in CI --- build/azure-pipelines/darwin/product-build-darwin.yml | 9 +++++++++ build/azure-pipelines/linux/product-build-linux.yml | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index c088bb8b4577f..de36023dfe827 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -107,6 +107,15 @@ steps: displayName: Run integration tests condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) +- script: | + set -e + cd test/smoke + yarn compile + cd - + yarn smoketest --web --headless +continueOnError: true +displayName: Smoke tests + - script: | set -e pushd ../VSCode-darwin && zip -r -X -y ../VSCode-darwin.zip * && popd diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index a6cc1c349659a..993dec7ce053e 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -111,6 +111,15 @@ steps: displayName: Run integration tests condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) +- script: | + set -e + cd test/smoke + yarn compile + cd - + yarn smoketest --web --headless +continueOnError: true +displayName: Smoke tests + - script: | set -e AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ From cfb9c21361737515fe9cbe5df058708f16bb49f3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 11:36:53 -0700 Subject: [PATCH 253/613] Make display name consistent --- build/azure-pipelines/darwin/product-build-darwin.yml | 3 ++- build/azure-pipelines/linux/product-build-linux.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index de36023dfe827..b64b17332efe0 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -114,7 +114,8 @@ steps: cd - yarn smoketest --web --headless continueOnError: true -displayName: Smoke tests +displayName: Run smoke tests +condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 993dec7ce053e..0404e9058140f 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -118,7 +118,8 @@ steps: cd - yarn smoketest --web --headless continueOnError: true -displayName: Smoke tests +displayName: Run smoke tests +condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e From 184f5aaeddd2d04b9a203e41c3e788862ad58e64 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 11:37:40 -0700 Subject: [PATCH 254/613] Fix indent --- .../darwin/product-build-darwin.yml | 16 ++++++++-------- .../linux/product-build-linux.yml | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index b64b17332efe0..175e3d0eb08a2 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -108,14 +108,14 @@ steps: condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | - set -e - cd test/smoke - yarn compile - cd - - yarn smoketest --web --headless -continueOnError: true -displayName: Run smoke tests -condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + set -e + cd test/smoke + yarn compile + cd - + yarn smoketest --web --headless + continueOnError: true + displayName: Run smoke tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 0404e9058140f..48261e0020c8b 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -112,14 +112,14 @@ steps: condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | - set -e - cd test/smoke - yarn compile - cd - - yarn smoketest --web --headless -continueOnError: true -displayName: Run smoke tests -condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + set -e + cd test/smoke + yarn compile + cd - + yarn smoketest --web --headless + continueOnError: true + displayName: Run smoke tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e From c587b3887e943ac6414e3e1e59f199c0e13fbbfc Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 15 Aug 2019 21:40:41 +0200 Subject: [PATCH 255/613] Improve token regex --- .../services/environment/browser/environmentService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index c8e2227fc0d91..318caeb3d77f2 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -203,7 +203,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment } private getConnectionToken(str: string): string | undefined { - const m = str.match(/[#&]tkn=([^&]+)/); + const m = str.match(/[#&?]tkn=([^&]+)/); return m ? m[1] : undefined; } } From 08d5f6dcc14d4836b49b02f0b3086f13fb0d1bf9 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 15 Aug 2019 21:57:26 +0200 Subject: [PATCH 256/613] Add connectionToken --- src/vs/platform/remote/common/remoteAgentEnvironment.ts | 3 ++- .../services/remote/common/remoteAgentEnvironmentChannel.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/remote/common/remoteAgentEnvironment.ts b/src/vs/platform/remote/common/remoteAgentEnvironment.ts index c1815d801f31e..839a6cb5c19cf 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; @@ -24,4 +25,4 @@ export interface IRemoteAgentEnvironment { export interface RemoteAgentConnectionContext { remoteAuthority: string; clientId: string; -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts index 9f0fb09442ec7..ee89f26f39400 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts @@ -18,6 +18,7 @@ export interface IGetEnvironmentDataArguments { export interface IRemoteAgentEnvironmentDTO { pid: number; + connectionToken: string; appRoot: UriComponents; appSettingsHome: UriComponents; settingsPath: UriComponents; @@ -45,6 +46,7 @@ export class RemoteExtensionEnvironmentChannelClient { return { pid: data.pid, + connectionToken: data.connectionToken, appRoot: URI.revive(data.appRoot), appSettingsHome: URI.revive(data.appSettingsHome), settingsPath: URI.revive(data.settingsPath), From bce21c1f5d53e6ea5f438920109a42b61e2b8e1a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 15 Aug 2019 21:59:56 +0200 Subject: [PATCH 257/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 294df311260e2..339774e76e54b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "4365d6c41f9a5e75730c78c937b68583cf6f0e4a", + "distro": "9d5ee3146c410b8afdec64a3a8731eb8ca64f3e7", "author": { "name": "Microsoft Corporation" }, From 3922fe8d604873953cfed6827fe49ad7be35462d Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 15 Aug 2019 22:48:47 +0200 Subject: [PATCH 258/613] Update search stop icon --- src/vs/workbench/contrib/search/browser/media/stop-dark.svg | 2 +- src/vs/workbench/contrib/search/browser/media/stop-hc.svg | 2 +- src/vs/workbench/contrib/search/browser/media/stop-light.svg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/media/stop-dark.svg b/src/vs/workbench/contrib/search/browser/media/stop-dark.svg index 7e6319104daf0..890af29835444 100644 --- a/src/vs/workbench/contrib/search/browser/media/stop-dark.svg +++ b/src/vs/workbench/contrib/search/browser/media/stop-dark.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/workbench/contrib/search/browser/media/stop-hc.svg b/src/vs/workbench/contrib/search/browser/media/stop-hc.svg index a879a194c7a9f..1c88dfb60a7b2 100644 --- a/src/vs/workbench/contrib/search/browser/media/stop-hc.svg +++ b/src/vs/workbench/contrib/search/browser/media/stop-hc.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/workbench/contrib/search/browser/media/stop-light.svg b/src/vs/workbench/contrib/search/browser/media/stop-light.svg index 10d05f5d8ab6d..7e41aeff58966 100644 --- a/src/vs/workbench/contrib/search/browser/media/stop-light.svg +++ b/src/vs/workbench/contrib/search/browser/media/stop-light.svg @@ -1,3 +1,3 @@ - + From 324da04f350f5cb0ceea7f3173f6c4cd12015ce9 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 15 Aug 2019 22:52:38 +0200 Subject: [PATCH 259/613] =?UTF-8?q?Update=20checkmark=20so=20they=20look?= =?UTF-8?q?=20more=20like=20a=20=E2=9C=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/git/resources/icons/dark/check.svg | 2 +- extensions/git/resources/icons/light/check.svg | 2 +- src/vs/base/browser/ui/menu/check.svg | 2 +- src/vs/workbench/contrib/files/browser/media/check-dark.svg | 2 +- src/vs/workbench/contrib/files/browser/media/check-light.svg | 2 +- .../workbench/contrib/preferences/browser/media/check-dark.svg | 2 +- .../workbench/contrib/preferences/browser/media/check-light.svg | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/extensions/git/resources/icons/dark/check.svg b/extensions/git/resources/icons/dark/check.svg index 865cc83c347af..2d16f39007839 100644 --- a/extensions/git/resources/icons/dark/check.svg +++ b/extensions/git/resources/icons/dark/check.svg @@ -1,3 +1,3 @@ - + diff --git a/extensions/git/resources/icons/light/check.svg b/extensions/git/resources/icons/light/check.svg index e1a546660ed15..a9f8aa131b59f 100644 --- a/extensions/git/resources/icons/light/check.svg +++ b/extensions/git/resources/icons/light/check.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/base/browser/ui/menu/check.svg b/src/vs/base/browser/ui/menu/check.svg index 865cc83c347af..cea818ef5968a 100644 --- a/src/vs/base/browser/ui/menu/check.svg +++ b/src/vs/base/browser/ui/menu/check.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/workbench/contrib/files/browser/media/check-dark.svg b/src/vs/workbench/contrib/files/browser/media/check-dark.svg index 865cc83c347af..51674695e1fde 100644 --- a/src/vs/workbench/contrib/files/browser/media/check-dark.svg +++ b/src/vs/workbench/contrib/files/browser/media/check-dark.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/workbench/contrib/files/browser/media/check-light.svg b/src/vs/workbench/contrib/files/browser/media/check-light.svg index e1a546660ed15..7b1da6d720863 100644 --- a/src/vs/workbench/contrib/files/browser/media/check-light.svg +++ b/src/vs/workbench/contrib/files/browser/media/check-light.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/workbench/contrib/preferences/browser/media/check-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/check-dark.svg index 865cc83c347af..51674695e1fde 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/check-dark.svg +++ b/src/vs/workbench/contrib/preferences/browser/media/check-dark.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/workbench/contrib/preferences/browser/media/check-light.svg b/src/vs/workbench/contrib/preferences/browser/media/check-light.svg index e1a546660ed15..7b1da6d720863 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/check-light.svg +++ b/src/vs/workbench/contrib/preferences/browser/media/check-light.svg @@ -1,3 +1,3 @@ - + From 6624dbbce9a3f2c2f4d8d294352c3c27bac34255 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 15 Aug 2019 23:11:40 +0200 Subject: [PATCH 260/613] introduce RemoteAuthorities --- src/vs/base/common/network.ts | 23 +++++++++++++++++++ .../remoteAuthorityResolverService.ts | 2 ++ .../common/remoteAgentEnvironmentChannel.ts | 3 +++ 3 files changed, 28 insertions(+) diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 46d2933a05e8c..f3006bdf3e1a6 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -49,3 +49,26 @@ export namespace Schemas { export const userData: string = 'vscode-userdata'; } + +class RemoteAuthoritiesImpl { + private readonly _hosts: { [authority: string]: string; }; + private readonly _ports: { [authority: string]: number; }; + private readonly _connectionTokens: { [authority: string]: string; }; + + constructor() { + this._hosts = Object.create(null); + this._ports = Object.create(null); + this._connectionTokens = Object.create(null); + } + + public set(authority: string, host: string, port: number): void { + this._hosts[authority] = host; + this._ports[authority] = port; + } + + public setConnectionToken(authority: string, connectionToken: string): void { + this._connectionTokens[authority] = connectionToken; + } +} + +export const RemoteAuthorities = new RemoteAuthoritiesImpl(); diff --git a/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts index 3cd1da3e018fc..9ab777f67bd04 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/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts index ee89f26f39400..fe15f2b644e0a 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts @@ -9,6 +9,7 @@ import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; +import { RemoteAuthorities } from 'vs/base/common/network'; export interface IGetEnvironmentDataArguments { language: string; @@ -44,6 +45,8 @@ export class RemoteExtensionEnvironmentChannelClient { const data = await this.channel.call('getEnvironmentData', args); + RemoteAuthorities.setConnectionToken(remoteAuthority, data.connectionToken); + return { pid: data.pid, connectionToken: data.connectionToken, From 454d16efd7f6e251347687dc0f74cbf2e3fbfea2 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 15 Aug 2019 14:34:00 -0700 Subject: [PATCH 261/613] remote explorer and contribution under proposed api --- src/vs/workbench/api/browser/viewsExtensionPoint.ts | 7 ++++++- src/vs/workbench/contrib/remote/browser/remote.ts | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 95a9aff3298bc..b12e7ec2bf6be 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/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index ea530271414bb..eb7bc581f9d5f 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -386,6 +386,10 @@ export class RemoteViewlet extends ViewContainerViewlet implements IViewModel { } private _handleRemoteInfoExtensionPoint(extension: IExtensionPointUser, helpInformation: HelpInformation[]) { + if (!extension.description.enableProposedApi) { + return; + } + if (!extension.value.documentation && !extension.value.feedback && !extension.value.getStarted && !extension.value.issues) { return; } From b32052728015c8712819ccc8b28b4bf9880179a7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 14:38:20 -0700 Subject: [PATCH 262/613] Disable smoke tests on Linux Puppeteer needs special user setup in order to launch --- build/azure-pipelines/linux/product-build-linux.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 48261e0020c8b..a6cc1c349659a 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -111,16 +111,6 @@ steps: displayName: Run integration tests condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - cd test/smoke - yarn compile - cd - - yarn smoketest --web --headless - continueOnError: true - displayName: Run smoke tests - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - script: | set -e AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ From b66f0d96d4c8729cb1fb041ddee2a2edb7203fcc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 15 Aug 2019 11:47:21 -0700 Subject: [PATCH 263/613] Strict init #78168 --- .../contrib/codeEditor/browser/find/simpleFindWidget.ts | 9 +++++---- .../workbench/contrib/webview/browser/webviewElement.ts | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index 1270b1422286b..8ba218619a29d 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -29,13 +29,14 @@ export abstract class SimpleFindWidget extends Widget { private readonly _findInput: FindInput; private readonly _domNode: HTMLElement; private readonly _innerDomNode: HTMLElement; - private _isVisible: boolean = false; private readonly _focusTracker: dom.IFocusTracker; private readonly _findInputFocusTracker: dom.IFocusTracker; private readonly _updateHistoryDelayer: Delayer; - private prevBtn: SimpleButton; - private nextBtn: SimpleButton; - private foundMatch: boolean; + private readonly prevBtn: SimpleButton; + private readonly nextBtn: SimpleButton; + + private _isVisible: boolean = false; + private foundMatch: boolean = false; constructor( @IContextViewService private readonly _contextViewService: IContextViewService, diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index aa4473313d4f9..6eb2d99a7b0c3 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -213,7 +213,7 @@ export class IFrameWebview extends Disposable implements Webview { } } - initialScrollProgress: number; + initialScrollProgress: number = 0; private readonly _onDidFocus = this._register(new Emitter()); public readonly onDidFocus = this._onDidFocus.event; From 3956654054a2cd8c90514769c4461524929dfcc5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 15 Aug 2019 11:49:09 -0700 Subject: [PATCH 264/613] Strict init and mark events readonly #78168 --- src/vs/platform/windows/common/windows.ts | 12 ++++++------ src/vs/platform/windows/common/windowsIpc.ts | 16 ++++++++-------- src/vs/workbench/test/workbenchTestServices.ts | 12 ++++++------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index a1f50a5a715db..7ac046630817f 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 6fbad27a5497b..3cdf71e2f4ab2 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/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 576df9cb63c53..d182e4ca73c43 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -1340,12 +1340,12 @@ export class TestWindowsService implements IWindowsService { public windowCount = 1; - onWindowOpen: Event; - onWindowFocus: Event; - onWindowBlur: Event; - onWindowMaximize: Event; - onWindowUnmaximize: Event; - onRecentlyOpenedChange: Event; + readonly onWindowOpen: Event = Event.None; + readonly onWindowFocus: Event = Event.None; + readonly onWindowBlur: Event = Event.None; + readonly onWindowMaximize: Event = Event.None; + readonly onWindowUnmaximize: Event = Event.None; + readonly onRecentlyOpenedChange: Event = Event.None; isFocused(_windowId: number): Promise { return Promise.resolve(false); From 883ae9069a23b9125d3afc47f51da407fa7baf2f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 15 Aug 2019 11:55:29 -0700 Subject: [PATCH 265/613] Remove extra null checks in coalesce The type system should catch these now --- src/vs/base/common/arrays.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 7aa4259771428..cb3c94b3d1580 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -293,12 +293,9 @@ function topStep(array: ReadonlyArray, compare: (a: T, b: T) => number, re } /** - * @returns a new array with all falsy values removed. The original array IS NOT modified. + * @returns New array with all falsy values removed. The original array IS NOT modified. */ export function coalesce(array: ReadonlyArray): T[] { - if (!array) { - return array; - } return array.filter(e => !!e); } @@ -306,9 +303,6 @@ export function coalesce(array: ReadonlyArray): T[] { * Remove all falsey values from `array`. The original array IS modified. */ export function coalesceInPlace(array: Array): void { - if (!array) { - return; - } let to = 0; for (let i = 0; i < array.length; i++) { if (!!array[i]) { From 1bbf3b3fa693afa1ae69adff22ea65317396aeda Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 15 Aug 2019 15:15:36 -0700 Subject: [PATCH 266/613] Add telemetry+warning for webviews that don't have a content security policy Fixes #79248 --- .../src/features/previewContentProvider.ts | 2 +- .../contrib/webview/browser/pre/main.js | 6 ++++ .../electron-browser/webviewElement.ts | 34 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/features/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts index 17b6d4f4ebb2e..9b70fe3beb371 100644 --- a/extensions/markdown-language-features/src/features/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/features/previewContentProvider.ts @@ -209,7 +209,7 @@ export class MarkdownContentProvider { return ``; case MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent: - return ''; + return ''; case MarkdownPreviewSecurityLevel.Strict: default: diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 6568b02f01199..0b25585c61197 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -285,6 +285,12 @@ applyStyles(newDocument, newDocument.body); + // Check for CSP + const csp = newDocument.querySelector('meta[http-equiv="Content-Security-Policy"]'); + if (!csp) { + host.postMessage('no-csp-found'); + } + // set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off // and DOCTYPE is needed in the iframe to ensure that the user agent stylesheet is correctly overridden return '\n' + newDocument.documentElement.outerHTML; diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 20daf1ec4bc04..4b06dd21bb1c6 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -13,9 +13,11 @@ import { endsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import * as modes from 'vs/editor/common/modes'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing'; @@ -284,6 +286,8 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { @IFileService fileService: IFileService, @ITunnelService tunnelService: ITunnelService, @IConfigurationService private readonly _configurationService: IConfigurationService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IEnvironmentService private readonly _environementService: IEnvironmentService, ) { super(); this.content = { @@ -412,6 +416,10 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { case 'did-blur': this.handleFocusChange(false); return; + + case 'no-csp-found': + this.handleNoCspFound(); + return; } })); this._register(addDisposableListener(this._webview, 'devtools-opened', () => { @@ -546,6 +554,32 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { } } + private _hasAlertedAboutMissingCsp = false; + + private handleNoCspFound(): void { + if (this._hasAlertedAboutMissingCsp) { + return; + } + this._hasAlertedAboutMissingCsp = true; + + if (this._options.extension && this._options.extension.id) { + if (this._environementService.isExtensionDevelopment) { + console.warn(`${this._options.extension.id.value} created a webview without a content security policy: https://aka.ms/vscode-webview-missing-csp`); + } + + type TelemetryClassification = { + extension?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' } + }; + type TelemetryData = { + extension?: string, + }; + + this._telemetryService.publicLog2('webviewMissingCsp', { + extension: this._options.extension.id.value + }); + } + } + public sendMessage(data: any): void { this._send('message', data); } From 8eb0097c39a7bc8a216e87d4075e67959606c2bb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 15 Aug 2019 15:15:50 -0700 Subject: [PATCH 267/613] Fixing comment --- src/vs/vscode.proposed.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 328fcae58961a..5284711d6e1a9 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1161,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; } From 86819a886c540c45a97d1a67cdc2fc31ee899a72 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 16:35:13 -0700 Subject: [PATCH 268/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 339774e76e54b..293e964893348 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "9d5ee3146c410b8afdec64a3a8731eb8ca64f3e7", + "distro": "294c6a19b85a4ec824a66dbe70c51a2858961b12", "author": { "name": "Microsoft Corporation" }, From c44c802a1dadb05f2ed7d315e05ba352185c2954 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 15 Aug 2019 17:07:40 -0700 Subject: [PATCH 269/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 293e964893348..ee393d1aaf3c9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "294c6a19b85a4ec824a66dbe70c51a2858961b12", + "distro": "13f407368a517ace39c2721a66a951ba4eebce20", "author": { "name": "Microsoft Corporation" }, From 55c5bbfddd6ab1974c92fcefc24b980b85b50a10 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 15 Aug 2019 17:59:38 -0700 Subject: [PATCH 270/613] Don't dispose of added object in already disposed of case Fixes #77192 See #77192 for discussion --- src/vs/base/common/lifecycle.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 6be438cee416b..44cfdada8ab1b 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -127,8 +127,7 @@ export class DisposableStore implements IDisposable { markTracked(t); if (this._isDisposed) { - console.warn(new Error('Registering disposable on object that has already been disposed of').stack); - t.dispose(); + console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack); } else { this._toDispose.add(t); } From 35c97ea91de2bd339d907f1f01ff4713c235fd52 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 15 Aug 2019 18:02:28 -0700 Subject: [PATCH 271/613] Mark readonly --- .../src/features/updatePathsOnRename.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index c4c2e9ca13565..cbb0d251adf17 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -40,7 +40,7 @@ enum UpdateImportsOnFileMoveSetting { } class UpdateImportsOnFileRenameHandler extends Disposable { - public static minVersion = API.v300; + public static readonly minVersion = API.v300; public constructor( private readonly client: ITypeScriptServiceClient, @@ -237,4 +237,4 @@ export function register( ) { return new VersionDependentRegistration(client, UpdateImportsOnFileRenameHandler.minVersion, () => new UpdateImportsOnFileRenameHandler(client, fileConfigurationManager, handles)); -} \ No newline at end of file +} From d9aba4fa7f4e3ab0039458c829bd7d8ade38b11c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 15 Aug 2019 18:03:55 -0700 Subject: [PATCH 272/613] Use const enums --- .../src/features/updatePathsOnRename.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index cbb0d251adf17..102658ecf0121 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -33,7 +33,7 @@ function isDirectory(path: string): Promise { }); } -enum UpdateImportsOnFileMoveSetting { +const enum UpdateImportsOnFileMoveSetting { Prompt = 'prompt', Always = 'always', Never = 'never', @@ -127,7 +127,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable { newResource: vscode.Uri, newDocument: vscode.TextDocument ): Promise { - enum Choice { + const enum Choice { None = 0, Accept = 1, Reject = 2, From 967b43392e2afb55659c062230f16ce15db2926d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 15 Aug 2019 18:10:07 -0700 Subject: [PATCH 273/613] Marking fields readonly --- src/vs/platform/files/common/files.ts | 24 +++++++++---------- .../contrib/files/common/explorerModel.ts | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index ad600a2a430fd..40d21fce9de44 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/workbench/contrib/files/common/explorerModel.ts b/src/vs/workbench/contrib/files/common/explorerModel.ts index aefd67e415d15..cf0172fd3174a 100644 --- a/src/vs/workbench/contrib/files/common/explorerModel.ts +++ b/src/vs/workbench/contrib/files/common/explorerModel.ts @@ -148,7 +148,7 @@ export class ExplorerItem { return this === this.root; } - static create(raw: IFileStat, parent: ExplorerItem | undefined, resolveTo?: URI[]): ExplorerItem { + static create(raw: IFileStat, parent: ExplorerItem | undefined, resolveTo?: readonly URI[]): ExplorerItem { const stat = new ExplorerItem(raw.resource, parent, raw.isDirectory, raw.isSymbolicLink, raw.isReadonly, raw.name, raw.mtime); // Recursively add children if present From 99912f5866092ff906e69b8e66771c8124922ee2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 15 Aug 2019 18:18:42 -0700 Subject: [PATCH 274/613] Remove webview svg whitelist This is no longer required --- src/vs/platform/product/common/product.ts | 1 - .../api/browser/mainThreadCodeInsets.ts | 1 - .../extensions/browser/extensionEditor.ts | 4 +- .../browser/extensionsWorkbenchService.ts | 8 --- .../contrib/extensions/common/extensions.ts | 1 - .../webview/browser/webviewEditorService.ts | 1 - .../contrib/webview/common/webview.ts | 2 - .../electron-browser/webviewElement.ts | 57 ------------------- 8 files changed, 1 insertion(+), 74 deletions(-) diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 3797e18a25ea4..0a3b1f75c89ff 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -46,7 +46,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/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index 2e9ea44a2331f..bb42627961df1 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/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index e31c90cb95be3..d4a6e6f60acbc 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -572,9 +572,7 @@ export class ExtensionEditor extends BaseEditor { { enableFindWidget: true, }, - { - svgWhiteList: this.extensionsWorkbenchService.allowedBadgeProviders, - }); + {}); webviewElement.mountTo(template.content); this.contentDisposables.add(webviewElement.onDidFocus(() => this.fireOnDidFocus())); const removeLayoutParticipant = arrays.insert(this.layoutParticipants, webviewElement); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index b64689e02c6c9..e82544c68e3f6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -484,7 +484,6 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension private readonly _onChange: Emitter = new Emitter(); get onChange(): Event { return this._onChange.event; } - private _extensionAllowedBadgeProviders: string[] | undefined; private installing: IExtension[] = []; constructor( @@ -1020,13 +1019,6 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return changed; } - get allowedBadgeProviders(): string[] { - if (!this._extensionAllowedBadgeProviders) { - this._extensionAllowedBadgeProviders = (this.productService.extensionAllowedBadgeProviders || []).map(s => s.toLowerCase()); - } - return this._extensionAllowedBadgeProviders; - } - private _activityCallBack: (() => void) | null = null; private updateActivity(): void { if ((this.localExtensions && this.localExtensions.local.some(e => e.state === ExtensionState.Installing || e.state === ExtensionState.Uninstalling)) diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index 33e46eecf825e..47b8e916d88e0 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -91,7 +91,6 @@ export interface IExtensionsWorkbenchService { setEnablement(extensions: IExtension | IExtension[], enablementState: EnablementState): Promise; open(extension: IExtension, sideByside?: boolean): Promise; checkForUpdates(): Promise; - allowedBadgeProviders: string[]; } export const ConfigurationKey = 'extensions'; diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts index fb0354fab1af9..c17d203aed333 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts @@ -244,7 +244,6 @@ export class WebviewEditorService implements IWebviewEditorService { private createWebiew(id: string, extension: { location: URI; id: ExtensionIdentifier; } | undefined, options: WebviewInputOptions) { return this._webviewService.createWebviewEditorOverlay(id, { - allowSvgs: true, extension: extension, enableFindWidget: options.enableFindWidget, retainContextWhenHidden: options.retainContextWhenHidden diff --git a/src/vs/workbench/contrib/webview/common/webview.ts b/src/vs/workbench/contrib/webview/common/webview.ts index d6a73ff5d6131..ea7ecb5a7209f 100644 --- a/src/vs/workbench/contrib/webview/common/webview.ts +++ b/src/vs/workbench/contrib/webview/common/webview.ts @@ -41,7 +41,6 @@ export interface IWebviewService { export const WebviewResourceScheme = 'vscode-resource'; export interface WebviewOptions { - readonly allowSvgs?: boolean; readonly extension?: { readonly location: URI; readonly id?: ExtensionIdentifier; @@ -53,7 +52,6 @@ export interface WebviewOptions { export interface WebviewContentOptions { readonly allowScripts?: boolean; - readonly svgWhiteList?: string[]; readonly localResourceRoots?: ReadonlyArray; readonly portMapping?: ReadonlyArray; readonly enableCommandUris?: boolean; diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 4b06dd21bb1c6..4002461cae284 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -9,7 +9,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { once } from 'vs/base/common/functional'; import { Disposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; -import { endsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import * as modes from 'vs/editor/common/modes'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -137,51 +136,6 @@ class WebviewPortMappingProvider extends Disposable { } } -class SvgBlocker extends Disposable { - - private readonly _onDidBlockSvg = this._register(new Emitter()); - public readonly onDidBlockSvg = this._onDidBlockSvg.event; - - constructor( - session: WebviewSession, - private readonly _options: WebviewContentOptions, - ) { - super(); - - session.onBeforeRequest(async (details) => { - if (details.url.indexOf('.svg') > 0) { - const uri = URI.parse(details.url); - if (uri && !uri.scheme.match(/file/i) && endsWith(uri.path, '.svg') && !this.isAllowedSvg(uri)) { - this._onDidBlockSvg.fire(); - return { cancel: true }; - } - } - - return undefined; - }); - - session.onHeadersReceived((details) => { - const headers: any = details.responseHeaders; - const contentType: string[] = headers['content-type'] || headers['Content-Type']; - if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { - const uri = URI.parse(details.url); - if (uri && !this.isAllowedSvg(uri)) { - this._onDidBlockSvg.fire(); - return { cancel: true }; - } - } - return undefined; - }); - } - - private isAllowedSvg(uri: URI): boolean { - if (this._options.svgWhiteList) { - return this._options.svgWhiteList.indexOf(uri.authority.toLowerCase()) >= 0; - } - return false; - } -} - class WebviewKeyboardHandler extends Disposable { private _ignoreMenuShortcut = false; @@ -335,11 +289,6 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { tunnelService, )); - if (!this._options.allowSvgs) { - const svgBlocker = this._register(new SvgBlocker(session, this.content.options)); - svgBlocker.onDidBlockSvg(() => this.onDidBlockSvg()); - } - this._register(new WebviewKeyboardHandler(this._webview)); this._register(addDisposableListener(this._webview, 'console-message', function (e: { level: number; message: string; line: number; sourceId: string; }) { @@ -584,12 +533,6 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { this._send('message', data); } - private onDidBlockSvg() { - this.sendMessage({ - name: 'vscode-did-block-svg' - }); - } - private style(theme: ITheme): void { const { styles, activeTheme } = getWebviewThemeData(theme, this._configurationService); this._send('styles', { styles, activeTheme }); From 1ce89e2cb720d69c496c2815c4696ee4fd4429a6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 15 Aug 2019 18:20:21 -0700 Subject: [PATCH 275/613] Removing test for disposable store We have disabled this behavior --- src/vs/base/test/common/lifecycle.test.ts | 36 ++--------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/src/vs/base/test/common/lifecycle.test.ts b/src/vs/base/test/common/lifecycle.test.ts index 46d8a254b5b54..4d15ad2046c4c 100644 --- a/src/vs/base/test/common/lifecycle.test.ts +++ b/src/vs/base/test/common/lifecycle.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IDisposable, dispose, ReferenceCollection, Disposable as DisposableBase, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, ReferenceCollection } from 'vs/base/common/lifecycle'; class Disposable implements IDisposable { isDisposed = false; @@ -50,38 +50,6 @@ suite('Lifecycle', () => { }); }); -suite('DisposableBase', () => { - test('register should not leak if object has already been disposed', () => { - let aCount = 0; - let bCount = 0; - - const disposable = new class extends DisposableBase { - register(other: IDisposable) { - this._register(other); - } - }; - - disposable.register(toDisposable(() => ++aCount)); - - assert.strictEqual(aCount, 0); - assert.strictEqual(bCount, 0); - - disposable.dispose(); - assert.strictEqual(aCount, 1); - assert.strictEqual(bCount, 0); - - // Any newly added disposables should be disposed of immediately - disposable.register(toDisposable(() => ++bCount)); - assert.strictEqual(aCount, 1); - assert.strictEqual(bCount, 1); - - // Further dispose calls should have no effect - disposable.dispose(); - assert.strictEqual(aCount, 1); - assert.strictEqual(bCount, 1); - }); -}); - suite('Reference Collection', () => { class Collection extends ReferenceCollection { private _count = 0; @@ -118,4 +86,4 @@ suite('Reference Collection', () => { ref4.dispose(); assert.equal(collection.count, 0); }); -}); \ No newline at end of file +}); From c23cacdc97b54e6056400599dad329cabc7facfb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 16 Aug 2019 08:08:25 +0200 Subject: [PATCH 276/613] build - disable smoketest --- .../darwin/product-build-darwin.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 175e3d0eb08a2..5718cfa56bd66 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -107,15 +107,15 @@ steps: displayName: Run integration tests condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -- script: | - set -e - cd test/smoke - yarn compile - cd - - yarn smoketest --web --headless - continueOnError: true - displayName: Run smoke tests - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) +# - script: | +# set -e +# cd test/smoke +# yarn compile +# cd - +# yarn smoketest --web --headless +# continueOnError: true +# displayName: Run smoke tests +# condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e From 0a7d4edb20715ddb7ad124936a7dcb0ca8940e65 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 16 Aug 2019 09:14:14 +0200 Subject: [PATCH 277/613] Enable new Octicons style by default --- src/vs/workbench/browser/workbench.contribution.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 22ca16ef4873e..5504ef503070f 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -239,6 +239,11 @@ import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform' 'description': nls.localize('workbench.useExperimentalGridLayout', "Enables the grid layout for the workbench. This setting may enable additional layout options for workbench components."), 'default': true, 'scope': ConfigurationScope.APPLICATION + }, + 'workbench.octiconsUpdate.enabled': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('workbench.octiconsUpdate.enabled', "Controls the visibility of the new Octicons style in the workbench.") } } }); From 54520f5f4437565b2bd68524d0b9586ca86712f1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 16 Aug 2019 09:27:34 +0200 Subject: [PATCH 278/613] fix web platform check --- .../contrib/extensions/browser/extensionTipsService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts index ec9b7e82c0fbb..ebf0b5ba3366f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts @@ -40,7 +40,7 @@ import { extname } from 'vs/base/common/resources'; import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/product'; import { timeout } from 'vs/base/common/async'; import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; -import { Platform, setImmediate } from 'vs/base/common/platform'; +import { setImmediate, isWeb } from 'vs/base/common/platform'; import { platform, env as processEnv } from 'vs/base/common/process'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -984,7 +984,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe * If user has any of the tools listed in this.productService.exeBasedExtensionTips, fetch corresponding recommendations */ private async fetchExecutableRecommendations(important: boolean): Promise { - if (Platform.Web) { + if (isWeb) { return; } From e741e07929e45157761d4deb976fcb156b886f87 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 16 Aug 2019 09:11:24 +0200 Subject: [PATCH 279/613] better worker error logging --- .../extensions/browser/webWorkerExtensionHostStarter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index 5794b25906167..ef8569f52525a 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -65,8 +65,8 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { }; worker.onerror = (event) => { - console.error(event.error); - this._onDidExit.fire([81, event.error]); + console.error(event.message, event.error); + this._onDidExit.fire([81, event.message || event.error]); }; // keep for cleanup From 81b873bb6e5ac65f78e49e788c1c0b8749028d50 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 16 Aug 2019 09:35:51 +0200 Subject: [PATCH 280/613] make sure to prepend vs/nls --- src/buildfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buildfile.js b/src/buildfile.js index 43fcfa807c35f..27928e946ebd7 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -26,7 +26,7 @@ exports.serviceWorker = [{ exports.workerExtensionHost = [{ name: 'vs/workbench/services/extensions/worker/extensionHostWorker', // include: [], - prepend: ['vs/loader.js'], + prepend: ['vs/loader.js', 'vs/nls.js'], append: ['vs/workbench/services/extensions/worker/extensionHostWorkerMain'], dest: 'vs/workbench/services/extensions/worker/extensionHostWorkerMain.js' }]; From bd9d98bb41b10bea4f5d8cc331e193b07e01bdaf Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 16 Aug 2019 10:50:21 +0200 Subject: [PATCH 281/613] :lipstick: --- src/vs/code/electron-main/app.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 159eb32b49c5b..c17c592775f9e 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -167,11 +167,13 @@ export class CodeApplication extends Disposable { event.preventDefault(); }); app.on('remote-get-current-web-contents', event => { - // The driver needs access to web contents - if (!this.environmentService.args.driver) { - this.logService.trace(`App#on(remote-get-current-web-contents): prevented`); - event.preventDefault(); + if (this.environmentService.args.driver) { + return; // the driver needs access to web contents } + + this.logService.trace(`App#on(remote-get-current-web-contents): prevented`); + + event.preventDefault(); }); app.on('web-contents-created', (_event: Electron.Event, contents) => { contents.on('will-attach-webview', (event: Electron.Event, webPreferences, params) => { From 92399b7867e4503806b916db357fd7a0451dbf8d Mon Sep 17 00:00:00 2001 From: weijiaxun Date: Fri, 16 Aug 2019 17:19:45 +0800 Subject: [PATCH 282/613] fix: keep the two "Copy Path" behavior consistent When using remote, this "Copy Path" function of SearchAction will keep the remote prefix while the FileCommand will not. Change-Id: Ide00d2da5a695d0adbe87622643c7a600dd46432 --- .../contrib/search/browser/searchActions.ts | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index ca96a4cd1ee76..9eac783512a11 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -7,14 +7,11 @@ import * as DOM from 'vs/base/browser/dom'; import { Action } from 'vs/base/common/actions'; import { INavigator } from 'vs/base/common/iterator'; import { createKeybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { normalizeDriveLetter } from 'vs/base/common/labels'; -import { Schemas } from 'vs/base/common/network'; -import { normalize } from 'vs/base/common/path'; import { isWindows, OS } from 'vs/base/common/platform'; import { repeat } from 'vs/base/common/strings'; -import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ILabelService } from 'vs/platform/label/common/label'; import { ICommandHandler } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -667,14 +664,11 @@ export class ReplaceAction extends AbstractSearchAndReplaceAction { } } -function uriToClipboardString(resource: URI): string { - return resource.scheme === Schemas.file ? normalize(normalizeDriveLetter(resource.fsPath)) : resource.toString(); -} - export const copyPathCommand: ICommandHandler = async (accessor, fileMatch: FileMatch | FolderMatch) => { const clipboardService = accessor.get(IClipboardService); + const labelService = accessor.get(ILabelService); - const text = uriToClipboardString(fileMatch.resource); + const text = labelService.getUriLabel(fileMatch.resource, { noPrefix: true }); await clipboardService.writeText(text); }; @@ -706,25 +700,26 @@ function matchToString(match: Match, indent = 0): string { } const lineDelimiter = isWindows ? '\r\n' : '\n'; -function fileMatchToString(fileMatch: FileMatch, maxMatches: number): { text: string, count: number } { +function fileMatchToString(fileMatch: FileMatch, maxMatches: number, labelService: ILabelService): { text: string, count: number } { const matchTextRows = fileMatch.matches() .sort(searchMatchComparer) .slice(0, maxMatches) .map(match => matchToString(match, 2)); + const uriString = labelService.getUriLabel(fileMatch.resource, { noPrefix: true }); return { - text: `${uriToClipboardString(fileMatch.resource)}${lineDelimiter}${matchTextRows.join(lineDelimiter)}`, + text: `${uriString}${lineDelimiter}${matchTextRows.join(lineDelimiter)}`, count: matchTextRows.length }; } -function folderMatchToString(folderMatch: FolderMatch | BaseFolderMatch, maxMatches: number): { text: string, count: number } { +function folderMatchToString(folderMatch: FolderMatch | BaseFolderMatch, maxMatches: number, labelService: ILabelService): { text: string, count: number } { const fileResults: string[] = []; let numMatches = 0; const matches = folderMatch.matches().sort(searchMatchComparer); for (let i = 0; i < folderMatch.fileCount() && numMatches < maxMatches; i++) { - const fileResult = fileMatchToString(matches[i], maxMatches - numMatches); + const fileResult = fileMatchToString(matches[i], maxMatches - numMatches, labelService); numMatches += fileResult.count; fileResults.push(fileResult.text); } @@ -738,14 +733,15 @@ function folderMatchToString(folderMatch: FolderMatch | BaseFolderMatch, maxMatc const maxClipboardMatches = 1e4; export const copyMatchCommand: ICommandHandler = async (accessor, match: RenderableMatch) => { const clipboardService = accessor.get(IClipboardService); + const labelService = accessor.get(ILabelService); let text: string | undefined; if (match instanceof Match) { text = matchToString(match); } else if (match instanceof FileMatch) { - text = fileMatchToString(match, maxClipboardMatches).text; + text = fileMatchToString(match, maxClipboardMatches, labelService).text; } else if (match instanceof BaseFolderMatch) { - text = folderMatchToString(match, maxClipboardMatches).text; + text = folderMatchToString(match, maxClipboardMatches, labelService).text; } if (text) { @@ -753,12 +749,12 @@ export const copyMatchCommand: ICommandHandler = async (accessor, match: Rendera } }; -function allFolderMatchesToString(folderMatches: Array, maxMatches: number): string { +function allFolderMatchesToString(folderMatches: Array, maxMatches: number, labelService: ILabelService): string { const folderResults: string[] = []; let numMatches = 0; folderMatches = folderMatches.sort(searchMatchComparer); for (let i = 0; i < folderMatches.length && numMatches < maxMatches; i++) { - const folderResult = folderMatchToString(folderMatches[i], maxMatches - numMatches); + const folderResult = folderMatchToString(folderMatches[i], maxMatches - numMatches, labelService); if (folderResult.count) { numMatches += folderResult.count; folderResults.push(folderResult.text); @@ -772,12 +768,13 @@ export const copyAllCommand: ICommandHandler = async (accessor) => { const viewletService = accessor.get(IViewletService); const panelService = accessor.get(IPanelService); const clipboardService = accessor.get(IClipboardService); + const labelService = accessor.get(ILabelService); const searchView = getSearchView(viewletService, panelService); if (searchView) { const root = searchView.searchResult; - const text = allFolderMatchesToString(root.folderMatches(), maxClipboardMatches); + const text = allFolderMatchesToString(root.folderMatches(), maxClipboardMatches, labelService); await clipboardService.writeText(text); } }; From 1c6e25b87e05a8429c986c59431be77467af9449 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 16 Aug 2019 11:20:09 +0200 Subject: [PATCH 283/613] Load Octicons through ts instead of css import --- src/vs/base/browser/ui/octiconLabel/octiconLabel.ts | 2 ++ src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts b/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts index 70c4a9e8cd2e4..75b3ba027dd28 100644 --- a/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts +++ b/src/vs/base/browser/ui/octiconLabel/octiconLabel.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./octicons/octicons'; +import 'vs/css!./octicons/octicons2'; import 'vs/css!./octicons/octicons-main'; import 'vs/css!./octicons/octicons-animations'; import { escape } from 'vs/base/common/strings'; diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css index d06eca484d13d..8a387b335ba39 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css @@ -1,6 +1,3 @@ -@import 'octicons.css'; -@import 'octicons2.css'; - body[data-octicons-update="enabled"] { --version: octicons2; } From c9c42e4f7f37992d9e178a007a097ae9d319e9ec Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 16 Aug 2019 11:21:00 +0200 Subject: [PATCH 284/613] update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ee393d1aaf3c9..f8a973d504eef 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "13f407368a517ace39c2721a66a951ba4eebce20", + "distro": "32d06dfa691431ba32bbe11ac39ab4b30663b342", "author": { "name": "Microsoft Corporation" }, @@ -159,4 +159,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} +} \ No newline at end of file From 73f852d270893e4208577de5b352076338a87083 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 16 Aug 2019 11:22:25 +0200 Subject: [PATCH 285/613] Fixes #79166 --- src/vs/editor/contrib/zoneWidget/zoneWidget.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts index 3e0fe0a7d66cb..703706bf238bf 100644 --- a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts @@ -192,9 +192,6 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { } public dispose(): void { - - this._disposables.dispose(); - if (this._overlayWidget) { this.editor.removeOverlayWidget(this._overlayWidget); this._overlayWidget = null; @@ -211,6 +208,8 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { this.editor.deltaDecorations(this._positionMarkerId, []); this._positionMarkerId = []; + + this._disposables.dispose(); } public create(): void { From 5352ba33be8dde00a0cb1354711174ee5c1fcf0d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 16 Aug 2019 12:10:43 +0200 Subject: [PATCH 286/613] web - synchronise global state changes --- .../storage/browser/storageService.ts | 159 +++++++++++++++--- src/vs/platform/storage/common/storage.ts | 74 -------- .../storage.test.ts | 10 +- 3 files changed, 143 insertions(+), 100 deletions(-) rename src/vs/platform/storage/test/{node => electron-browser}/storage.test.ts (92%) diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index a11505e32acde..cec2d7dc9c773 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -5,15 +5,17 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage, FileStorageDatabase } from 'vs/platform/storage/common/storage'; +import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { IFileService } from 'vs/platform/files/common/files'; -import { IStorage, Storage } from 'vs/base/parts/storage/common/storage'; +import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; +import { IStorage, Storage, IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; -import { runWhenIdle } from 'vs/base/common/async'; +import { runWhenIdle, RunOnceScheduler } from 'vs/base/common/async'; +import { serializableToMap, mapToSerializable } from 'vs/base/common/map'; +import { VSBuffer } from 'vs/base/common/buffer'; export class BrowserStorageService extends Disposable implements IStorageService { @@ -35,6 +37,7 @@ export class BrowserStorageService extends Disposable implements IStorageService private workspaceStorageFile: URI; private initializePromise: Promise; + private periodicSaveScheduler = this._register(new RunOnceScheduler(() => this.saveState(), 5000)); get hasPendingUpdate(): boolean { return this.globalStorageDatabase.hasPendingUpdate || this.workspaceStorageDatabase.hasPendingUpdate; @@ -51,20 +54,23 @@ export class BrowserStorageService extends Disposable implements IStorageService // long running operation. // Instead, periodically ask customers to save save. The library will be clever enough // to only save state that has actually changed. - this.saveStatePeriodically(); + this.periodicSaveScheduler.schedule(); } - private saveStatePeriodically(): void { - setTimeout(() => { - runWhenIdle(() => { + private saveState(): void { + runWhenIdle(() => { - // this event will potentially cause new state to be stored + // this event will potentially cause new state to be stored + // since new state will only be created while the document + // has focus, one optimization is to not run this when the + // document has no focus, assuming that state has not changed + if (document.hasFocus()) { this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); + } - // repeat - this.saveStatePeriodically(); - }); - }, 5000); + // repeat + this.periodicSaveScheduler.schedule(); + }); } initialize(payload: IWorkspaceInitializationPayload): Promise { @@ -83,14 +89,14 @@ export class BrowserStorageService extends Disposable implements IStorageService // Workspace Storage this.workspaceStorageFile = joinPath(stateRoot, `${payload.id}.json`); - this.workspaceStorageDatabase = this._register(new FileStorageDatabase(this.workspaceStorageFile, this.fileService)); - this.workspaceStorage = new Storage(this.workspaceStorageDatabase); + this.workspaceStorageDatabase = this._register(new FileStorageDatabase(this.workspaceStorageFile, false /* do not watch for external changes */, this.fileService)); + this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase)); this._register(this.workspaceStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.WORKSPACE }))); // Global Storage this.globalStorageFile = joinPath(stateRoot, 'global.json'); - this.globalStorageDatabase = this._register(new FileStorageDatabase(this.globalStorageFile, this.fileService)); - this.globalStorage = new Storage(this.globalStorageDatabase); + this.globalStorageDatabase = this._register(new FileStorageDatabase(this.globalStorageFile, true /* watch for external changes */, this.fileService)); + this.globalStorage = this._register(new Storage(this.globalStorageDatabase)); this._register(this.globalStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.GLOBAL }))); // Init both @@ -140,14 +146,125 @@ export class BrowserStorageService extends Disposable implements IStorageService } close(): void { - - // Signal as event so that clients can still store data - this._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); - // We explicitly do not close our DBs because writing data onBeforeUnload() // can result in unexpected results. Namely, it seems that - even though this // operation is async - sometimes it is being triggered on unload and // succeeds. Often though, the DBs turn out to be empty because the write // never had a chance to complete. + // + // Instead we trigger dispose() to ensure that no timeouts or callbacks + // get triggered in this phase. + this.dispose(); + } +} + +export class FileStorageDatabase extends Disposable implements IStorageDatabase { + + private readonly _onDidChangeItemsExternal: Emitter = this._register(new Emitter()); + readonly onDidChangeItemsExternal: Event = this._onDidChangeItemsExternal.event; + + private cache: Map | undefined; + + private pendingUpdate: Promise = Promise.resolve(); + + private _hasPendingUpdate = false; + get hasPendingUpdate(): boolean { + return this._hasPendingUpdate; + } + + private isWatching = false; + + constructor( + private readonly file: URI, + private readonly watchForExternalChanges: boolean, + @IFileService private readonly fileService: IFileService + ) { + super(); + } + + private async ensureWatching(): Promise { + if (this.isWatching || !this.watchForExternalChanges) { + return; + } + + const exists = await this.fileService.exists(this.file); + if (this.isWatching || !exists) { + return; // file must exist to be watched + } + + this.isWatching = true; + + this._register(this.fileService.watch(this.file)); + this._register(this.fileService.onFileChanges(e => { + if (document.hasFocus()) { + return; // ignore changes from ourselves by checking for focus + } + + if (!e.contains(this.file, FileChangeType.UPDATED)) { + return; // not our file + } + + this.onDidStorageChangeExternal(); + })); + } + + private async onDidStorageChangeExternal(): Promise { + const items = await this.doGetItemsFromFile(); + + this.cache = items; + + this._onDidChangeItemsExternal.fire({ items }); + } + + async getItems(): Promise> { + if (!this.cache) { + try { + this.cache = await this.doGetItemsFromFile(); + } catch (error) { + this.cache = new Map(); + } + } + + return this.cache; + } + + private async doGetItemsFromFile(): Promise> { + await this.pendingUpdate; + + const itemsRaw = await this.fileService.readFile(this.file); + + this.ensureWatching(); // now that the file must exist, ensure we watch it for changes + + return serializableToMap(JSON.parse(itemsRaw.value.toString())); + } + + async updateItems(request: IUpdateRequest): Promise { + const items = await this.getItems(); + + if (request.insert) { + request.insert.forEach((value, key) => items.set(key, value)); + } + + if (request.delete) { + request.delete.forEach(key => items.delete(key)); + } + + await this.pendingUpdate; + + this._hasPendingUpdate = true; + + this.pendingUpdate = this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(mapToSerializable(items)))) + .then(() => { + this.ensureWatching(); // now that the file must exist, ensure we watch it for changes + }) + .finally(() => { + this._hasPendingUpdate = false; + }); + + return this.pendingUpdate; + } + + close(): Promise { + return this.pendingUpdate; } } diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 46fdd613d11c8..8860535a54a65 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -7,11 +7,6 @@ import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/co import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { isUndefinedOrNull } from 'vs/base/common/types'; -import { IUpdateRequest, IStorageDatabase } from 'vs/base/parts/storage/common/storage'; -import { serializableToMap, mapToSerializable } from 'vs/base/common/map'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { URI } from 'vs/base/common/uri'; -import { IFileService } from 'vs/platform/files/common/files'; export const IStorageService = createDecorator('storageService'); @@ -212,75 +207,6 @@ export class InMemoryStorageService extends Disposable implements IStorageServic } } -export class FileStorageDatabase extends Disposable implements IStorageDatabase { - - readonly onDidChangeItemsExternal = Event.None; // TODO@Ben implement global UI storage events - - private cache: Map | undefined; - - private pendingUpdate: Promise = Promise.resolve(); - - private _hasPendingUpdate = false; - get hasPendingUpdate(): boolean { - return this._hasPendingUpdate; - } - - constructor( - private readonly file: URI, - private readonly fileService: IFileService - ) { - super(); - } - - async getItems(): Promise> { - if (!this.cache) { - try { - this.cache = await this.doGetItemsFromFile(); - } catch (error) { - this.cache = new Map(); - } - } - - return this.cache; - } - - private async doGetItemsFromFile(): Promise> { - await this.pendingUpdate; - - const itemsRaw = await this.fileService.readFile(this.file); - - return serializableToMap(JSON.parse(itemsRaw.value.toString())); - } - - async updateItems(request: IUpdateRequest): Promise { - const items = await this.getItems(); - - if (request.insert) { - request.insert.forEach((value, key) => items.set(key, value)); - } - - if (request.delete) { - request.delete.forEach(key => items.delete(key)); - } - - await this.pendingUpdate; - - this._hasPendingUpdate = true; - - this.pendingUpdate = this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(mapToSerializable(items)))) - .then(() => undefined) - .finally(() => { - this._hasPendingUpdate = false; - }); - - return this.pendingUpdate; - } - - close(): Promise { - return this.pendingUpdate; - } -} - export async function logStorage(global: Map, workspace: Map, globalPath: string, workspacePath: string): Promise { const safeParse = (value: string) => { try { diff --git a/src/vs/platform/storage/test/node/storage.test.ts b/src/vs/platform/storage/test/electron-browser/storage.test.ts similarity index 92% rename from src/vs/platform/storage/test/node/storage.test.ts rename to src/vs/platform/storage/test/electron-browser/storage.test.ts index 7c77b9e1c08ff..4e5e10a4e13ab 100644 --- a/src/vs/platform/storage/test/node/storage.test.ts +++ b/src/vs/platform/storage/test/electron-browser/storage.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { equal } from 'assert'; -import { FileStorageDatabase } from 'vs/platform/storage/common/storage'; +import { FileStorageDatabase } from 'vs/platform/storage/browser/storageService'; import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'vs/base/common/path'; import { tmpdir } from 'os'; @@ -49,7 +49,7 @@ suite('Storage', () => { }); test('File Based Storage', async () => { - let storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), fileService)); + let storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), false, fileService)); await storage.init(); @@ -63,7 +63,7 @@ suite('Storage', () => { await storage.close(); - storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), fileService)); + storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), false, fileService)); await storage.init(); @@ -81,7 +81,7 @@ suite('Storage', () => { await storage.close(); - storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), fileService)); + storage = new Storage(new FileStorageDatabase(URI.file(join(testDir, 'storage.json')), false, fileService)); await storage.init(); @@ -89,4 +89,4 @@ suite('Storage', () => { equal(storage.get('barNumber', 'undefinedNumber'), 'undefinedNumber'); equal(storage.get('barBoolean', 'undefinedBoolean'), 'undefinedBoolean'); }); -}); \ No newline at end of file +}); From b8974b05a62c83fd92deb787162cf507e10bfd46 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 16 Aug 2019 12:26:32 +0200 Subject: [PATCH 287/613] explorer input black magic fixes #78153 --- .../files/browser/views/explorerViewer.ts | 50 ++++++++----------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 75aae73d1448b..d8d9f52f32014 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -218,56 +218,46 @@ export class FilesRenderer implements ITreeRenderer 0 && !stat.isDirectory ? lastDot : value.length }); - let isFinishableDisposeEvent = false; - setTimeout(() => { - // Check if disposed - if (!inputBox.inputElement) { - return; - } - inputBox.focus(); - inputBox.select({ start: 0, end: lastDot > 0 && !stat.isDirectory ? lastDot : value.length }); - isFinishableDisposeEvent = true; - }, 0); - - const done = once(async (success: boolean) => { + const done = once(async (success: boolean, blur: boolean) => { label.element.style.display = 'none'; const value = inputBox.value; dispose(toDispose); - label.element.remove(); - // Timeout: once done rendering only then re-render #70902 - setTimeout(() => editableData.onFinish(value, success), 0); + container.removeChild(label.element); + editableData.onFinish(value, success); }); - const blurDisposable = DOM.addDisposableListener(inputBox.inputElement, DOM.EventType.BLUR, () => { - done(inputBox.isInputValid()); - }); + // It can happen that the tree re-renders this node. When that happens, + // we're gonna get a blur event first and only after an element disposable. + // Because of that, we should setTimeout the blur handler to differentiate + // between the blur happening because of a unrender or because of a user action. + let ignoreBlur = false; const toDispose = [ inputBox, DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_DOWN, (e: IKeyboardEvent) => { if (e.equals(KeyCode.Enter)) { if (inputBox.validate()) { - done(true); + done(true, false); } } else if (e.equals(KeyCode.Escape)) { - done(false); + done(false, false); } }), - blurDisposable, + DOM.addDisposableListener(inputBox.inputElement, DOM.EventType.BLUR, () => { + setTimeout(() => { + if (!ignoreBlur) { + done(inputBox.isInputValid(), true); + } + }, 0); + }), label, styler ]; - return toDisposable(() => { - if (isFinishableDisposeEvent) { - done(false); - } - else { - dispose(toDispose); - label.element.remove(); - } - }); + return toDisposable(() => ignoreBlur = true); } disposeElement?(element: ITreeNode, index: number, templateData: IFileTemplateData): void { From 4f9f4c3c13ce178e65e2cf5dc8deedbe647b1f2e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 16 Aug 2019 15:27:59 +0200 Subject: [PATCH 288/613] fix #72417 --- .../notification/common/notification.ts | 19 ++++++ .../contrib/files/browser/saveErrorHandler.ts | 23 +++---- .../browser/localizations.contribution.ts | 14 ++-- .../electron-browser/workspaceStatsService.ts | 20 ++---- .../terminal/browser/terminalConfigHelper.ts | 19 ++---- .../contrib/update/electron-browser/update.ts | 68 ++++--------------- .../services/files/common/workspaceWatcher.ts | 32 ++++----- .../common/notificationService.ts | 12 ++-- 8 files changed, 73 insertions(+), 134 deletions(-) diff --git a/src/vs/platform/notification/common/notification.ts b/src/vs/platform/notification/common/notification.ts index 2582047934eb3..23b14135edbca 100644 --- a/src/vs/platform/notification/common/notification.ts +++ b/src/vs/platform/notification/common/notification.ts @@ -37,6 +37,19 @@ export interface INotificationProperties { neverShowAgain?: INeverShowAgainOptions; } +export enum NeverShowAgainScope { + + /** + * Will never show this notification on the current workspace again. + */ + WORKSPACE, + + /** + * Will never show this notification on any workspace again. + */ + GLOBAL +} + export interface INeverShowAgainOptions { /** @@ -49,6 +62,12 @@ export interface INeverShowAgainOptions { * make it a secondary action instead. */ isSecondary?: boolean; + + /** + * Wether to persist the choice in the current workspace or for all workspaces. By + * default it will be persisted for all workspaces. + */ + scope?: NeverShowAgainScope; } export interface INotification extends INotificationProperties { diff --git a/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts index ac09186d04619..608638b517820 100644 --- a/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/saveErrorHandler.ts @@ -236,7 +236,6 @@ class ResolveSaveConflictAction extends Action { @IEditorService private readonly editorService: IEditorService, @INotificationService private readonly notificationService: INotificationService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IStorageService private readonly storageService: IStorageService, @IEnvironmentService private readonly environmentService: IEnvironmentService ) { super('workbench.files.action.resolveConflict', nls.localize('compareChanges', "Compare")); @@ -250,21 +249,15 @@ class ResolveSaveConflictAction extends Action { await TextFileContentProvider.open(resource, CONFLICT_RESOLUTION_SCHEME, editorLabel, this.editorService, { pinned: true }); - if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, StorageScope.GLOBAL)) { - return; // return if this message is ignored - } - // Show additional help how to resolve the save conflict - const primaryActions: IAction[] = [ - this.instantiationService.createInstance(ResolveConflictLearnMoreAction) - ]; - const secondaryActions: IAction[] = [ - this.instantiationService.createInstance(DoNotShowResolveConflictLearnMoreAction) - ]; - - const actions: INotificationActions = { primary: primaryActions, secondary: secondaryActions }; - const handle = this.notificationService.notify({ severity: Severity.Info, message: conflictEditorHelp, actions }); - Event.once(handle.onDidClose)(() => { dispose(primaryActions); dispose(secondaryActions); }); + const actions: INotificationActions = { primary: [this.instantiationService.createInstance(ResolveConflictLearnMoreAction)] }; + const handle = this.notificationService.notify({ + severity: Severity.Info, + message: conflictEditorHelp, + actions, + neverShowAgain: { id: LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, isSecondary: true } + }); + Event.once(handle.onDidClose)(() => dispose(actions.primary!)); pendingResolveSaveConflictMessages.push(handle); } diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts index 0ab7785ff311e..1982d8aebc025 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts @@ -68,8 +68,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo } private onDidInstallExtension(e: DidInstallExtensionEvent): void { - const donotAskUpdateKey = 'langugage.update.donotask'; - if (!this.storageService.getBoolean(donotAskUpdateKey, StorageScope.GLOBAL) && e.local && e.operation === InstallOperation.Install && e.local.manifest.contributes && e.local.manifest.contributes.localizations && e.local.manifest.contributes.localizations.length) { + if (e.local && e.operation === InstallOperation.Install && e.local.manifest.contributes && e.local.manifest.contributes.localizations && e.local.manifest.contributes.localizations.length) { const locale = e.local.manifest.contributes.localizations[0].languageId; if (platform.language !== locale) { const updateAndRestart = platform.locale !== locale; @@ -83,12 +82,11 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.localeResource, { key: 'locale', value: locale }, true) : Promise.resolve(undefined); updatePromise.then(() => this.windowsService.relaunch({}), e => this.notificationService.error(e)); } - }, { - label: localize('neverAgain', "Don't Show Again"), - isSecondary: true, - run: () => this.storageService.store(donotAskUpdateKey, true, StorageScope.GLOBAL) }], - { sticky: true } + { + sticky: true, + neverShowAgain: { id: 'langugage.update.donotask', isSecondary: true } + } ); } } @@ -302,4 +300,4 @@ ExtensionsRegistry.registerExtensionPoint({ } } } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts b/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts index 9c1c1d5718047..2cb249cba4761 100644 --- a/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts +++ b/src/vs/workbench/contrib/stats/electron-browser/workspaceStatsService.ts @@ -8,9 +8,8 @@ import { IFileService, IResolveFileResult, IFileStat } from 'vs/platform/files/c import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWindowService, IWindowConfiguration } from 'vs/platform/windows/common/windows'; -import { INotificationService, IPromptChoice } from 'vs/platform/notification/common/notification'; +import { INotificationService, NeverShowAgainScope, INeverShowAgainOptions } from 'vs/platform/notification/common/notification'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITextFileService, ITextFileContent } from 'vs/workbench/services/textfile/common/textfiles'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; @@ -22,8 +21,6 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats'; import { getHashedRemotesFromConfig } from 'vs/workbench/contrib/stats/electron-browser/workspaceStats'; -const DISABLE_WORKSPACE_PROMPT_KEY = 'workspaces.dontPromptToOpen'; - const ModulesToLookFor = [ // Packages that suggest a node server 'express', @@ -103,7 +100,6 @@ export class WorkspaceStatsService implements IWorkspaceStatsService { @IWindowService private readonly windowService: IWindowService, @INotificationService private readonly notificationService: INotificationService, @IQuickInputService private readonly quickInputService: IQuickInputService, - @IStorageService private readonly storageService: IStorageService, @ITextFileService private readonly textFileService: ITextFileService ) { } @@ -449,15 +445,7 @@ export class WorkspaceStatsService implements IWorkspaceStatsService { } private doHandleWorkspaceFiles(folder: URI, workspaces: string[]): void { - if (this.storageService.getBoolean(DISABLE_WORKSPACE_PROMPT_KEY, StorageScope.WORKSPACE)) { - return; // prompt disabled by user - } - - const doNotShowAgain: IPromptChoice = { - label: localize('never again', "Don't Show Again"), - isSecondary: true, - run: () => this.storageService.store(DISABLE_WORKSPACE_PROMPT_KEY, true, StorageScope.WORKSPACE) - }; + const neverShowAgain: INeverShowAgainOptions = { id: 'workspaces.dontPromptToOpen', scope: NeverShowAgainScope.WORKSPACE, isSecondary: true }; // Prompt to open one workspace if (workspaces.length === 1) { @@ -466,7 +454,7 @@ export class WorkspaceStatsService implements IWorkspaceStatsService { this.notificationService.prompt(Severity.Info, localize('workspaceFound', "This folder contains a workspace file '{0}'. Do you want to open it? [Learn more]({1}) about workspace files.", workspaceFile, 'https://go.microsoft.com/fwlink/?linkid=2025315'), [{ label: localize('openWorkspace', "Open Workspace"), run: () => this.windowService.openWindow([{ workspaceUri: joinPath(folder, workspaceFile) }]) - }, doNotShowAgain]); + }], { neverShowAgain }); } // Prompt to select a workspace from many @@ -482,7 +470,7 @@ export class WorkspaceStatsService implements IWorkspaceStatsService { } }); } - }, doNotShowAgain]); + }], { neverShowAgain }); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index 7d7aafdb6b39b..e4a307e4059f1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -11,7 +11,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { ITerminalConfiguration, ITerminalFont, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; import Severity from 'vs/base/common/severity'; import { Terminal as XTermTerminal } from 'xterm'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { INotificationService, NeverShowAgainScope } from 'vs/platform/notification/common/notification'; import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal'; import { Emitter, Event } from 'vs/base/common/event'; import { basename } from 'vs/base/common/path'; @@ -254,7 +254,6 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { return r; } - private readonly NO_RECOMMENDATIONS_KEY = 'terminalConfigHelper/launchRecommendationsIgnore'; private recommendationsShown = false; public async showRecommendations(shellLaunchConfig: IShellLaunchConfig): Promise { @@ -264,10 +263,6 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { this.recommendationsShown = true; if (platform.isWindows && shellLaunchConfig.executable && basename(shellLaunchConfig.executable).toLowerCase() === 'wsl.exe') { - if (this._storageService.getBoolean(this.NO_RECOMMENDATIONS_KEY, StorageScope.WORKSPACE, false)) { - return; - } - if (! await this.isExtensionInstalled('ms-vscode-remote.remote-wsl')) { this._notificationService.prompt( Severity.Info, @@ -276,16 +271,10 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { "Check out the 'Visual Studio Code Remote - WSL' extension for a great development experience in WSL. Click [here]({0}) to learn more.", 'https://go.microsoft.com/fwlink/?linkid=2097212' ), - [ - { - label: nls.localize('doNotShowAgain', "Don't Show Again"), - run: () => { - this._storageService.store(this.NO_RECOMMENDATIONS_KEY, true, StorageScope.WORKSPACE); - } - } - ], + [], { - sticky: true + sticky: true, + neverShowAgain: { id: 'terminalConfigHelper/launchRecommendationsIgnore', scope: NeverShowAgainScope.WORKSPACE } } ); } diff --git a/src/vs/workbench/contrib/update/electron-browser/update.ts b/src/vs/workbench/contrib/update/electron-browser/update.ts index 0d2d53003b725..64d4320961276 100644 --- a/src/vs/workbench/contrib/update/electron-browser/update.ts +++ b/src/vs/workbench/contrib/update/electron-browser/update.ts @@ -19,7 +19,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IUpdateService, State as UpdateState, StateType, IUpdate } from 'vs/platform/update/common/update'; import * as semver from 'semver-umd'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { INotificationService, INotificationHandle, Severity } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ReleaseNotesManager } from './releaseNotesEditor'; @@ -162,32 +162,8 @@ export class ProductContribution implements IWorkbenchContribution { } } -class NeverShowAgain { - - private readonly key: string; - - readonly action = new Action(`neverShowAgain:${this.key}`, nls.localize('neveragain', "Don't Show Again"), undefined, true, (notification: INotificationHandle) => { - - // Hide notification - notification.close(); - - this.storageService.store(this.key, true, StorageScope.GLOBAL); - - return Promise.resolve(true); - }); - - constructor(key: string, @IStorageService private readonly storageService: IStorageService) { - this.key = `neverShowAgain:${key}`; - } - - shouldShow(): boolean { - return !this.storageService.getBoolean(this.key, StorageScope.GLOBAL, false); - } -} - export class Win3264BitContribution implements IWorkbenchContribution { - private static readonly KEY = 'update/win32-64bits'; private static readonly URL = 'https://code.visualstudio.com/updates/v1_15#_windows-64-bit'; private static readonly INSIDER_URL = 'https://github.com/Microsoft/vscode-docs/blob/vnext/release-notes/v1_15.md#windows-64-bit'; @@ -200,28 +176,18 @@ export class Win3264BitContribution implements IWorkbenchContribution { return; } - const neverShowAgain = new NeverShowAgain(Win3264BitContribution.KEY, storageService); - - if (!neverShowAgain.shouldShow()) { - return; - } - const url = product.quality === 'insider' ? Win3264BitContribution.INSIDER_URL : Win3264BitContribution.URL; - const handle = notificationService.prompt( + notificationService.prompt( severity.Info, nls.localize('64bitisavailable', "{0} for 64-bit Windows is now available! Click [here]({1}) to learn more.", product.nameShort, url), - [{ - label: nls.localize('neveragain', "Don't Show Again"), - isSecondary: true, - run: () => { - neverShowAgain.action.run(handle); - neverShowAgain.action.dispose(); - } - }], - { sticky: true } + [], + { + sticky: true, + neverShowAgain: { id: 'neverShowAgain:update/win32-64bits', isSecondary: true } + } ); } } @@ -396,23 +362,13 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu } // windows fast updates (target === system) - const neverShowAgain = new NeverShowAgain('update/win32-fast-updates', this.storageService); - - if (!neverShowAgain.shouldShow()) { - return; - } - - const handle = this.notificationService.prompt( + this.notificationService.prompt( severity.Info, nls.localize('updateInstalling', "{0} {1} is being installed in the background; we'll let you know when it's done.", product.nameLong, update.productVersion), - [{ - label: nls.localize('neveragain', "Don't Show Again"), - isSecondary: true, - run: () => { - neverShowAgain.action.run(handle); - neverShowAgain.action.dispose(); - } - }] + [], + { + neverShowAgain: { id: 'neverShowAgain:update/win32-fast-updates', isSecondary: true } + } ); } diff --git a/src/vs/workbench/services/files/common/workspaceWatcher.ts b/src/vs/workbench/services/files/common/workspaceWatcher.ts index 7aa7ef157f688..46badeabf8069 100644 --- a/src/vs/workbench/services/files/common/workspaceWatcher.ts +++ b/src/vs/workbench/services/files/common/workspaceWatcher.ts @@ -13,8 +13,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { ResourceMap } from 'vs/base/common/map'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity, NeverShowAgainScope } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; import { FileService } from 'vs/platform/files/common/fileService'; @@ -26,8 +25,7 @@ export class WorkspaceWatcher extends Disposable { @IFileService private readonly fileService: FileService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @INotificationService private readonly notificationService: INotificationService, - @IStorageService private readonly storageService: IStorageService + @INotificationService private readonly notificationService: INotificationService ) { super(); @@ -73,38 +71,34 @@ export class WorkspaceWatcher extends Disposable { onUnexpectedError(msg); // Detect if we run < .NET Framework 4.5 - if (msg.indexOf('System.MissingMethodException') >= 0 && !this.storageService.getBoolean('ignoreNetVersionError', StorageScope.WORKSPACE)) { + if (msg.indexOf('System.MissingMethodException') >= 0) { this.notificationService.prompt( Severity.Warning, localize('netVersionError', "The Microsoft .NET Framework 4.5 is required. Please follow the link to install it."), [{ label: localize('installNet', "Download .NET Framework 4.5"), run: () => window.open('https://go.microsoft.com/fwlink/?LinkId=786533') - }, - { - label: localize('neverShowAgain', "Don't Show Again"), - isSecondary: true, - run: () => this.storageService.store('ignoreNetVersionError', true, StorageScope.WORKSPACE) }], - { sticky: true } + { + sticky: true, + neverShowAgain: { id: 'ignoreNetVersionError', isSecondary: true, scope: NeverShowAgainScope.WORKSPACE } + } ); } // Detect if we run into ENOSPC issues - if (msg.indexOf('ENOSPC') >= 0 && !this.storageService.getBoolean('ignoreEnospcError', StorageScope.WORKSPACE)) { + if (msg.indexOf('ENOSPC') >= 0) { this.notificationService.prompt( Severity.Warning, localize('enospcError', "Unable to watch for file changes in this large workspace. Please follow the instructions link to resolve this issue."), [{ label: localize('learnMore', "Instructions"), run: () => window.open('https://go.microsoft.com/fwlink/?linkid=867693') - }, - { - label: localize('neverShowAgain', "Don't Show Again"), - isSecondary: true, - run: () => this.storageService.store('ignoreEnospcError', true, StorageScope.WORKSPACE) }], - { sticky: true } + { + sticky: true, + neverShowAgain: { id: 'ignoreEnospcError', isSecondary: true, scope: NeverShowAgainScope.WORKSPACE } + } ); } } @@ -157,4 +151,4 @@ export class WorkspaceWatcher extends Disposable { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceWatcher, LifecyclePhase.Restored); \ No newline at end of file +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspaceWatcher, LifecyclePhase.Restored); diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index 81ba282a7c359..e701a079c296c 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification } from 'vs/platform/notification/common/notification'; +import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, NeverShowAgainScope } from 'vs/platform/notification/common/notification'; import { INotificationsModel, NotificationsModel, ChoiceAction } from 'vs/workbench/common/notifications'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; @@ -63,11 +63,12 @@ export class NotificationService extends Disposable implements INotificationServ // Handle neverShowAgain option accordingly let handle: INotificationHandle; if (notification.neverShowAgain) { + const scope = notification.neverShowAgain.scope === NeverShowAgainScope.WORKSPACE ? StorageScope.WORKSPACE : StorageScope.GLOBAL; // If the user already picked to not show the notification // again, we return with a no-op notification here const id = notification.neverShowAgain.id; - if (this.storageService.getBoolean(id, StorageScope.GLOBAL)) { + if (this.storageService.getBoolean(id, scope)) { return new NoOpNotification(); } @@ -80,7 +81,7 @@ export class NotificationService extends Disposable implements INotificationServ handle.close(); // Remember choice - this.storageService.store(id, true, StorageScope.GLOBAL); + this.storageService.store(id, true, scope); return Promise.resolve(); })); @@ -110,17 +111,18 @@ export class NotificationService extends Disposable implements INotificationServ // Handle neverShowAgain option accordingly if (options && options.neverShowAgain) { + const scope = options.neverShowAgain.scope === NeverShowAgainScope.WORKSPACE ? StorageScope.WORKSPACE : StorageScope.GLOBAL; // If the user already picked to not show the notification // again, we return with a no-op notification here const id = options.neverShowAgain.id; - if (this.storageService.getBoolean(id, StorageScope.GLOBAL)) { + if (this.storageService.getBoolean(id, scope)) { return new NoOpNotification(); } const neverShowAgainChoice = { label: nls.localize('neverShowAgain', "Don't Show Again"), - run: () => this.storageService.store(id, true, StorageScope.GLOBAL), + run: () => this.storageService.store(id, true, scope), isSecondary: options.neverShowAgain.isSecondary }; From 329f2fe76b86f244ca310dc77464fa623ead3baa Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 16 Aug 2019 06:39:38 -0700 Subject: [PATCH 289/613] Fix smoke tests --- test/smoke/src/vscode/puppeteerDriver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 6b1f51d8ce8d0..955460f9d0a0d 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -95,6 +95,7 @@ export async function launch(_args: string[]): Promise { await promisify(mkdir)(webUserDataDir); server = spawn(join(args[0], `resources/server/web.${process.platform === 'win32' ? 'bat' : 'sh'}`), ['--browser', 'none', '--driver', 'web', '--web-user-data-dir', webUserDataDir]); server.stderr.on('data', e => console.log('Server stderr: ' + e)); + server.stdout.on('data', e => console.log('Server stdout: ' + e)); process.on('exit', teardown); process.on('SIGINT', teardown); endpoint = await waitForEndpoint(); @@ -129,8 +130,7 @@ export function connect(headless: boolean, outPath: string, handle: string): Pro }); const page = (await browser.pages())[0]; await page.setViewport({ width, height }); - const endpointSplit = endpoint!.split('#'); - await page.goto(`${endpointSplit[0]}?folder=${args![1]}#${endpointSplit[1]}`); + await page.goto(`${endpoint}&folder=${args![1]}`); const result = { client: { dispose: () => teardown }, driver: buildDriver(browser, page) From db8368395f65b584eec79e74e8d4a47aa0c60349 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 16 Aug 2019 06:41:28 -0700 Subject: [PATCH 290/613] Revert "build - disable smoketest" This reverts commit c23cacdc97b54e6056400599dad329cabc7facfb. --- .../darwin/product-build-darwin.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 5718cfa56bd66..175e3d0eb08a2 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -107,15 +107,15 @@ steps: displayName: Run integration tests condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) -# - script: | -# set -e -# cd test/smoke -# yarn compile -# cd - -# yarn smoketest --web --headless -# continueOnError: true -# displayName: Run smoke tests -# condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) +- script: | + set -e + cd test/smoke + yarn compile + cd - + yarn smoketest --web --headless + continueOnError: true + displayName: Run smoke tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e From f411f6672c51126a55a3948ede98593467b38c46 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 16 Aug 2019 07:24:16 -0700 Subject: [PATCH 291/613] Scope new variable to a string --- .../terminal/common/terminalEnvironment.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index 9d6215af30c98..ab99f2cf048b3 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -208,33 +208,33 @@ export function getDefaultShell( if (!maybeExecutable) { maybeExecutable = getShellSetting(fetchSetting, isWorkspaceShellAllowed, 'shell', platformOverride); } - maybeExecutable = maybeExecutable || defaultShell; + let executable: string = maybeExecutable || defaultShell; // Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's // safe to assume that this was used by accident as Sysnative does not // exist and will break the terminal in non-WoW64 environments. if ((platformOverride === platform.Platform.Windows) && !isWoW64 && windir) { const sysnativePath = path.join(windir, 'Sysnative').replace(/\//g, '\\').toLowerCase(); - if (maybeExecutable && maybeExecutable.toLowerCase().indexOf(sysnativePath) === 0) { - maybeExecutable = path.join(windir, 'System32', maybeExecutable.substr(sysnativePath.length + 1)); + if (executable && executable.toLowerCase().indexOf(sysnativePath) === 0) { + executable = path.join(windir, 'System32', executable.substr(sysnativePath.length + 1)); } } // Convert / to \ on Windows for convenience - if (maybeExecutable && platformOverride === platform.Platform.Windows) { - maybeExecutable = maybeExecutable.replace(/\//g, '\\'); + if (executable && platformOverride === platform.Platform.Windows) { + executable = executable.replace(/\//g, '\\'); } if (configurationResolverService) { try { - maybeExecutable = configurationResolverService.resolve(lastActiveWorkspace, maybeExecutable); + executable = configurationResolverService.resolve(lastActiveWorkspace, executable); } catch (e) { logService.error(`Could not resolve shell`, e); - maybeExecutable = maybeExecutable; + executable = executable; } } - return maybeExecutable; + return executable; } export function getDefaultShellArgs( From 90b64a2f17ff19da19906b3fd422d7a8c8d4a1fd Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 16 Aug 2019 07:27:40 -0700 Subject: [PATCH 292/613] Fallback to default when cwd var cannot be resolved Fixes #79281 --- .../terminal/common/terminalEnvironment.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index ab99f2cf048b3..e6936280a4603 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -144,18 +144,20 @@ export function getCwd( try { customCwd = configurationResolverService.resolve(lastActiveWorkspace, customCwd); } catch (e) { - // There was an issue resolving a variable, just use the unresolved customCwd which - // which will fail, and log the error in the console. + // There was an issue resolving a variable, log the error in the console and + // fallback to the default. if (logService) { logService.error('Could not resolve terminal.integrated.cwd', e); } - return customCwd; + customCwd = undefined; } } - if (path.isAbsolute(customCwd)) { - cwd = customCwd; - } else if (root) { - cwd = path.join(root.fsPath, customCwd); + if (customCwd) { + if (path.isAbsolute(customCwd)) { + cwd = customCwd; + } else if (root) { + cwd = path.join(root.fsPath, customCwd); + } } } From 5f593c7a797acb59449de8b38281aa461340fbb5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 16 Aug 2019 11:53:56 +0200 Subject: [PATCH 293/613] fix error when dismissing snippets picker --- src/vs/workbench/contrib/snippets/browser/insertSnippet.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts index 5c84fba277593..e15578264ec55 100644 --- a/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts +++ b/src/vs/workbench/contrib/snippets/browser/insertSnippet.ts @@ -168,13 +168,14 @@ class InsertSnippetAction extends EditorAction { return quickInputService.pick(picks, { matchOnDetail: true }).then(pick => resolve(pick && pick.snippet), reject); } }).then(async snippet => { + if (!snippet) { + return; + } let clipboardText: string | undefined; if (snippet.needsClipboard) { clipboardText = await clipboardService.readText(); } - if (snippet) { - SnippetController2.get(editor).insert(snippet.codeSnippet, { clipboardText }); - } + SnippetController2.get(editor).insert(snippet.codeSnippet, { clipboardText }); }); } } From dc5fdf7b625311d12bf81bd676e368ad725b2041 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 16 Aug 2019 16:03:36 +0200 Subject: [PATCH 294/613] add fetch file system provider --- src/vs/base/common/errors.ts | 10 +++ .../browser/webWorkerFileSystemProvider.ts | 62 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider.ts diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index da6f814332f62..b8077dd537647 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -194,3 +194,13 @@ export function getErrorMessage(err: any): string { return String(err); } + + +export class NotImplementedError extends Error { + constructor(message?: string) { + super('NotImplemented'); + if (message) { + this.message = message; + } + } +} diff --git a/src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider.ts b/src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider.ts new file mode 100644 index 0000000000000..13afbbcce1fad --- /dev/null +++ b/src/vs/workbench/services/extensions/browser/webWorkerFileSystemProvider.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IFileSystemProvider, FileSystemProviderCapabilities, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileSystemProviderError, FileSystemProviderErrorCode } from 'vs/platform/files/common/files'; + +import { Event } from 'vs/base/common/event'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { NotImplementedError } from 'vs/base/common/errors'; + +export class FetchFileSystemProvider implements IFileSystemProvider { + + readonly capabilities = FileSystemProviderCapabilities.Readonly + FileSystemProviderCapabilities.FileReadWrite + FileSystemProviderCapabilities.PathCaseSensitive; + readonly onDidChangeCapabilities = Event.None; + readonly onDidChangeFile = Event.None; + + // working implementations + async readFile(resource: URI): Promise { + try { + const res = await fetch(resource.toString(true)); + if (res.status === 200) { + return new Uint8Array(await res.arrayBuffer()); + } + throw new FileSystemProviderError(res.statusText, FileSystemProviderErrorCode.Unknown); + } catch (err) { + throw new FileSystemProviderError(err, FileSystemProviderErrorCode.Unknown); + } + } + + // fake implementations + async stat(_resource: URI): Promise { + return { + type: FileType.File, + size: 0, + mtime: 0, + ctime: 0 + }; + } + + watch(): IDisposable { + return Disposable.None; + } + + // error implementations + writeFile(_resource: URI, _content: Uint8Array, _opts: FileWriteOptions): Promise { + throw new NotImplementedError(); + } + readdir(_resource: URI): Promise<[string, FileType][]> { + throw new NotImplementedError(); + } + mkdir(_resource: URI): Promise { + throw new NotImplementedError(); + } + delete(_resource: URI, _opts: FileDeleteOptions): Promise { + throw new NotImplementedError(); + } + rename(_from: URI, _to: URI, _opts: FileOverwriteOptions): Promise { + throw new NotImplementedError(); + } +} From 811dea422e334fcee3c7459924669c57e8f7335d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 16 Aug 2019 16:35:19 +0200 Subject: [PATCH 295/613] add IStaticExtensionsService, add `staticExtensions` to embedder API --- src/vs/workbench/browser/web.main.ts | 5 +++ .../extensions/browser/extensionService.ts | 44 +++++++++++++++---- .../extensions/common/staticExtensions.ts | 34 ++++++++++++++ .../electron-browser/extensionService.ts | 23 +++++----- src/vs/workbench/workbench.desktop.main.ts | 2 + src/vs/workbench/workbench.web.api.ts | 6 +++ 6 files changed, 94 insertions(+), 20 deletions(-) create mode 100644 src/vs/workbench/services/extensions/common/staticExtensions.ts diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 7ee10dddb4123..e882484c10b7a 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -41,6 +41,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/theme/common/themeService'; import { InMemoryUserDataProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider'; import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; +import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; class CodeRendererMain extends Disposable { @@ -145,6 +146,10 @@ class CodeRendererMain extends Disposable { const fileService = this._register(new FileService(logService)); serviceCollection.set(IFileService, fileService); + // Static Extensions + const staticExtensions = new StaticExtensionsService(this.configuration.staticExtensions || []); + serviceCollection.set(IStaticExtensionsService, staticExtensions); + let userDataProvider: IFileSystemProvider | undefined = this.configuration.userDataProvider; const connection = remoteAgentService.getConnection(); if (connection) { diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 80e9fe37c3fda..59e7baf1a17b5 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -22,10 +22,16 @@ import { WebWorkerExtensionHostStarter } from 'vs/workbench/services/extensions/ import { URI } from 'vs/base/common/uri'; import { isWebExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browser/webWorkerFileSystemProvider'; +import { Schemas } from 'vs/base/common/network'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { - private _remoteExtensionsEnvironmentData: IRemoteAgentEnvironment | null; + private _disposables = new DisposableStore(); + private _remoteExtensionsEnvironmentData: IRemoteAgentEnvironment | null = null; constructor( @IInstantiationService instantiationService: IInstantiationService, @@ -37,6 +43,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IProductService productService: IProductService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, @IConfigurationService private readonly _configService: IConfigurationService, + @IStaticExtensionsService private readonly _staticExtensions: IStaticExtensionsService, ) { super( instantiationService, @@ -48,8 +55,19 @@ export class ExtensionService extends AbstractExtensionService implements IExten productService, ); - this._remoteExtensionsEnvironmentData = null; this._initialize(); + this._initFetchFileSystem(); + } + + dispose(): void { + this._disposables.dispose(); + super.dispose(); + } + + private _initFetchFileSystem(): void { + const provider = new FetchFileSystemProvider(); + this._disposables.add(this._fileService.registerProvider(Schemas.http, provider)); + this._disposables.add(this._fileService.registerProvider(Schemas.https, provider)); } private _createProvider(remoteAuthority: string): IInitDataProvider { @@ -84,23 +102,31 @@ export class ExtensionService extends AbstractExtensionService implements IExten protected async _scanAndHandleExtensions(): Promise { // fetch the remote environment - const remoteEnv = (await this._remoteAgentService.getEnvironment())!; + let [remoteEnv, localExtensions] = await Promise.all([ + >this._remoteAgentService.getEnvironment(), + this._staticExtensions.getExtensions() + ]); + + // local: only enabled and web'ish extension + localExtensions = localExtensions.filter(ext => this._isEnabled(ext) && isWebExtension(ext, this._configService)); + this._checkEnableProposedApi(localExtensions); - // enable or disable proposed API per extension + // remote: only enabled and none-web'ish extension + remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !isWebExtension(extension, this._configService)); this._checkEnableProposedApi(remoteEnv.extensions); - // remove disabled extensions - remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension)); + // in case of overlap, the remote wins + const isRemoteExtension = new Set(); + remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier))); + localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier))); // save for remote extension's init data this._remoteExtensionsEnvironmentData = remoteEnv; - // this._handleExtensionPoints(([]).concat(remoteEnv.extensions).concat(localExtensions)); - const result = this._registry.deltaExtensions(remoteEnv.extensions, []); + const result = this._registry.deltaExtensions(remoteEnv.extensions.concat(localExtensions), []); if (result.removedDueToLooping.length > 0) { this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); } - this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions()); } diff --git a/src/vs/workbench/services/extensions/common/staticExtensions.ts b/src/vs/workbench/services/extensions/common/staticExtensions.ts new file mode 100644 index 0000000000000..06f102534d1b0 --- /dev/null +++ b/src/vs/workbench/services/extensions/common/staticExtensions.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionDescription, IExtensionManifest, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { UriComponents, URI } from 'vs/base/common/uri'; + +export const IStaticExtensionsService = createDecorator('IStaticExtensionsService'); + +export interface IStaticExtensionsService { + _serviceBrand: any; + getExtensions(): Promise; +} + +export class StaticExtensionsService implements IStaticExtensionsService { + + _serviceBrand: any; + + private readonly _descriptions: IExtensionDescription[] = []; + + constructor(staticExtensions: { packageJSON: IExtensionManifest, extensionLocation: UriComponents }[]) { + this._descriptions = staticExtensions.map(data => { + identifier: new ExtensionIdentifier(`${data.packageJSON.publisher}.${data.packageJSON.name}`), + extensionLocation: URI.revive(data.extensionLocation), + ...data.packageJSON, + }); + } + + async getExtensions(): Promise { + return this._descriptions; + } +} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 272f399268f5c..b8c267ab59041 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -34,6 +34,8 @@ import { IFileService } from 'vs/platform/files/common/files'; import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IProductService } from 'vs/platform/product/common/product'; import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints'; +import { flatten } from 'vs/base/common/arrays'; +import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; class DeltaExtensionsQueueItem { constructor( @@ -64,6 +66,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IConfigurationService private readonly _configurationService: IConfigurationService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, @IWindowService protected readonly _windowService: IWindowService, + @IStaticExtensionsService private readonly _staticExtensions: IStaticExtensionsService, ) { super( instantiationService, @@ -72,7 +75,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten telemetryService, extensionEnablementService, fileService, - productService, + productService ); if (this._extensionEnablementService.allUserExtensionsDisabled) { @@ -432,7 +435,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten const remoteAuthority = this._environmentService.configuration.remoteAuthority; const extensionHost = this._extensionHostProcessManagers[0]; - let localExtensions = await this._extensionScanner.scannedExtensions; + let localExtensions = flatten(await Promise.all([this._extensionScanner.scannedExtensions, this._staticExtensions.getExtensions()])); // enable or disable proposed API per extension this._checkEnableProposedApi(localExtensions); @@ -458,7 +461,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten this._remoteAuthorityResolverService.setResolvedAuthorityError(remoteAuthority, err); // Proceed with the local extension host - await this._startLocalExtensionHost(extensionHost, localExtensions); + await this._startLocalExtensionHost(extensionHost, localExtensions, localExtensions.map(extension => extension.identifier)); return; } @@ -503,20 +506,18 @@ export class ExtensionService extends AbstractExtensionService implements IExten // save for remote extension's init data this._remoteExtensionsEnvironmentData.set(remoteAuthority, remoteEnv); - this._handleExtensionPoints(([]).concat(remoteEnv.extensions).concat(localExtensions)); - extensionHost.start(localExtensions.map(extension => extension.identifier)); - + await this._startLocalExtensionHost(extensionHost, remoteEnv.extensions.concat(localExtensions), localExtensions.map(extension => extension.identifier)); } else { - await this._startLocalExtensionHost(extensionHost, localExtensions); + await this._startLocalExtensionHost(extensionHost, localExtensions, localExtensions.map(extension => extension.identifier)); } } - private async _startLocalExtensionHost(extensionHost: ExtensionHostProcessManager, localExtensions: IExtensionDescription[]): Promise { - this._handleExtensionPoints(localExtensions); - extensionHost.start(localExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id))); + private async _startLocalExtensionHost(extensionHost: ExtensionHostProcessManager, allExtensions: IExtensionDescription[], localExtensions: ExtensionIdentifier[]): Promise { + this._registerAndHandleExtensions(allExtensions); + extensionHost.start(localExtensions.filter(id => this._registry.containsExtension(id))); } - private _handleExtensionPoints(allExtensions: IExtensionDescription[]): void { + private _registerAndHandleExtensions(allExtensions: IExtensionDescription[]): void { const result = this._registry.deltaExtensions(allExtensions, []); if (result.removedDueToLooping.length > 0) { this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 90f3e4c63d142..04981fd3a82af 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -73,6 +73,7 @@ import { IMenubarService } from 'vs/platform/menubar/common/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; import { IURLService } from 'vs/platform/url/common/url'; import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; +import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; registerSingleton(IClipboardService, ClipboardService, true); registerSingleton(IRequestService, RequestService, true); @@ -85,6 +86,7 @@ registerSingleton(IIssueService, IssueService); registerSingleton(IWorkspacesService, WorkspacesService); registerSingleton(IMenubarService, MenubarService); registerSingleton(IURLService, RelayURLService); +registerSingleton(IStaticExtensionsService, class extends StaticExtensionsService { constructor() { super([]); } }); //#endregion diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 2727fa5eb5d77..69c2fa84a65dc 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -9,6 +9,7 @@ import { UriComponents } from 'vs/base/common/uri'; import { IFileSystemProvider } from 'vs/platform/files/common/files'; import { IWebSocketFactory } from 'vs/platform/remote/browser/browserSocketFactory'; import { ICredentialsProvider } from 'vs/workbench/services/credentials/browser/credentialsService'; +import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; export interface IWorkbenchConstructionOptions { @@ -59,6 +60,11 @@ export interface IWorkbenchConstructionOptions { * Experimental: The credentials provider to store and retrieve secrets. */ credentialsProvider?: ICredentialsProvider; + + /** + * Experimental: Add static extensions that cannot be uninstalled but only be disabled. + */ + staticExtensions: { packageJSON: IExtensionManifest, extensionLocation: UriComponents }[]; } /** From bcca36044bfaaedeb9182114ed812ca7def4a671 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 16 Aug 2019 16:41:32 +0200 Subject: [PATCH 296/613] make staticExtensions optional --- src/vs/workbench/workbench.web.api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 69c2fa84a65dc..dd84853e87ab6 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -64,7 +64,7 @@ export interface IWorkbenchConstructionOptions { /** * Experimental: Add static extensions that cannot be uninstalled but only be disabled. */ - staticExtensions: { packageJSON: IExtensionManifest, extensionLocation: UriComponents }[]; + staticExtensions?: { packageJSON: IExtensionManifest, extensionLocation: UriComponents }[]; } /** From 2821335cb5aaafde1c4f3bfe337e009e94575e89 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 16 Aug 2019 07:45:07 -0700 Subject: [PATCH 297/613] Indicate web in smoke test step --- build/azure-pipelines/darwin/product-build-darwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 175e3d0eb08a2..84764519f5325 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -114,7 +114,7 @@ steps: cd - yarn smoketest --web --headless continueOnError: true - displayName: Run smoke tests + displayName: Run web smoke tests condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | From a90999e63537a24c7f1d3cce24db490066dd14f5 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 16 Aug 2019 16:54:15 +0200 Subject: [PATCH 298/613] Update octicon css logic --- src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css index 8a387b335ba39..841e5e575cde2 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons-main.css @@ -7,7 +7,7 @@ body { } .octicon, .mega-octicon { - font-family: var(--version); + font-family: var(--version) !important; } body[data-octicons-update="enabled"] .monaco-workbench .part.statusbar > .items-container > .statusbar-item span.octicon { From d71800cbc594b20606b58347e34eecc23aa235cb Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 16 Aug 2019 16:55:38 +0200 Subject: [PATCH 299/613] Fix Octicon icons on Linux --- .../ui/octiconLabel/octicons/octicons2.css | 4 ++-- .../ui/octiconLabel/octicons/octicons2.svg | 14 +++++++------- .../ui/octiconLabel/octicons/octicons2.ttf | Bin 35152 -> 35276 bytes 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css index 1b262b5defa45..6da9003b49a79 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css @@ -1,7 +1,7 @@ @font-face { font-family: "octicons2"; - src: url("./octicons2.ttf?be4194a3b16397b3f3d1c63861eae9a2") format("truetype"), -url("./octicons2.svg?be4194a3b16397b3f3d1c63861eae9a2#octicons2") format("svg"); + src: url("./octicons2.ttf?064476e75412ccad4ae99d4bf24539b4") format("truetype"), +url("./octicons2.svg?064476e75412ccad4ae99d4bf24539b4#octicons2") format("svg"); } .octicon, .mega-octicon { diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg index f12b9fef3013f..e7d0d05d458b0 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg @@ -45,13 +45,13 @@ horiz-adv-x="1000" d=" M868.30625 -27.375L624.99625 444.625V694.625H687.49375V757.125H624.62125V757.5L596.12125 757.1875H312.49625V695H374.99625V448L131.62125 -27.5625C126.889375 -37.0812500000001 124.665625 -47.65625 125.15875 -58.275C125.6525 -68.9 128.8475 -79.21875 134.44125 -88.25625C140.035625 -97.2999999999999 147.84375 -104.7625 157.1275 -109.95C166.41125 -115.13125 176.86375 -117.8562499999999 187.49625 -117.875H812.49375C823.1500000000001 -117.8625000000001 833.61875 -115.13125 842.91875 -109.93125C852.21875 -104.7374999999999 860.0375 -97.25 865.6312499999999 -88.1812500000001C871.21875 -79.1124999999999 874.4 -68.7687499999999 874.86875 -58.125C875.3375 -47.4875 873.0812500000001 -36.9 868.30625 -27.375zM430.8087500000001 419.5L437.49625 432.9375V692.75L562.49625 694.25V444.5625V429.4375L569.4337499999999 416L683.55625 194.625H315.55875L430.8087500000001 419.5zM187.49625 -56.0625L283.80875 132.125H715.99375L812.68125 -55.375L187.49625 -56.0625z" /> + horiz-adv-x="1000" d=" M837.5 157.5C818.75 207.5018750000001 812.5 257.5018750000001 812.5 307.501875V432.501875C812.5 513.751875 787.5 588.751875 731.25 645.0018749999999C681.25 707.501875 606.25 745.001875 531.25 751.251875C487.5 757.5019374999999 443.75 757.5019374999999 400 738.751875C356.25 720.001875 318.75 707.501875 287.5 676.2518749999999C256.25 645.0018749999999 231.25 613.7518749999999 212.5 570.001875C193.75 532.5018749999999 187.5 488.751875 187.5 445.001875V307.501875C187.5 257.5018750000001 175 207.5018750000001 162.5 157.5L125 51.25L156.25 7.5H375C375 -23.75 387.5 -55 412.5 -80C431.25 -105 462.5 -117.5 500 -117.5C537.5 -117.5 562.5 -105 587.5 -80C612.5 -55 625 -23.75 625 7.5H843.75L875 51.25L837.5 157.5zM543.75 -36.25C531.25 -48.75 512.5 -55 500 -55C487.5 -55 468.75 -48.75 456.25 -36.25C443.75 -23.75 437.5 -11.25 437.5 7.5H500H562.5C562.5 -11.25 556.25 -23.75 543.75 -36.25zM587.5 70H562.5H437.5H412.5H200L225 138.75C237.5 195.0018750000001 250 251.251875 250 307.501875V445.001875C250 482.501875 256.25 513.751875 268.75 545.001875C281.25 576.2518749999999 306.25 607.5018749999999 331.25 626.251875C356.25 651.251875 387.5 670.0018749999999 418.75 676.2518749999999C456.25 695.001875 487.5 695.001875 525 695.001875C587.5 688.751875 643.75 657.501875 687.5 607.5018749999999C731.25 557.5018749999999 750 495.001875 750 432.501875V307.501875C750 251.251875 756.25 195.0018750000001 775 138.75L800 70H587.5V70z" /> + horiz-adv-x="1000" d=" M906.25 695H562.5L537.5 688.75L500 645L462.5 688.75L437.5 695H93.75L62.5 663.75V38.75L93.75 7.5H425L481.25 -48.75H525L575 7.5H906.25L937.5 38.75V663.75L906.25 695zM456.25 63.75L437.5 70H125V632.5H425L468.75 588.75V51.25L456.25 63.75zM875 70H562.5L537.5 63.75L531.25 57.5V588.75L575 632.5H875V70zM375 507.5H187.5V445H375V507.5zM375 382.5H187.5V320H375V382.5zM187.5 257.5H375V195H187.5V257.5zM812.5 507.5H625V445H812.5V507.5zM625 382.5H812.5V320H625V382.5zM625 257.5H812.5V195H625V257.5z" /> @@ -162,22 +162,22 @@ horiz-adv-x="1000" d=" M93.75 757.5H906.25L937.5 726.25V-86.25L906.25 -117.5H93.75L62.5 -86.25V726.25L93.75 757.5zM125 -55H875V695H125V-55zM250 257.5H437.5V70L750 320L437.5 570V382.5H250V257.5z" /> + horiz-adv-x="1000" d=" M125 601.25L156.25 632.5H468.75L500 601.25V38.75L468.75 7.5H156.25L125 38.75V601.25zM187.5 70H437.5V445H187.5V70zM187.5 507.5H437.5V570H187.5V507.5zM593.75 632.5H906.25L937.5 601.25V38.75L906.25 7.5H593.75L562.5 38.75V601.25L593.75 632.5zM625 70H875V195H625V70zM625 320H875V570H625V320z" /> + horiz-adv-x="1000" d=" M537.5 757.5009375C637.5 751.250625 731.25 701.250625 800 632.500625C881.25 545.000625 925 438.750625 925 313.750625C925 213.750625 887.5 120 825 38.75C762.5 -36.25 675 -92.5 575 -111.25C475 -130 375 -117.5 287.5 -67.5C200 -17.5 131.25 57.5 93.75 151.25C56.2498125 245.000625 49.9998125 351.250625 81.25 445.000625C112.5 545.000625 168.75 626.250625 256.25 682.500625C337.5 738.750625 437.5 763.7509375 537.5 757.5009375zM568.75 -48.75C650 -30 725 13.75 781.25 82.5C831.25 151.25 862.5 232.5006249999999 856.25 320.000625C856.25 420.000625 818.75 520.000625 750 588.750625C687.5 651.250625 612.5 688.750625 525 695.000625C443.75 701.250625 356.25 682.500625 287.5 632.500625C218.75 582.500625 168.75 513.750625 143.75 426.250625C118.75 345.000625 118.75 257.500625 156.25 176.25C193.75 95 250 32.5 325 -11.25C400 -55 487.5 -67.5 568.75 -48.75zM493.749375 351.25L643.75 507.5L687.5 463.75L537.499375 307.5L687.5 151.25L643.75 107.5L493.749375 263.75L343.7493750000001 107.5L299.999375 151.25L449.999375 307.5L299.999375 463.75L343.7493750000001 507.5L493.749375 351.25z" /> + horiz-adv-x="1000" d=" M500 695C406.25 695 325 670 256.25 620L306.25 576.25C362.5 613.75 425 632.5 500 632.5C706.25 632.5 875 463.75 875 257.5H937.5C937.5 501.25 743.75 695 500 695zM62.5 632.496875L162.5 538.746875C100 463.746875 62.5 363.7468750000001 62.5 257.4968750000001H125C125 351.246875 156.25 432.496875 212.5 494.996875L350 369.996875C325 338.746875 312.5 301.246875 312.5 257.4968750000001C312.5 151.25 393.75 70 500 70C550 70 593.75 88.75 625 120L812.5 -55L856.25 -11.25L106.25 676.246875L62.5 632.496875zM393.75 326.246875L575 157.5C556.25 145 531.25 132.5 500 132.5C431.25 132.5 375 188.75 375 257.4968750000001C375 282.4968750000001 381.25 307.4968750000001 393.75 326.246875zM687.5 226.25L625 282.5C612.5 332.5 568.75 376.25 512.5 382.5L450 438.75C468.75 445 481.25 445 500 445C606.25 445 687.5 363.75 687.5 257.5V226.25z" /> + horiz-adv-x="1000" d=" M62.5 195C62.5 438.75 256.25 632.5 500 632.5C743.75 632.5 937.5 438.75 937.5 195H875C875 401.25 706.25 570 500 570C293.75 570 125 401.25 125 195H62.5zM312.5 195C312.5 301.25 393.75 382.5 500 382.5C606.25 382.5 687.5 301.25 687.5 195C687.5 88.75 606.25 7.5 500 7.5C393.75 7.5 312.5 88.75 312.5 195zM375 195C375 126.25 431.25 70 500 70C568.75 70 625 126.25 625 195C625 263.75 568.75 320 500 320C431.25 320 375 263.75 375 195z" /> @@ -186,7 +186,7 @@ horiz-adv-x="1000" d=" M660.625 748.75L865.625 542.5L875 520V-86.25L843.75 -117.5H156.25L125 -86.25V726.25L156.25 757.5H638.75L660.625 748.75zM625 507.5H812.5L625 695V507.5zM187.5 695V-55H812.5V445H593.75L562.5 476.25V695H187.5zM316.375 224.1875L429.9375 338.4375L385.625 382.5L250 246.3125V202.125L385.6875 66.25L429.875 110.4375L316.375 224.1875zM550 337.875L593.75 382.1875L730.5625 246.25V201.9375L593.75 66.125L549.6875 110.4375L664.125 224.1875L550 337.875z" /> + horiz-adv-x="1000" d=" M906.25 632.5H481.25L431.25 688.75L406.25 695H93.75L62.5 663.75V413.75V-23.75L93.75 -55H906.25L937.5 -23.75V257.5V601.25L906.25 632.5zM875 101.25V7.5H125V101.25V351.25V382.5H406.25L431.25 388.75L481.25 445H875V351.25V101.25zM875 507.5H468.75L443.75 501.25L393.75 445H125V632.5H393.75L450 576.25L468.75 570H875V507.5z" /> diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.ttf b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.ttf index 97d221792967c3dc9182078b5514358637c80843..654cfe91291a4b391122cf0b76277f89e8723e80 100644 GIT binary patch delta 1648 zcmXw2Yi!e26u#GqokyFbj*~b}+c{`XjsQT_0@0UD2oY zqxuQlg^PGSz8desCx{F&NZd0lH;fu4jb>w`ai{ULan!WTbjdVj_LzIjXUr4kM`iA^ zWo4aZePt8nMEUOWlNQXf!t#aXUn_6zvc9k_x1F&~RkT(Nkga4ld5XMl&)P>Qj5<#J zS#WeXo;r^^CtbT;y{_NrEPar^L>Jv1?m_q8Oe539OnS;an>?p0U6nnR533SY8>;@Q z?ybJ&6};=bz25WePIic$^fmhS`>t{Xm*Ea_U-1n!*8?EX8R!%ALcOp?7!*c@`#~a@ z3bqDs*E(uPYH!s(4($rvn$x^;&h;=K?hcQJr^HsVPrNKXs*BZi*G)y{My^DP(QNd5 zY-{XL>{{Fv-xWU@pOUQ70%?tORj!eD%3sRg%aihb`9-23F_h$!yOJZxhbi+|E%gsp z*n&L)bs!B|00;>4VIn95BQPi!2pc2}vsSHzHb~137wv^o7E8(sZiRdV1r9>-b4p4{ zp>LHe)FLG%dokKo4IM1hEnytaP@q$-srIuJU*RD=BxPZs0WTNWI}}y0)TKiLb%!lp zFfk6sZpxeeOz|_uqBl||o(e^oDzYZ>*1`%K!zWn=9U@u( z*RBrubHA5%7a(VYQOFrcnxkcfQ%D%(NRB20XrEL#-0zf~=rte@XJ>6k*IWFY5duu{ zEX%^Cv?#$QU(stX-hiSA2Z|GnL4;zl{F|A9?HHdK*hNAV33mDjtEw!vWBROzz+`dy zE=3R+CFXNkWKbIQ#qTyBFQ?R8ZuzHjn+dm7)T}s=%hu1^zmTKg$zgG5 zI6O2I9v%|4PWOc0DUcutVjzpa@di32OC^M|MtT9Kt%%`?ltL4Fx>=HQ8MS@W^sgPUB+S-@4+}8fk_aqtkB6u2(03sq z@G6fQX`Ua-SqkMwGSMPo7n?*iAvLesxMx%Q+ao|B?2G23^@*0a$9+!3&J|Cc zWw^OqXqhUgcv=s&LqUnLKsSQz12m`saR8KrCbtxENJWFe~4p@|eK2#%D6 zi3nW9TVZYj$udIZ65?K698C40PGmsw)Mp=_Qg-`A6?6%e>H#<~fAm7LXN!6F;}^_re*%MQ0id|^dV2MLr>IJo;v1uZjE=^Ty(hg`Z;uK!SPvJl4Jh~3uZe5vh6Qjf|@teL|zeiuy zU)KL(a2whTn+=nOyGGu))_Ac_U-weoiMn}H&UDFaGH)|a)qCpKSv;0@%NEO_2GB6j zaM@b4PL9}E+XZ{kUUjrMIvwwlfGm)&k+V+LxzYKd%jn9vs;=wqkh|o*-MFIhOydI& z<9WZSv#H#)KzXT7Y8RcN`{=6I=xy^J^v-z|U%|JB(FP6$z6$ce?%)+x%~I?}c8ERA z&an?U4|k0FE;JB296A@4!-pe1j>w_Ne6&3}6rJKdd^i6F|0#bz)*hRR{T)9LpG|rdSnEily zdY1}1rOo?SWVd&Pp)z=jY>akom73S)ywF1`#{;P#6gvxpVOK0*wFY9Y$j(CN=n7`< z^AQ|xSwEOnj<4!2j!K&fJ`El#tWWGOcCX620|9r%NN#VrsXB$72laqNunb^91jG@v z3_y?+)4sHY^O<1+`fv*w^jUnQg(LG2-IyysHah#@Nr{)>GhVN9k_?3$$_bi=>y)dI zj~fa)61FI}qVIja9FJy}Lnq5L_6kje!vwwfHl4%7#NvX1AP7w4pUdaa1v5*-H0Kl= zHd_%Ije1x^Ks+kHMda+Fr149l#cvlOV#0l#1%9hsMPJX*xko7A*;-ca!4||eM`L@v zi`Quy6Bd7m!~1ebqP(>a6PFh&=OYIVWz{4)FdGO1sj{x;`G@}kclH3LsTP)tQ{Yzc^cKLI+d?sTP#{}c;C!`zacYg@j`&19fr5d0K2MiUaUC{*qthn$6zc|w|zi5W`y2%eLtw Date: Fri, 16 Aug 2019 17:16:41 +0200 Subject: [PATCH 300/613] debug: introduce data breakpoints --- .../api/browser/mainThreadDebugService.ts | 29 ++++-- .../workbench/api/common/extHost.protocol.ts | 15 +++- src/vs/workbench/api/common/extHostTypes.ts | 18 ++++ .../workbench/api/node/extHostDebugService.ts | 7 +- .../contrib/debug/browser/breakpointsView.ts | 90 +++++++++++++++++-- .../contrib/debug/browser/debugActions.ts | 6 +- .../contrib/debug/browser/debugCommands.ts | 4 +- .../contrib/debug/browser/debugService.ts | 46 +++++++++- .../contrib/debug/browser/debugSession.ts | 30 ++++++- .../contrib/debug/browser/rawDebugSession.ts | 14 +++ .../contrib/debug/browser/variablesView.ts | 24 +++-- .../workbench/contrib/debug/common/debug.ts | 27 +++++- .../contrib/debug/common/debugModel.ts | 71 ++++++++++++++- .../debug/test/browser/debugModel.test.ts | 2 +- .../contrib/debug/test/common/mockDebug.ts | 16 +++- 15 files changed, 356 insertions(+), 43 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 20572fa77839c..2f0f5283bfd21 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -5,10 +5,10 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI as uri } from 'vs/base/common/uri'; -import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext, - IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto + IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto, IDataBreakpointDto } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import severity from 'vs/base/common/severity'; @@ -110,15 +110,16 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb // send all breakpoints const bps = this.debugService.getModel().getBreakpoints(); const fbps = this.debugService.getModel().getFunctionBreakpoints(); + const dbps = this.debugService.getModel().getDataBreakpoints(); if (bps.length > 0 || fbps.length > 0) { this._proxy.$acceptBreakpointsDelta({ - added: this.convertToDto(bps).concat(this.convertToDto(fbps)) + added: this.convertToDto(bps).concat(this.convertToDto(fbps)).concat(this.convertToDto(dbps)) }); } } } - public $registerBreakpoints(DTOs: Array): Promise { + public $registerBreakpoints(DTOs: Array): Promise { for (let dto of DTOs) { if (dto.type === 'sourceMulti') { @@ -136,14 +137,17 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb this.debugService.addBreakpoints(uri.revive(dto.uri), rawbps, 'extension'); } else if (dto.type === 'function') { this.debugService.addFunctionBreakpoint(dto.functionName, dto.id); + } else if (dto.type === 'data') { + this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist); } } return Promise.resolve(); } - public $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): Promise { + public $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[], dataBreakpointIds: string[]): Promise { breakpointIds.forEach(id => this.debugService.removeBreakpoints(id)); functionBreakpointIds.forEach(id => this.debugService.removeFunctionBreakpoints(id)); + dataBreakpointIds.forEach(id => this.debugService.removeDataBreakpoints(id)); return Promise.resolve(); } @@ -294,7 +298,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return undefined; } - private convertToDto(bps: (ReadonlyArray)): Array { + private convertToDto(bps: (ReadonlyArray)): Array { return bps.map(bp => { if ('name' in bp) { const fbp = bp; @@ -307,6 +311,19 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb logMessage: fbp.logMessage, functionName: fbp.name }; + } else if ('dataId' in bp) { + const dbp = bp; + return { + type: 'data', + id: dbp.getId(), + dataId: dbp.dataId, + enabled: dbp.enabled, + condition: dbp.condition, + hitCondition: dbp.hitCondition, + logMessage: dbp.logMessage, + label: dbp.label, + canPersist: dbp.canPersist + }; } else { const sbp = bp; return { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 1cc498a9ca48a..2b1f57a09d635 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -715,8 +715,8 @@ export interface MainThreadDebugServiceShape extends IDisposable { $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise; $appendDebugConsole(value: string): void; $startBreakpointEvents(): void; - $registerBreakpoints(breakpoints: Array): Promise; - $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): Promise; + $registerBreakpoints(breakpoints: Array): Promise; + $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[], dataBreakpointIds: string[]): Promise; } export interface IOpenUriOptions { @@ -1198,6 +1198,13 @@ export interface IFunctionBreakpointDto extends IBreakpointDto { functionName: string; } +export interface IDataBreakpointDto extends IBreakpointDto { + type: 'data'; + dataId: string; + canPersist: boolean; + label: string; +} + export interface ISourceBreakpointDto extends IBreakpointDto { type: 'source'; uri: UriComponents; @@ -1206,9 +1213,9 @@ export interface ISourceBreakpointDto extends IBreakpointDto { } export interface IBreakpointsDeltaDto { - added?: Array; + added?: Array; removed?: string[]; - changed?: Array; + changed?: Array; } export interface ISourceMultiBreakpointDto { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index e3c30b6e3a070..9084c6170571f 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2166,6 +2166,24 @@ export class FunctionBreakpoint extends Breakpoint { } } +@es5ClassCompat +export class DataBreakpoint extends Breakpoint { + readonly label: string; + readonly dataId: string; + readonly canPersist: boolean; + + constructor(label: string, dataId: string, canPersist: boolean, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) { + super(enabled, condition, hitCondition, logMessage); + if (!dataId) { + throw illegalArgument('dataId'); + } + this.label = label; + this.dataId = dataId; + this.canPersist = canPersist; + } +} + + @es5ClassCompat export class DebugAdapterExecutable implements vscode.DebugAdapterExecutable { readonly command: string; diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 87adb02d7483c..c82f788a37098 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -14,7 +14,7 @@ import { IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto } from 'vs/workbench/api/common/extHost.protocol'; import * as vscode from 'vscode'; -import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable } from 'vs/workbench/api/common/extHostTypes'; +import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DataBreakpoint } from 'vs/workbench/api/common/extHostTypes'; import { ExecutableDebugAdapter, SocketDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; @@ -248,7 +248,8 @@ export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugSe // unregister with VS Code const ids = breakpoints.filter(bp => bp instanceof SourceBreakpoint).map(bp => bp.id); const fids = breakpoints.filter(bp => bp instanceof FunctionBreakpoint).map(bp => bp.id); - return this._debugServiceProxy.$unregisterBreakpoints(ids, fids); + const dids = breakpoints.filter(bp => bp instanceof DataBreakpoint).map(bp => bp.id); + return this._debugServiceProxy.$unregisterBreakpoints(ids, fids, dids); } public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession): Promise { @@ -554,6 +555,8 @@ export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugSe let bp: vscode.Breakpoint; if (bpd.type === 'function') { bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); + } else if (bpd.type === 'data') { + bp = new DataBreakpoint(bpd.label, bpd.dataId, bpd.canPersist, bpd.enabled, bpd.hitCondition, bpd.condition, bpd.logMessage); } else { const uri = URI.revive(bpd.uri); bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 4c8f5b36ee8d7..e1f0c4496a0dc 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -8,7 +8,7 @@ import * as resources from 'vs/base/common/resources'; import * as dom from 'vs/base/browser/dom'; import { IAction, Action } from 'vs/base/common/actions'; import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, EDITOR_CONTRIBUTION_ID, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugEditorContribution } from 'vs/workbench/contrib/debug/common/debug'; -import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; +import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction, RemoveBreakpointAction, EnableAllBreakpointsAction, DisableAllBreakpointsAction, ReapplyBreakpointsAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -74,6 +74,7 @@ export class BreakpointsView extends ViewletPanel { this.instantiationService.createInstance(BreakpointsRenderer), new ExceptionBreakpointsRenderer(this.debugService), this.instantiationService.createInstance(FunctionBreakpointsRenderer), + this.instantiationService.createInstance(DataBreakpointsRenderer), new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService) ], { identityProvider: { getId: (element: IEnablement) => element.getId() }, @@ -91,7 +92,7 @@ export class BreakpointsView extends ViewletPanel { this._register(this.list.onContextMenu(this.onListContextMenu, this)); - this._register(this.list.onDidOpen(e => { + this._register(this.list.onDidOpen(async e => { let isSingleClick = false; let isDoubleClick = false; let isMiddleClick = false; @@ -110,9 +111,11 @@ export class BreakpointsView extends ViewletPanel { if (isMiddleClick) { if (element instanceof Breakpoint) { - this.debugService.removeBreakpoints(element.getId()); + await this.debugService.removeBreakpoints(element.getId()); } else if (element instanceof FunctionBreakpoint) { - this.debugService.removeFunctionBreakpoints(element.getId()); + await this.debugService.removeFunctionBreakpoints(element.getId()); + } else if (element instanceof DataBreakpoint) { + await this.debugService.removeDataBreakpoints(element.getId()); } return; } @@ -222,14 +225,14 @@ export class BreakpointsView extends ViewletPanel { private get elements(): IEnablement[] { const model = this.debugService.getModel(); - const elements = (>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getBreakpoints()); + const elements = (>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints()); return elements; } private getExpandedBodySize(): number { const model = this.debugService.getModel(); - const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length; + const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length + model.getDataBreakpoints().length; return Math.min(BreakpointsView.MAX_VISIBLE_FILES, length) * 22; } } @@ -259,6 +262,9 @@ class BreakpointsDelegate implements IListVirtualDelegate { if (element instanceof ExceptionBreakpoint) { return ExceptionBreakpointsRenderer.ID; } + if (element instanceof DataBreakpoint) { + return DataBreakpointsRenderer.ID; + } return ''; } @@ -454,6 +460,61 @@ class FunctionBreakpointsRenderer implements IListRenderer { + + constructor( + @IDebugService private readonly debugService: IDebugService + ) { + // noop + } + + static readonly ID = 'databreakpoints'; + + get templateId() { + return DataBreakpointsRenderer.ID; + } + + renderTemplate(container: HTMLElement): IBaseBreakpointWithIconTemplateData { + const data: IBreakpointTemplateData = Object.create(null); + data.breakpoint = dom.append(container, $('.breakpoint')); + + data.icon = $('.icon'); + data.checkbox = createCheckbox(); + data.toDispose = []; + data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => { + this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); + })); + + dom.append(data.breakpoint, data.icon); + dom.append(data.breakpoint, data.checkbox); + + data.name = dom.append(data.breakpoint, $('span.name')); + + return data; + } + + renderElement(dataBreakpoint: DataBreakpoint, index: number, data: IBaseBreakpointWithIconTemplateData): void { + data.context = dataBreakpoint; + data.name.textContent = dataBreakpoint.label; + const { className, message } = getBreakpointMessageAndClassName(this.debugService, dataBreakpoint); + data.icon.className = className + ' icon'; + data.icon.title = message ? message : ''; + data.checkbox.checked = dataBreakpoint.enabled; + data.breakpoint.title = dataBreakpoint.label; + + // Mark function breakpoints as disabled if deactivated or if debug type does not support them #9099 + const session = this.debugService.getViewModel().focusedSession; + dom.toggleClass(data.breakpoint, 'disabled', (session && !session.capabilities.supportsDataBreakpoints) || !this.debugService.getModel().areBreakpointsActivated()); + if (session && !session.capabilities.supportsDataBreakpoints) { + data.breakpoint.title = nls.localize('dataBreakpointsNotSupported', "Data breakpoints are not supported by this debug type"); + } + } + + disposeTemplate(templateData: IBaseBreakpointWithIconTemplateData): void { + dispose(templateData.toDispose); + } +} + class FunctionBreakpointInputRenderer implements IListRenderer { constructor( @@ -572,7 +633,7 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } -export function getBreakpointMessageAndClassName(debugService: IDebugService, breakpoint: IBreakpoint | FunctionBreakpoint): { message?: string, className: string } { +export function getBreakpointMessageAndClassName(debugService: IDebugService, breakpoint: IBreakpoint | FunctionBreakpoint | DataBreakpoint): { message?: string, className: string } { const state = debugService.state; const debugActive = state === State.Running || state === State.Stopped; @@ -584,7 +645,7 @@ export function getBreakpointMessageAndClassName(debugService: IDebugService, br } const appendMessage = (text: string): string => { - return !(breakpoint instanceof FunctionBreakpoint) && breakpoint.message ? text.concat(', ' + breakpoint.message) : text; + return !(breakpoint instanceof FunctionBreakpoint) && !(breakpoint instanceof DataBreakpoint) && breakpoint.message ? text.concat(', ' + breakpoint.message) : text; }; if (debugActive && !breakpoint.verified) { return { @@ -607,6 +668,19 @@ export function getBreakpointMessageAndClassName(debugService: IDebugService, br }; } + if (breakpoint instanceof DataBreakpoint) { + if (session && !session.capabilities.supportsDataBreakpoints) { + return { + className: 'debug-data-breakpoint-unverified', + message: nls.localize('dataBreakpointUnsupported', "Data breakpoints not supported by this debug type"), + }; + } + + return { + className: 'debug-data-breakpoint', + }; + } + if (breakpoint.logMessage || breakpoint.condition || breakpoint.hitCondition) { const messages: string[] = []; if (breakpoint.logMessage) { diff --git a/src/vs/workbench/contrib/debug/browser/debugActions.ts b/src/vs/workbench/contrib/debug/browser/debugActions.ts index f2ed8a479ac6b..8f926b2a35a3c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActions.ts @@ -9,7 +9,7 @@ import * as lifecycle from 'vs/base/common/lifecycle'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDebugService, State, IEnablement, IBreakpoint, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; -import { Variable, Breakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; +import { Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -191,7 +191,7 @@ export class RemoveBreakpointAction extends AbstractDebugAction { public run(breakpoint: IBreakpoint): Promise { return breakpoint instanceof Breakpoint ? this.debugService.removeBreakpoints(breakpoint.getId()) - : this.debugService.removeFunctionBreakpoints(breakpoint.getId()); + : breakpoint instanceof FunctionBreakpoint ? this.debugService.removeFunctionBreakpoints(breakpoint.getId()) : this.debugService.removeDataBreakpoints(breakpoint.getId()); } } @@ -205,7 +205,7 @@ export class RemoveAllBreakpointsAction extends AbstractDebugAction { } public run(): Promise { - return Promise.all([this.debugService.removeBreakpoints(), this.debugService.removeFunctionBreakpoints()]); + return Promise.all([this.debugService.removeBreakpoints(), this.debugService.removeFunctionBreakpoints(), this.debugService.removeDataBreakpoints()]); } protected isEnabled(state: State): boolean { diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 67bcdb80de94e..7acca069182d0 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -10,7 +10,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { IListService } from 'vs/platform/list/browser/listService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, REPL_ID, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug'; -import { Expression, Variable, Breakpoint, FunctionBreakpoint, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; +import { Expression, Variable, Breakpoint, FunctionBreakpoint, Thread, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -410,6 +410,8 @@ export function registerCommands(): void { debugService.removeBreakpoints(element.getId()); } else if (element instanceof FunctionBreakpoint) { debugService.removeFunctionBreakpoints(element.getId()); + } else if (element instanceof DataBreakpoint) { + debugService.removeDataBreakpoints(element.getId()); } } } diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index eddc8876af727..8b139d2a3e3ff 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -18,7 +18,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { DebugModel, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression } from 'vs/workbench/contrib/debug/common/debugModel'; +import { DebugModel, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expression, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { ViewModel } from 'vs/workbench/contrib/debug/common/debugViewModel'; import * as debugactions from 'vs/workbench/contrib/debug/browser/debugActions'; import { ConfigurationManager } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager'; @@ -52,6 +52,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated'; const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint'; +const DEBUG_DATA_BREAKPOINTS_KEY = 'debug.databreakpoint'; const DEBUG_EXCEPTION_BREAKPOINTS_KEY = 'debug.exceptionbreakpoint'; const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions'; @@ -129,7 +130,7 @@ export class DebugService implements IDebugService { this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); this.model = new DebugModel(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(), - this.loadExceptionBreakpoints(), this.loadWatchExpressions(), this.textFileService); + this.loadExceptionBreakpoints(), this.loadDataBreakpoints(), this.loadWatchExpressions(), this.textFileService); this.toDispose.push(this.model); this.viewModel = new ViewModel(contextKeyService); @@ -851,6 +852,8 @@ export class DebugService implements IDebugService { await this.sendBreakpoints(breakpoint.uri); } else if (breakpoint instanceof FunctionBreakpoint) { await this.sendFunctionBreakpoints(); + } else if (breakpoint instanceof DataBreakpoint) { + await this.sendDataBreakpoints(); } else { await this.sendExceptionBreakpoints(); } @@ -920,6 +923,19 @@ export class DebugService implements IDebugService { this.storeBreakpoints(); } + async addDataBreakpoint(label: string, dataId: string, canPersist: boolean): Promise { + this.model.addDataBreakpoint(label, dataId, canPersist); + await this.sendDataBreakpoints(); + + this.storeBreakpoints(); + } + + async removeDataBreakpoints(id?: string): Promise { + this.model.removeDataBreakpoints(id); + await this.sendDataBreakpoints(); + this.storeBreakpoints(); + } + sendAllBreakpoints(session?: IDebugSession): Promise { return Promise.all(distinct(this.model.getBreakpoints(), bp => bp.uri.toString()).map(bp => this.sendBreakpoints(bp.uri, false, session))) .then(() => this.sendFunctionBreakpoints(session)) @@ -943,6 +959,14 @@ export class DebugService implements IDebugService { }); } + private sendDataBreakpoints(session?: IDebugSession): Promise { + const breakpointsToSend = this.model.getDataBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated()); + + return this.sendToOneOrAllSessions(session, s => { + return s.capabilities.supportsDataBreakpoints ? s.sendDataBreakpoints(breakpointsToSend) : Promise.resolve(undefined); + }); + } + private sendExceptionBreakpoints(session?: IDebugSession): Promise { const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled); @@ -1006,6 +1030,17 @@ export class DebugService implements IDebugService { return result || []; } + private loadDataBreakpoints(): DataBreakpoint[] { + let result: DataBreakpoint[] | undefined; + try { + result = JSON.parse(this.storageService.get(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE, '[]')).map((dbp: any) => { + return new DataBreakpoint(dbp.label, dbp.dataId, true, dbp.enabled, dbp.hitCondition, dbp.condition, dbp.logMessage); + }); + } catch (e) { } + + return result || []; + } + private loadWatchExpressions(): Expression[] { let result: Expression[] | undefined; try { @@ -1041,6 +1076,13 @@ export class DebugService implements IDebugService { this.storageService.remove(DEBUG_FUNCTION_BREAKPOINTS_KEY, StorageScope.WORKSPACE); } + const dataBreakpoints = this.model.getDataBreakpoints().filter(dbp => dbp.canPersist); + if (dataBreakpoints.length) { + this.storageService.store(DEBUG_DATA_BREAKPOINTS_KEY, JSON.stringify(dataBreakpoints), StorageScope.WORKSPACE); + } else { + this.storageService.remove(DEBUG_DATA_BREAKPOINTS_KEY, StorageScope.WORKSPACE); + } + const exceptionBreakpoints = this.model.getExceptionBreakpoints(); if (exceptionBreakpoints.length) { this.storageService.store(DEBUG_EXCEPTION_BREAKPOINTS_KEY, JSON.stringify(exceptionBreakpoints), StorageScope.WORKSPACE); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index d454894401968..b0f361a487216 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -12,7 +12,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { CompletionItem, completionKindFromString } from 'vs/editor/common/modes'; import { Position } from 'vs/editor/common/core/position'; import * as aria from 'vs/base/browser/ui/aria/aria'; -import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger, VIEWLET_ID, IDebugConfiguration, IReplElement, IStackFrame, IExpression, IReplElementSource } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger, VIEWLET_ID, IDebugConfiguration, IReplElement, IStackFrame, IExpression, IReplElementSource, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { mixin } from 'vs/base/common/objects'; import { Thread, ExpressionContainer, DebugModel } from 'vs/workbench/contrib/debug/common/debugModel'; @@ -327,6 +327,34 @@ export class DebugSession implements IDebugSession { return Promise.reject(new Error('no debug adapter')); } + dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean }> { + if (this.raw) { + if (this.raw.readyForBreakpoints) { + return this.raw.dataBreakpointInfo({ name, variablesReference }).then(response => response.body); + } + return Promise.reject(new Error(nls.localize('sessionNotReadyForBreakpoints', "Session is not ready for breakpoints"))); + } + return Promise.reject(new Error('no debug adapter')); + } + + sendDataBreakpoints(dataBreakpoints: IDataBreakpoint[]): Promise { + if (this.raw) { + if (this.raw.readyForBreakpoints) { + return this.raw.setDataBreakpoints({ breakpoints: dataBreakpoints }).then(response => { + if (response && response.body) { + const data = new Map(); + for (let i = 0; i < dataBreakpoints.length; i++) { + data.set(dataBreakpoints[i].getId(), response.body.breakpoints[i]); + } + this.model.setBreakpointSessionData(this.getId(), data); + } + }); + } + return Promise.resolve(undefined); + } + return Promise.reject(new Error('no debug adapter')); + } + customRequest(request: string, args: any): Promise { if (this.raw) { return this.raw.custom(request, args); diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index 0d1772ff111dd..fe5e98b83dd8a 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -357,6 +357,20 @@ export class RawDebugSession { return Promise.reject(new Error('setFunctionBreakpoints not supported')); } + dataBreakpointInfo(args: DebugProtocol.DataBreakpointInfoArguments): Promise { + if (this.capabilities.supportsDataBreakpoints) { + return this.send('dataBreakpointInfo', args); + } + return Promise.reject(new Error('dataBreakpointInfo not supported')); + } + + setDataBreakpoints(args: DebugProtocol.SetDataBreakpointsArguments): Promise { + if (this.capabilities.supportsDataBreakpoints) { + return this.send('setDataBreakpoints', args); + } + return Promise.reject(new Error('setDataBreakpoints not supported')); + } + setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise { return this.send('setExceptionBreakpoints', args); } diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 45eb8037c717b..3c3890b574d11 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -89,11 +89,11 @@ export class VariablesView extends ViewletPanel { this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, treeContainer, new VariablesDelegate(), [this.instantiationService.createInstance(VariablesRenderer), new ScopesRenderer()], new VariablesDataSource(), { - ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"), - accessibilityProvider: new VariablesAccessibilityProvider(), - identityProvider: { getId: (element: IExpression | IScope) => element.getId() }, - keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IExpression | IScope) => e } - }); + ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"), + accessibilityProvider: new VariablesAccessibilityProvider(), + identityProvider: { getId: (element: IExpression | IScope) => element.getId() }, + keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IExpression | IScope) => e } + }); this.tree.setInput(this.debugService.getViewModel()).then(null, onUnexpectedError); @@ -123,7 +123,7 @@ export class VariablesView extends ViewletPanel { this.tree.updateChildren(); })); this._register(this.tree.onMouseDblClick(e => this.onMouseDblClick(e))); - this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); + this._register(this.tree.onContextMenu(async e => await this.onContextMenu(e))); this._register(this.onDidChangeBodyVisibility(visible => { if (visible && this.needsRefresh) { @@ -152,7 +152,7 @@ export class VariablesView extends ViewletPanel { } } - private onContextMenu(e: ITreeContextMenuEvent): void { + private async onContextMenu(e: ITreeContextMenuEvent): Promise { const variable = e.element; if (variable instanceof Variable && !!variable.value) { const actions: IAction[] = []; @@ -174,6 +174,16 @@ export class VariablesView extends ViewletPanel { return Promise.resolve(undefined); })); } + if (session && session.capabilities.supportsDataBreakpoints) { + const response = await session.dataBreakpointInfo(variable.name, variable.parent.reference); + const dataid = response.dataId; + if (dataid) { + actions.push(new Separator()); + actions.push(new Action('debug.addDataBreakpoint', nls.localize('setDataBreakpoint', "Set Data Breakpoint"), undefined, true, () => { + return this.debugService.addDataBreakpoint(response.description, dataid, !!response.canPersist); + })); + } + } this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 65e28660dc9aa..42a3576979723 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -103,6 +103,7 @@ export interface IReplElementSource { export interface IExpressionContainer extends ITreeElement { readonly hasChildren: boolean; getChildren(): Promise; + readonly reference?: number; } export interface IExpression extends IReplElement, IExpressionContainer { @@ -201,6 +202,8 @@ export interface IDebugSession extends ITreeElement { sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): Promise; sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise; + dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean }>; + sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise; sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise; stackTrace(threadId: number, startFrame: number, levels: number): Promise; @@ -357,6 +360,12 @@ export interface IExceptionBreakpoint extends IEnablement { readonly label: string; } +export interface IDataBreakpoint extends IBaseBreakpoint { + readonly label: string; + readonly dataId: string; + readonly canPersist: boolean; +} + export interface IExceptionInfo { readonly id?: string; readonly description?: string; @@ -404,6 +413,7 @@ export interface IDebugModel extends ITreeElement { getBreakpoints(filter?: { uri?: uri, lineNumber?: number, column?: number, enabledOnly?: boolean }): ReadonlyArray; areBreakpointsActivated(): boolean; getFunctionBreakpoints(): ReadonlyArray; + getDataBreakpoints(): ReadonlyArray; getExceptionBreakpoints(): ReadonlyArray; getWatchExpressions(): ReadonlyArray; @@ -416,9 +426,9 @@ export interface IDebugModel extends ITreeElement { * An event describing a change to the set of [breakpoints](#debug.Breakpoint). */ export interface IBreakpointsChangeEvent { - added?: Array; - removed?: Array; - changed?: Array; + added?: Array; + removed?: Array; + changed?: Array; sessionOnly?: boolean; } @@ -754,6 +764,17 @@ export interface IDebugService { */ removeFunctionBreakpoints(id?: string): Promise; + /** + * Adds a new data breakpoint. + */ + addDataBreakpoint(label: string, dataId: string, canPersist: boolean): Promise; + + /** + * Removes all data breakpoints. If id is passed only removes the data breakpoint with the passed id. + * Notifies debug adapter of breakpoint changes. + */ + removeDataBreakpoints(id?: string): Promise; + /** * Sends all breakpoints to the passed session. * If session is not passed, sends all breakpoints to each session. diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 16a838721c4ee..599c496dcd97a 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -16,7 +16,7 @@ import { distinct, lastIndex } from 'vs/base/common/arrays'; import { Range, IRange } from 'vs/editor/common/core/range'; import { ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel, IReplElementSource, - IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IReplElement, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State + IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IReplElement, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { Source, UNKNOWN_SOURCE_LABEL } from 'vs/workbench/contrib/debug/common/debugSource'; import { commonSuffixLength } from 'vs/base/common/strings'; @@ -735,6 +735,34 @@ export class FunctionBreakpoint extends BaseBreakpoint implements IFunctionBreak } } +export class DataBreakpoint extends BaseBreakpoint implements IDataBreakpoint { + + constructor( + public label: string, + public dataId: string, + public canPersist: boolean, + enabled: boolean, + hitCondition: string | undefined, + condition: string | undefined, + logMessage: string | undefined, + id = generateUuid() + ) { + super(enabled, hitCondition, condition, logMessage, id); + } + + toJSON(): any { + const result = super.toJSON(); + result.label = this.label; + result.dataid = this.dataId; + + return result; + } + + toString(): string { + return this.label; + } +} + export class ExceptionBreakpoint extends Enablement implements IExceptionBreakpoint { constructor(public filter: string, public label: string, enabled: boolean) { @@ -778,6 +806,7 @@ export class DebugModel implements IDebugModel { private breakpointsActivated: boolean, private functionBreakpoints: FunctionBreakpoint[], private exceptionBreakpoints: ExceptionBreakpoint[], + private dataBreakopints: DataBreakpoint[], private watchExpressions: Expression[], private textFileService: ITextFileService ) { @@ -918,6 +947,10 @@ export class DebugModel implements IDebugModel { return this.functionBreakpoints; } + getDataBreakpoints(): IDataBreakpoint[] { + return this.dataBreakopints; + } + getExceptionBreakpoints(): IExceptionBreakpoint[] { return this.exceptionBreakpoints; } @@ -991,6 +1024,12 @@ export class DebugModel implements IDebugModel { fbp.setSessionData(sessionId, fbpData); } }); + this.dataBreakopints.forEach(dbp => { + const dbpData = data.get(dbp.getId()); + if (dbpData) { + dbp.setSessionData(sessionId, dbpData); + } + }); this._onDidChangeBreakpoints.fire({ sessionOnly: true @@ -1001,6 +1040,7 @@ export class DebugModel implements IDebugModel { this.breakpointsSessionId = sessionId; this.breakpoints.forEach(bp => bp.setSessionId(sessionId)); this.functionBreakpoints.forEach(fbp => fbp.setSessionId(sessionId)); + this.dataBreakopints.forEach(dbp => dbp.setSessionId(sessionId)); this._onDidChangeBreakpoints.fire({ sessionOnly: true @@ -1038,7 +1078,7 @@ export class DebugModel implements IDebugModel { } enableOrDisableAllBreakpoints(enable: boolean): void { - const changed: Array = []; + const changed: Array = []; this.breakpoints.forEach(bp => { if (bp.enabled !== enable) { @@ -1052,6 +1092,12 @@ export class DebugModel implements IDebugModel { } fbp.enabled = enable; }); + this.dataBreakopints.forEach(dbp => { + if (dbp.enabled !== enable) { + changed.push(dbp); + } + dbp.enabled = enable; + }); this._onDidChangeBreakpoints.fire({ changed: changed }); } @@ -1073,7 +1119,6 @@ export class DebugModel implements IDebugModel { } removeFunctionBreakpoints(id?: string): void { - let removed: FunctionBreakpoint[]; if (id) { removed = this.functionBreakpoints.filter(fbp => fbp.getId() === id); @@ -1082,7 +1127,25 @@ export class DebugModel implements IDebugModel { removed = this.functionBreakpoints; this.functionBreakpoints = []; } - this._onDidChangeBreakpoints.fire({ removed: removed }); + this._onDidChangeBreakpoints.fire({ removed }); + } + + addDataBreakpoint(label: string, dataId: string, canPersist: boolean): void { + const newDataBreakpoint = new DataBreakpoint(label, dataId, canPersist, true, undefined, undefined, undefined); + this.dataBreakopints.push(newDataBreakpoint); + this._onDidChangeBreakpoints.fire({ added: [newDataBreakpoint] }); + } + + removeDataBreakpoints(id?: string): void { + let removed: DataBreakpoint[]; + if (id) { + removed = this.dataBreakopints.filter(fbp => fbp.getId() === id); + this.dataBreakopints = this.dataBreakopints.filter(fbp => fbp.getId() !== id); + } else { + removed = this.dataBreakopints; + this.dataBreakopints = []; + } + this._onDidChangeBreakpoints.fire({ removed }); } getWatchExpressions(): Expression[] { diff --git a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts index 6b313596ca61c..d6ebbfd721be2 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts @@ -23,7 +23,7 @@ suite('Debug - Model', () => { let rawSession: MockRawSession; setup(() => { - model = new DebugModel([], true, [], [], [], { isDirty: (e: any) => false }); + model = new DebugModel([], true, [], [], [], [], { isDirty: (e: any) => false }); rawSession = new MockRawSession(); }); diff --git a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index 19aae90891d6c..bb10110ab92cc 100644 --- a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts @@ -7,7 +7,7 @@ import { URI as uri } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Position } from 'vs/editor/common/core/position'; -import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource } from 'vs/workbench/contrib/debug/common/debug'; +import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { CompletionItem } from 'vs/editor/common/modes'; import Severity from 'vs/base/common/severity'; @@ -79,6 +79,13 @@ export class MockDebugService implements IDebugService { throw new Error('not implemented'); } + addDataBreakpoint(label: string, dataId: string, canPersist: boolean): Promise { + throw new Error('Method not implemented.'); + } + removeDataBreakpoints(id?: string | undefined): Promise { + throw new Error('Method not implemented.'); + } + public addReplExpression(name: string): Promise { throw new Error('not implemented'); } @@ -125,6 +132,13 @@ export class MockDebugService implements IDebugService { } export class MockSession implements IDebugSession { + dataBreakpointInfo(name: string, variablesReference?: number | undefined): Promise<{ dataId: string | null; description: string; canPersist?: boolean | undefined; }> { + throw new Error('Method not implemented.'); + } + + sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise { + throw new Error('Method not implemented.'); + } subId: string | undefined; From 623855b7c45993cf3eae9bef262f5222cd9fd827 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 16 Aug 2019 15:56:38 +0200 Subject: [PATCH 301/613] Introduce and adopt asCSSUrl --- src/vs/base/browser/dom.ts | 7 +++++++ src/vs/editor/browser/services/codeEditorServiceImpl.ts | 8 ++++---- .../platform/actions/browser/menuEntryActionViewItem.ts | 6 +++--- src/vs/workbench/api/browser/viewsExtensionPoint.ts | 4 ++-- .../browser/parts/activitybar/activitybarActions.ts | 2 +- .../workbench/browser/parts/quickinput/quickInputUtils.ts | 4 ++-- src/vs/workbench/browser/parts/views/customView.ts | 2 +- .../extensions/browser/extensionsWorkbenchService.ts | 2 +- .../contrib/webview/browser/webviewEditorInput.ts | 6 +++--- .../services/themes/browser/fileIconThemeData.ts | 6 +++--- 10 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 717041747da9f..0bca0bfdec46b 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1204,3 +1204,10 @@ export function asDomUri(uri: URI): URI { } return uri; } + +/** + * returns url('...') + */ +export function asCSSUrl(uri: URI): string { + return `url('${asDomUri(uri).toString(true).replace(/'/g, '%27')}')`; +} diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index 106c75b5d046a..c7cf1da3f4535 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -229,11 +229,11 @@ const _CSS_MAP: { [prop: string]: string; } = { cursor: 'cursor:{0};', letterSpacing: 'letter-spacing:{0};', - gutterIconPath: 'background:url(\'{0}\') center center no-repeat;', + gutterIconPath: 'background:{0} center center no-repeat;', gutterIconSize: 'background-size:{0};', contentText: 'content:\'{0}\';', - contentIconPath: 'content:url(\'{0}\');', + contentIconPath: 'content:{0};', margin: 'margin:{0};', width: 'width:{0};', height: 'height:{0};' @@ -399,7 +399,7 @@ class DecorationCSSRules { if (typeof opts !== 'undefined') { this.collectBorderSettingsCSSText(opts, cssTextArr); if (typeof opts.contentIconPath !== 'undefined') { - cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, dom.asDomUri(URI.revive(opts.contentIconPath)).toString(true).replace(/'/g, '%27'))); + cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, dom.asCSSUrl(URI.revive(opts.contentIconPath)))); } if (typeof opts.contentText === 'string') { const truncated = opts.contentText.match(/^.*$/m)![0]; // only take first line @@ -426,7 +426,7 @@ class DecorationCSSRules { const cssTextArr: string[] = []; if (typeof opts.gutterIconPath !== 'undefined') { - cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, dom.asDomUri(URI.revive(opts.gutterIconPath)).toString(true).replace(/'/g, '%27'))); + cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, dom.asCSSUrl(URI.revive(opts.gutterIconPath)))); if (typeof opts.gutterIconSize !== 'undefined') { cssTextArr.push(strings.format(_CSS_MAP.gutterIconSize, opts.gutterIconSize)); } diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 6c3102bc4df28..01d88e2ece88c 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { addClasses, createCSSRule, removeClasses, asDomUri } from 'vs/base/browser/dom'; +import { addClasses, createCSSRule, removeClasses, asCSSUrl } from 'vs/base/browser/dom'; import { domEvent } from 'vs/base/browser/event'; import { ActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IAction } from 'vs/base/common/actions'; @@ -244,8 +244,8 @@ export class MenuEntryActionViewItem extends ActionViewItem { iconClass = MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!; } else { iconClass = ids.nextId(); - createCSSRule(`.icon.${iconClass}`, `background-image: url("${asDomUri(item.iconLocation.light || item.iconLocation.dark).toString()}")`); - createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${asDomUri(item.iconLocation.dark).toString()}")`); + createCSSRule(`.icon.${iconClass}`, `background-image: ${asCSSUrl(item.iconLocation.light || item.iconLocation.dark)}`); + createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: ${asCSSUrl(item.iconLocation.dark)}`); MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass); } diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index b12e7ec2bf6be..2a5030ae9f87c 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -37,7 +37,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { createCSSRule, asDomUri } from 'vs/base/browser/dom'; +import { createCSSRule, asCSSUrl } from 'vs/base/browser/dom'; export interface IUserFriendlyViewsContainerDescriptor { id: string; @@ -356,7 +356,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { // Generate CSS to show the icon in the activity bar const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${cssClass}`; - createCSSRule(iconClass, `-webkit-mask: url('${asDomUri(icon)}') no-repeat 50% 50%; -webkit-mask-size: 24px;`); + createCSSRule(iconClass, `-webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%; -webkit-mask-size: 24px;`); } return viewContainer; diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index d73545fb3ef34..5c4c70a2e75ca 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -171,7 +171,7 @@ export class PlaceHolderViewletActivityAction extends ViewletActivityAction { super({ id, name: id, cssClass: `extensionViewlet-placeholder-${id.replace(/\./g, '-')}` }, viewletService, layoutService, telemetryService); const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${this.class}`; // Generate Placeholder CSS to show the icon in the activity bar - DOM.createCSSRule(iconClass, `-webkit-mask: url('${DOM.asDomUri(iconUrl) || ''}') no-repeat 50% 50%; -webkit-mask-size: 24px;`); + DOM.createCSSRule(iconClass, `-webkit-mask: ${DOM.asCSSUrl(iconUrl)} no-repeat 50% 50%; -webkit-mask-size: 24px;`); } setActivity(activity: IActivity): void { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts b/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts index 378d2be0c4c55..690543c695937 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts @@ -22,8 +22,8 @@ export function getIconClass(iconPath: { dark: URI; light?: URI; } | undefined): iconClass = iconPathToClass[key]; } else { iconClass = iconClassGenerator.nextId(); - dom.createCSSRule(`.${iconClass}`, `background-image: url("${dom.asDomUri(iconPath.light || iconPath.dark).toString()}")`); - dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: url("${dom.asDomUri(iconPath.dark).toString()}")`); + dom.createCSSRule(`.${iconClass}`, `background-image: ${dom.asCSSUrl(iconPath.light || iconPath.dark)}`); + dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: ${dom.asCSSUrl(iconPath.dark)}`); iconPathToClass[key] = iconClass; } diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 9fd89851d9d1e..7e98724953dbc 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -756,7 +756,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index e82544c68e3f6..31f9d115e24f8 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -131,7 +131,7 @@ class Extension implements IExtension { private get localIconUrl(): string | null { if (this.local && this.local.manifest.icon) { - return asDomUri(resources.joinPath(this.local.location, this.local.manifest.icon)).toString(); + return asDomUri(resources.joinPath(this.local.location, this.local.manifest.icon)).toString(true); } return null; } diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts index c91e93d915ffd..614097c5ba93b 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts @@ -39,11 +39,11 @@ class WebviewIconsManager { this._icons.forEach((value, key) => { const webviewSelector = `.show-file-icons .webview-${key}-name-file-icon::before`; if (URI.isUri(value)) { - cssRules.push(`${webviewSelector} { content: ""; background-image: url(${dom.asDomUri(value).toString()}); }`); + cssRules.push(`${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value)}; }`); } else { - cssRules.push(`.vs ${webviewSelector} { content: ""; background-image: url(${dom.asDomUri(value.light).toString()}); }`); - cssRules.push(`.vs-dark ${webviewSelector} { content: ""; background-image: url(${dom.asDomUri(value.dark).toString()}); }`); + cssRules.push(`.vs ${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value.light)}; }`); + cssRules.push(`.vs-dark ${webviewSelector} { content: ""; background-image: ${dom.asCSSUrl(value.dark)}; }`); } }); this._styleElement.innerHTML = cssRules.join('\n'); diff --git a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts index 3059579a64956..2512af088a0e6 100644 --- a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts @@ -11,7 +11,7 @@ import * as Json from 'vs/base/common/json'; import { ExtensionData, IThemeExtensionPoint, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IFileService } from 'vs/platform/files/common/files'; import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; -import { asDomUri } from 'vs/base/browser/dom'; +import { asCSSUrl } from 'vs/base/browser/dom'; export class FileIconThemeData implements IFileIconTheme { id: string; @@ -332,7 +332,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i let fonts = iconThemeDocument.fonts; if (Array.isArray(fonts)) { fonts.forEach(font => { - let src = font.src.map(l => `url('${asDomUri(resolvePath(l.path))}') format('${l.format}')`).join(', '); + let src = font.src.map(l => `${asCSSUrl(resolvePath(l.path))} format('${l.format}')`).join(', '); cssRules.push(`@font-face { src: ${src}; font-family: '${font.id}'; font-weight: ${font.weight}; font-style: ${font.style}; }`); }); cssRules.push(`.show-file-icons .file-icon::before, .show-file-icons .folder-icon::before, .show-file-icons .rootfolder-icon::before { font-family: '${fonts[0].id}'; font-size: ${fonts[0].size || '150%'}}`); @@ -343,7 +343,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i let definition = iconThemeDocument.iconDefinitions[defId]; if (definition) { if (definition.iconPath) { - cssRules.push(`${selectors.join(', ')} { content: ' '; background-image: url("${asDomUri(resolvePath(definition.iconPath))}"); }`); + cssRules.push(`${selectors.join(', ')} { content: ' '; background-image: ${asCSSUrl(resolvePath(definition.iconPath))}; }`); } if (definition.fontCharacter || definition.fontColor) { let body = ''; From 265dba559806b4298fa8f3e7923b80c2d06d9c9b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 16 Aug 2019 17:37:28 +0200 Subject: [PATCH 302/613] Fixes microsoft/vscode-remote-release#687: - remove ipc message that passes over the resolved authority - don't go through the protocol handler when hitting 127.0.0.1 --- src/vs/base/browser/dom.ts | 16 +-- src/vs/base/common/network.ts | 17 ++- .../electron-browser/workbench/workbench.html | 2 +- src/vs/code/electron-main/app.ts | 122 +----------------- .../remoteAuthorityResolverService.ts | 2 - 5 files changed, 31 insertions(+), 128 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 0bca0bfdec46b..a97f27a51e736 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -15,7 +15,7 @@ import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle' import * as platform from 'vs/base/common/platform'; import { coalesce } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; +import { Schemas, RemoteAuthorities } from 'vs/base/common/network'; export function clearNode(node: HTMLElement): void { while (node.firstChild) { @@ -1193,14 +1193,14 @@ export function asDomUri(uri: URI): URI { if (!uri) { return uri; } - if (!platform.isWeb) { - //todo@joh remove this once we have sw in electron going - return uri; - } if (Schemas.vscodeRemote === uri.scheme) { - // rewrite vscode-remote-uris to uris of the window location - // so that they can be intercepted by the service worker - return _location.with({ path: '/vscode-remote', query: JSON.stringify(uri) }); + if (platform.isWeb) { + // rewrite vscode-remote-uris to uris of the window location + // so that they can be intercepted by the service worker + return _location.with({ path: '/vscode-remote', query: JSON.stringify(uri) }); + } else { + return RemoteAuthorities.rewrite(uri.authority, uri.path); + } } return uri; } diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index f3006bdf3e1a6..bccc215360265 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { URI } from 'vs/base/common/uri'; + export namespace Schemas { /** @@ -62,13 +64,26 @@ class RemoteAuthoritiesImpl { } public set(authority: string, host: string, port: number): void { - this._hosts[authority] = host; + this._hosts[authority] = (host === 'localhost' ? '127.0.0.1' : host); this._ports[authority] = port; } public setConnectionToken(authority: string, connectionToken: string): void { this._connectionTokens[authority] = connectionToken; } + + public rewrite(authority: string, path: string): URI { + const host = this._hosts[authority]; + const port = this._ports[authority]; + const connectionToken = this._connectionTokens[authority]; + const scheme = (host === '127.0.0.1' ? Schemas.http : Schemas.vscodeRemote); + return URI.from({ + scheme: scheme, + authority: `${host}:${port}`, + path: `/vscode-remote2`, + query: `path=${encodeURIComponent(path)}&tkn=${encodeURIComponent(connectionToken)}` + }); + } } export const RemoteAuthorities = new RemoteAuthoritiesImpl(); diff --git a/src/vs/code/electron-browser/workbench/workbench.html b/src/vs/code/electron-browser/workbench/workbench.html index 483d01493f57a..bef3d951473ac 100644 --- a/src/vs/code/electron-browser/workbench/workbench.html +++ b/src/vs/code/electron-browser/workbench/workbench.html @@ -3,7 +3,7 @@ - + diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index c17c592775f9e..f60f4f73c44c2 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -39,7 +39,6 @@ import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService } from 'vs/platform/history/common/history'; -import { withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { WorkspacesChannel } from 'vs/platform/workspaces/node/workspacesIpc'; import { IWorkspacesMainService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; @@ -54,7 +53,6 @@ import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc'; import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/errors'; import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; -import { connectRemoteAgentManagement, ManagementPersistentConnection, IConnectionOptions } from 'vs/platform/remote/common/remoteAgentConnection'; import { IMenubarService } from 'vs/platform/menubar/common/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService'; import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc'; @@ -65,8 +63,6 @@ import { homedir } from 'os'; import { join, sep } from 'vs/base/common/path'; import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; -import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from 'vs/platform/remote/common/remoteAgentFileSystemChannel'; -import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap'; import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService'; import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc'; @@ -76,11 +72,7 @@ import { IBackupMainService } from 'vs/platform/backup/common/backup'; import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; import { URLService } from 'vs/platform/url/common/urlService'; import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; -import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; -import { VSBuffer } from 'vs/base/common/buffer'; import { statSync } from 'fs'; -import { ISignService } from 'vs/platform/sign/common/sign'; import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc'; import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; import { FileService } from 'vs/platform/files/common/fileService'; @@ -103,8 +95,7 @@ export class CodeApplication extends Disposable { @IEnvironmentService private readonly environmentService: IEnvironmentService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IStateService private readonly stateService: IStateService, - @ISignService private readonly signService: ISignService + @IStateService private readonly stateService: IStateService ) { super(); @@ -697,112 +688,11 @@ export class CodeApplication extends Disposable { } private handleRemoteAuthorities(): void { - const connectionPool: Map = new Map(); - - class ActiveConnection { - private readonly _authority: string; - private readonly _connection: Promise; - private readonly _disposeRunner: RunOnceScheduler; - - constructor(authority: string, host: string, port: number, signService: ISignService) { - this._authority = authority; - - const options: IConnectionOptions = { - commit: product.commit, - socketFactory: nodeSocketFactory, - addressProvider: { - getAddress: () => { - return Promise.resolve({ host, port }); - } - }, - signService - }; - - this._connection = connectRemoteAgentManagement(options, authority, `main`); - this._disposeRunner = new RunOnceScheduler(() => this.dispose(), 5000); - } - - dispose(): void { - this._disposeRunner.dispose(); - connectionPool.delete(this._authority); - this._connection.then(connection => connection.dispose()); - } - - async getClient(): Promise> { - this._disposeRunner.schedule(); - const connection = await this._connection; - - return connection.client; - } - } - - const resolvedAuthorities = new Map(); - ipc.on('vscode:remoteAuthorityResolved', (event: Electron.Event, data: ResolvedAuthority) => { - this.logService.info('Received resolved authority', data.authority); - - resolvedAuthorities.set(data.authority, data); - - // Make sure to close and remove any existing connections - if (connectionPool.has(data.authority)) { - connectionPool.get(data.authority)!.dispose(); - } - }); - - const resolveAuthority = (authority: string): ResolvedAuthority | null => { - this.logService.info('Resolving authority', authority); - - if (authority.indexOf('+') >= 0) { - if (resolvedAuthorities.has(authority)) { - return withUndefinedAsNull(resolvedAuthorities.get(authority)); - } - - this.logService.info('Didnot find resolved authority for', authority); - - return null; - } else { - const [host, strPort] = authority.split(':'); - const port = parseInt(strPort, 10); - - return { authority, host, port }; - } - }; - - protocol.registerBufferProtocol(Schemas.vscodeRemote, async (request, callback) => { - if (request.method !== 'GET') { - return callback(undefined); - } - - const uri = URI.parse(request.url); - - let activeConnection: ActiveConnection | undefined; - if (connectionPool.has(uri.authority)) { - activeConnection = connectionPool.get(uri.authority); - } else { - const resolvedAuthority = resolveAuthority(uri.authority); - if (!resolvedAuthority) { - callback(undefined); - return; - } - - activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port, this.signService); - connectionPool.set(uri.authority, activeConnection); - } - - try { - const rawClient = await activeConnection!.getClient(); - if (connectionPool.has(uri.authority)) { // not disposed in the meantime - const channel = rawClient.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME); - - // TODO@alex don't use call directly, wrap it around a `RemoteExtensionsFileSystemProvider` - const fileContents = await channel.call('readFile', [uri]); - callback(fileContents.buffer); - } else { - callback(undefined); - } - } catch (err) { - onUnexpectedError(err); - callback(undefined); - } + protocol.registerHttpProtocol(Schemas.vscodeRemote, (request, callback) => { + callback({ + url: request.url.replace(/^vscode-remote:/, 'http:'), + method: request.method + }); }); } } diff --git a/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts index 9ab777f67bd04..45ad072f2eb35 100644 --- a/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ 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'; @@ -50,7 +49,6 @@ export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverS setResolvedAuthority(resolvedAuthority: ResolvedAuthority, options?: ResolvedOptions) { 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 }); } From 64875aa7c6198d7d164fbfee2759ed045bebd209 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 16 Aug 2019 17:58:30 +0200 Subject: [PATCH 303/613] Introduce machine overridable setting --- .../configuration/common/configurationRegistry.ts | 4 ++++ .../api/common/configurationExtensionPoint.ts | 13 ++++++++----- .../preferences/browser/settingsTreeModels.ts | 14 +++++++++----- .../services/configuration/common/configuration.ts | 8 ++++---- .../preferences/common/preferencesModels.ts | 8 ++++++-- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 0d27f890edf91..329276d4e46e1 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -99,6 +99,10 @@ export const enum ConfigurationScope { * Resource specific configuration, which can be configured in the user, workspace or folder settings. */ RESOURCE, + /** + * Machine specific configuration that can also be configured in workspace or folder settings. + */ + MACHINE_OVERRIDABLE, } export interface IConfigurationPropertySchema extends IJSONSchema { diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index aa05f7e1187e2..d36d498330dd3 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -39,13 +39,14 @@ const configurationEntrySchema: IJSONSchema = { }, scope: { type: 'string', - enum: ['application', 'machine', 'window', 'resource'], + enum: ['application', 'machine', 'window', 'resource', 'machine-overridable'], default: 'window', enumDescriptions: [ - nls.localize('scope.application.description', "Application specific configuration, which can be configured only in the user settings."), - nls.localize('scope.machine.description', "Machine specific configuration, which can be configured only in the user settings when the extension is running locally, or only in the remote settings when the extension is running remotely."), - nls.localize('scope.window.description', "Window specific configuration, which can be configured in the user, remote or workspace settings."), - nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the user, remote, workspace or folder settings.") + nls.localize('scope.application.description', "Configuration that can be configured only in the user settings."), + nls.localize('scope.machine.description', "Configuration that can be configured only in the user settings when the extension is running locally, or only in the remote settings when the extension is running remotely."), + nls.localize('scope.window.description', "Configuration that can be configured in the user, remote or workspace settings."), + nls.localize('scope.resource.description', "Configuration that can be configured in the user, remote, workspace or folder settings."), + nls.localize('scope.machine-overridable.description', "Machine configuration that can be configured also in workspace or folder settings.") ], description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `application`, `machine`, `window` and `resource`.") }, @@ -215,6 +216,8 @@ function validateProperties(configuration: IConfigurationNode, extension: IExten propertyConfiguration.scope = ConfigurationScope.MACHINE; } else if (propertyConfiguration.scope.toString() === 'resource') { propertyConfiguration.scope = ConfigurationScope.RESOURCE; + } else if (propertyConfiguration.scope.toString() === 'machine-overridable') { + propertyConfiguration.scope = ConfigurationScope.MACHINE_OVERRIDABLE; } else { propertyConfiguration.scope = ConfigurationScope.WINDOW; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index c85026c360b5d..efe952907cd15 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -9,12 +9,12 @@ import { isArray, withUndefinedAsNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { SettingsTarget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; import { ITOCEntry, knownAcronyms, knownTermMappings } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; import { MODIFIED_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; import { IExtensionSetting, ISearchResult, ISetting, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { FOLDER_SCOPES, WORKSPACE_SCOPES, REMOTE_MACHINE_SCOPES, LOCAL_MACHINE_SCOPES } from 'vs/workbench/services/configuration/common/configuration'; export const ONLINE_SERVICES_SETTING_TAG = 'usesOnlineServices'; @@ -227,20 +227,24 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { matchesScope(scope: SettingsTarget, isRemote: boolean): boolean { const configTarget = URI.isUri(scope) ? ConfigurationTarget.WORKSPACE_FOLDER : scope; + if (!this.setting.scope) { + return true; + } + if (configTarget === ConfigurationTarget.WORKSPACE_FOLDER) { - return this.setting.scope === ConfigurationScope.RESOURCE; + return FOLDER_SCOPES.indexOf(this.setting.scope) !== -1; } if (configTarget === ConfigurationTarget.WORKSPACE) { - return this.setting.scope === ConfigurationScope.WINDOW || this.setting.scope === ConfigurationScope.RESOURCE; + return WORKSPACE_SCOPES.indexOf(this.setting.scope) !== -1; } if (configTarget === ConfigurationTarget.USER_REMOTE) { - return this.setting.scope === ConfigurationScope.MACHINE || this.setting.scope === ConfigurationScope.WINDOW || this.setting.scope === ConfigurationScope.RESOURCE; + return REMOTE_MACHINE_SCOPES.indexOf(this.setting.scope) !== -1; } if (configTarget === ConfigurationTarget.USER_LOCAL && isRemote) { - return this.setting.scope !== ConfigurationScope.MACHINE; + return LOCAL_MACHINE_SCOPES.indexOf(this.setting.scope) !== -1; } return true; diff --git a/src/vs/workbench/services/configuration/common/configuration.ts b/src/vs/workbench/services/configuration/common/configuration.ts index 170e3f136aa02..a5f15850dbf75 100644 --- a/src/vs/workbench/services/configuration/common/configuration.ts +++ b/src/vs/workbench/services/configuration/common/configuration.ts @@ -17,9 +17,9 @@ export const folderSettingsSchemaId = 'vscode://schemas/settings/folder'; export const launchSchemaId = 'vscode://schemas/launch'; export const LOCAL_MACHINE_SCOPES = [ConfigurationScope.APPLICATION, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]; -export const REMOTE_MACHINE_SCOPES = [ConfigurationScope.MACHINE, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]; -export const WORKSPACE_SCOPES = [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]; -export const FOLDER_SCOPES = [ConfigurationScope.RESOURCE]; +export const REMOTE_MACHINE_SCOPES = [ConfigurationScope.MACHINE, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE, ConfigurationScope.MACHINE_OVERRIDABLE]; +export const WORKSPACE_SCOPES = [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE, ConfigurationScope.MACHINE_OVERRIDABLE]; +export const FOLDER_SCOPES = [ConfigurationScope.RESOURCE, ConfigurationScope.MACHINE_OVERRIDABLE]; export const TASKS_CONFIGURATION_KEY = 'tasks'; export const LAUNCH_CONFIGURATION_KEY = 'launch'; @@ -36,4 +36,4 @@ export interface IConfigurationCache { write(key: ConfigurationKey, content: string): Promise; remove(key: ConfigurationKey): Promise; -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index d24d2a53f6803..3e252282711e1 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -23,6 +23,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { EditorModel } from 'vs/workbench/common/editor'; import { IFilterMetadata, IFilterResult, IGroupFilter, IKeybindingsEditorModel, ISearchResultGroup, ISetting, ISettingMatch, ISettingMatcher, ISettingsEditorModel, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; import { withNullAsUndefined, isArray } from 'vs/base/common/types'; +import { FOLDER_SCOPES, WORKSPACE_SCOPES } from 'vs/workbench/services/configuration/common/configuration'; export const nullRange: IRange = { startLineNumber: -1, startColumn: -1, endLineNumber: -1, endColumn: -1 }; export function isNullRange(range: IRange): boolean { return range.startLineNumber === -1 && range.startColumn === -1 && range.endLineNumber === -1 && range.endColumn === -1; } @@ -659,11 +660,14 @@ export class DefaultSettings extends Disposable { } private matchesScope(property: IConfigurationNode): boolean { + if (!property.scope) { + return true; + } if (this.target === ConfigurationTarget.WORKSPACE_FOLDER) { - return property.scope === ConfigurationScope.RESOURCE; + return FOLDER_SCOPES.indexOf(property.scope) !== -1; } if (this.target === ConfigurationTarget.WORKSPACE) { - return property.scope === ConfigurationScope.WINDOW || property.scope === ConfigurationScope.RESOURCE; + return WORKSPACE_SCOPES.indexOf(property.scope) !== -1; } return true; } From ae42e42cf10df59773851b2d69db08189d6989eb Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 16 Aug 2019 18:08:16 +0200 Subject: [PATCH 304/613] Seti uses TS-icon for jsonc-language. Fixes #78261 --- extensions/theme-seti/build/update-icon-theme.js | 6 ++++-- extensions/theme-seti/cgmanifest.json | 2 +- extensions/theme-seti/icons/vs-seti-icon-theme.json | 10 ++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/extensions/theme-seti/build/update-icon-theme.js b/extensions/theme-seti/build/update-icon-theme.js index 6819e853339aa..3e14951e34228 100644 --- a/extensions/theme-seti/build/update-icon-theme.js +++ b/extensions/theme-seti/build/update-icon-theme.js @@ -32,7 +32,8 @@ let nonBuiltInLanguages = { // { fileNames, extensions } "haml": { extensions: ['haml'] }, "stylus": { extensions: ['styl'] }, "vala": { extensions: ['vala'] }, - "todo": { fileNames: ['todo'] } + "todo": { fileNames: ['todo'] }, + "jsonc": { extensions: ['json'] } }; let FROM_DISK = true; // set to true to take content from a repo checked out next to the vscode repo @@ -109,7 +110,7 @@ function downloadBinary(source, dest) { return new Promise((c, e) => { https.get(source, function (response) { switch (response.statusCode) { - case 200: + case 200: { let file = fs.createWriteStream(dest); response.on('data', function (chunk) { file.write(chunk); @@ -121,6 +122,7 @@ function downloadBinary(source, dest) { e(err.message); }); break; + } case 301: case 302: case 303: diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index c742c019ea339..1c86b8bcb2bc5 100644 --- a/extensions/theme-seti/cgmanifest.json +++ b/extensions/theme-seti/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "seti-ui", "repositoryUrl": "https://github.com/jesseweed/seti-ui", - "commitHash": "904c16acced1134a81b31d71d60293288c31334b" + "commitHash": "85a222708824c6f19bbecbec71633d2c97077dad" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json index 52a81da4596d7..825ac52ee830c 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -1564,6 +1564,7 @@ "version.md": "_clock", "version": "_clock", "mvnw": "_maven", + "tsconfig.json": "_tsconfig", "swagger.json": "_json_1", "swagger.yml": "_json_1", "swagger.yaml": "_json_1", @@ -1573,6 +1574,8 @@ "docker-healthcheck": "_docker_2", "docker-compose.yml": "_docker_3", "docker-compose.yaml": "_docker_3", + "docker-compose.override.yml": "_docker_3", + "docker-compose.override.yaml": "_docker_3", "firebase.json": "_firebase", "geckodriver": "_firefox", "gruntfile.js": "_grunt", @@ -1940,6 +1943,7 @@ "version.md": "_clock_light", "version": "_clock_light", "mvnw": "_maven_light", + "tsconfig.json": "_tsconfig_light", "swagger.json": "_json_1_light", "swagger.yml": "_json_1_light", "swagger.yaml": "_json_1_light", @@ -1949,6 +1953,8 @@ "docker-healthcheck": "_docker_2_light", "docker-compose.yml": "_docker_3_light", "docker-compose.yaml": "_docker_3_light", + "docker-compose.override.yml": "_docker_3_light", + "docker-compose.override.yaml": "_docker_3_light", "firebase.json": "_firebase_light", "geckodriver": "_firefox_light", "gruntfile.js": "_grunt_light", @@ -1980,5 +1986,5 @@ "npm-debug.log": "_npm_ignored_light" } }, - "version": "https://github.com/jesseweed/seti-ui/commit/904c16acced1134a81b31d71d60293288c31334b" -} + "version": "https://github.com/jesseweed/seti-ui/commit/85a222708824c6f19bbecbec71633d2c97077dad" +} \ No newline at end of file From 5470be1b7e73b18ec3741ed85422c8b657b68469 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 15 Aug 2019 16:37:36 -0700 Subject: [PATCH 305/613] flexible height input boxes and fitler out arrow keys --- src/vs/editor/contrib/find/findWidget.css | 6 +- src/vs/editor/contrib/find/findWidget.ts | 78 ++++++++++++++++++++++- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index 7e1140442eac4..515dcad1123b8 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -81,7 +81,7 @@ .monaco-editor .find-widget > .find-part .monaco-inputbox, .monaco-editor .find-widget > .replace-part .monaco-inputbox { - height: 25px; + min-height: 25px; } .monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input { @@ -94,7 +94,9 @@ } .monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input, -.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input { +.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .mirror, +.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input, +.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .mirror { padding-top: 2px; padding-bottom: 2px; } diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index a25d3b5b46432..8436fd3dd472a 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -84,6 +84,22 @@ export class FindWidgetViewZone implements IViewZone { } } +function stopPropagationForMultiLineUpwards(event: IKeyboardEvent, value: string, textarea: HTMLTextAreaElement | null) { + const isMultiline = !!value.match(/\n/); + if (textarea && isMultiline && textarea.selectionStart > 0) { + event.stopPropagation(); + return; + } +} + +function stopPropagationForMultiLineDownwards(event: IKeyboardEvent, value: string, textarea: HTMLTextAreaElement | null) { + const isMultiline = !!value.match(/\n/); + if (textarea && isMultiline && textarea.selectionEnd < textarea.value.length) { + event.stopPropagation(); + return; + } +} + export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSashLayoutProvider { private static readonly ID = 'editor.contrib.findWidget'; private readonly _codeEditor: ICodeEditor; @@ -646,6 +662,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } } + private _tryUpdateHeight() { + + } + // ----- Public public focusFindInput(): void { @@ -705,6 +725,22 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas return; } + if (e.equals(KeyMod.WinCtrl | KeyCode.Enter)) { + const inputElement = this._findInput.inputBox.inputElement; + const start = inputElement.selectionStart; + const end = inputElement.selectionEnd; + const content = inputElement.value; + + if (start && end) { + const value = content.substr(0, start) + '\n' + content.substr(end); + this._findInput.inputBox.value = value; + inputElement.setSelectionRange(start + 1, start + 1); + this._findInput.inputBox.layout(); + e.preventDefault(); + return; + } + } + if (e.equals(KeyCode.Tab)) { if (this._isReplaceVisible) { this._replaceInputBox.focus(); @@ -720,6 +756,14 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas e.preventDefault(); return; } + + if (e.equals(KeyCode.UpArrow)) { + return stopPropagationForMultiLineUpwards(e, this._findInput.getValue(), this._findInput.domNode.querySelector('textarea')); + } + + if (e.equals(KeyCode.DownArrow)) { + return stopPropagationForMultiLineDownwards(e, this._findInput.getValue(), this._findInput.domNode.querySelector('textarea')); + } } private _onReplaceInputKeyDown(e: IKeyboardEvent): void { @@ -730,6 +774,22 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas return; } + if (e.equals(KeyMod.WinCtrl | KeyCode.Enter)) { + const inputElement = this._replaceInputBox.inputElement; + const start = inputElement.selectionStart; + const end = inputElement.selectionEnd; + const content = inputElement.value; + + if (start && end) { + const value = content.substr(0, start) + '\n' + content.substr(end); + this._replaceInputBox.value = value; + inputElement.setSelectionRange(start + 1, start + 1); + this._replaceInputBox.layout(); + e.preventDefault(); + return; + } + } + if (e.equals(KeyMod.CtrlCmd | KeyCode.Enter)) { this._controller.replaceAll(); e.preventDefault(); @@ -753,6 +813,14 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas e.preventDefault(); return; } + + if (e.equals(KeyCode.UpArrow)) { + return stopPropagationForMultiLineUpwards(e, this._replaceInputBox.value, this._replaceInputBox.element.querySelector('textarea')); + } + + if (e.equals(KeyCode.DownArrow)) { + return stopPropagationForMultiLineDownwards(e, this._replaceInputBox.value, this._replaceInputBox.element.querySelector('textarea')); + } } // ----- sash @@ -796,7 +864,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } catch (e) { return { content: e.message }; } - } + }, + flexibleHeight: true }, this._contextKeyService, true)); this._findInput.setRegex(!!this._state.isRegex); this._findInput.setCaseSensitive(!!this._state.matchCase); @@ -823,6 +892,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } } })); + this._register(this._findInput.inputBox.onDidHeightChange((e) => { + console.log(e); + })); if (platform.isLinux) { this._register(this._findInput.onMouseDown((e) => this._onFindInputMouseDown(e))); } @@ -907,7 +979,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._replaceInputBox = this._register(new ContextScopedHistoryInputBox(replaceInput, undefined, { ariaLabel: NLS_REPLACE_INPUT_LABEL, placeholder: NLS_REPLACE_INPUT_PLACEHOLDER, - history: [] + history: [], + flexibleHeight: true }, this._contextKeyService)); @@ -973,6 +1046,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._state.change({ isReplaceRevealed: !this._isReplaceVisible }, false); if (this._isReplaceVisible) { this._replaceInputBox.width = this._findInput.inputBox.width; + this._replaceInputBox.layout(); } this._showViewZone(); } From 60748bc0bc262160071ff0520beb0db97c174467 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 16 Aug 2019 10:41:41 -0700 Subject: [PATCH 306/613] Transion for find widget fade in fade out --- src/vs/editor/contrib/find/findWidget.css | 26 ++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index 515dcad1123b8..1dd9a43b67186 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -32,11 +32,11 @@ .monaco-editor .find-widget { position: absolute; z-index: 10; - top: -44px; /* find input height + shadow (10px) */ - height: 34px; /* find input height */ + top: unset; + bottom: 10px; overflow: hidden; line-height: 19px; - transition: top 200ms linear; + transition: top bottom 200ms linear; padding: 0 4px; } @@ -45,19 +45,15 @@ } /* Find widget when replace is toggled on */ -.monaco-editor .find-widget.replaceToggled { - top: -74px; /* find input height + replace input height + shadow (10px) */ - height: 64px; /* find input height + replace input height */ -} .monaco-editor .find-widget.replaceToggled > .replace-part { display: flex; display: -webkit-flex; - align-items: center; } .monaco-editor .find-widget.visible, .monaco-editor .find-widget.replaceToggled.visible { top: 0; + bottom: unset; } .monaco-editor .find-widget .monaco-inputbox .input { @@ -76,7 +72,6 @@ font-size: 12px; display: flex; display: -webkit-flex; - align-items: center; } .monaco-editor .find-widget > .find-part .monaco-inputbox, @@ -85,7 +80,6 @@ } .monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input { - width: 100% !important; padding-right: 66px; } @@ -101,6 +95,18 @@ padding-bottom: 2px; } +.monaco-editor .find-widget > .find-part .find-actions { + height: 25px; + display: flex; + align-items: center; +} + +.monaco-editor .find-widget > .replace-part .replace-actions { + height: 25px; + display: flex; + align-items: center; +} + .monaco-editor .find-widget .monaco-findInput { vertical-align: middle; display: flex; From e1d5f1a51720a954899de50eb81028d23d148c8d Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 16 Aug 2019 10:45:16 -0700 Subject: [PATCH 307/613] tab sequence for preserve case button --- src/vs/editor/contrib/find/findWidget.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 8436fd3dd472a..e2d476314cd93 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -892,6 +892,14 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } } })); + this._register(this._findInput.onRegexKeyDown((e) => { + if (e.equals(KeyCode.Tab)) { + if (this._isReplaceVisible) { + this._preserveCase.focus(); + e.preventDefault(); + } + } + })); this._register(this._findInput.inputBox.onDidHeightChange((e) => { console.log(e); })); @@ -1001,6 +1009,22 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._replaceInputBox.focus(); } })); + this._register(this._preserveCase.onKeyDown((e) => { + if (e.equals(KeyCode.Tab)) { + if (this._prevBtn.isEnabled()) { + this._prevBtn.focus(); + } else if (this._nextBtn.isEnabled()) { + this._nextBtn.focus(); + } else if (this._toggleSelectionFind.domNode.tabIndex >= 0) { + this._toggleSelectionFind.focus(); + } else if (this._closeBtn.isEnabled()) { + this._closeBtn.focus(); + } + + e.preventDefault(); + } + } + )); // Replace one button this._replaceBtn = this._register(new SimpleButton({ From 88c58b2486fabac9997ae34f21f60babe2af2163 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 16 Aug 2019 10:45:47 -0700 Subject: [PATCH 308/613] flexible height of find widget and align options always at top --- src/vs/editor/contrib/find/findWidget.ts | 50 ++++++++++++++++++++---- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index e2d476314cd93..40393e06533b1 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -312,6 +312,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } } } + if ((e.isRevealed || e.isReplaceRevealed) && (this._state.isRevealed || this._state.isReplaceRevealed)) { + this._tryUpdateHeight(); + } + if (e.isRegex) { this._findInput.setRegex(this._state.isRegex); } @@ -663,7 +667,25 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } private _tryUpdateHeight() { + let totalheight = 0; + + // find input margin top + totalheight += 4; + // find input height + totalheight += this._findInput.inputBox.height + 2 /** input box border */; + + if (this._isReplaceVisible) { + // replace input margin + totalheight += 4; + + totalheight += this._replaceInputBox.height + 2 /** input box border */; + } + + // margin bottom + totalheight += 4; + + this._domNode.style.height = `${totalheight}px`; } // ----- Public @@ -901,7 +923,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } })); this._register(this._findInput.inputBox.onDidHeightChange((e) => { - console.log(e); + this._tryUpdateHeight(); })); if (platform.isLinux) { this._register(this._findInput.onMouseDown((e) => this._onFindInputMouseDown(e))); @@ -932,13 +954,16 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas let findPart = document.createElement('div'); findPart.className = 'find-part'; findPart.appendChild(this._findInput.domNode); - findPart.appendChild(this._matchesCount); - findPart.appendChild(this._prevBtn.domNode); - findPart.appendChild(this._nextBtn.domNode); + const actionsContainer = document.createElement('div'); + actionsContainer.className = 'find-actions'; + findPart.appendChild(actionsContainer); + actionsContainer.appendChild(this._matchesCount); + actionsContainer.appendChild(this._prevBtn.domNode); + actionsContainer.appendChild(this._nextBtn.domNode); // Toggle selection button this._toggleSelectionFind = this._register(new SimpleCheckbox({ - parent: findPart, + parent: actionsContainer, title: NLS_TOGGLE_SELECTION_FIND_TITLE + this._keybindingLabelFor(FIND_IDS.ToggleSearchScopeCommand), onChange: () => { if (this._toggleSelectionFind.checked) { @@ -978,7 +1003,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } })); - findPart.appendChild(this._closeBtn.domNode); + actionsContainer.appendChild(this._closeBtn.domNode); // Replace input let replaceInput = document.createElement('div'); @@ -996,6 +1021,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._register(this._replaceInputBox.onDidChange(() => { this._state.change({ replaceString: this._replaceInputBox.value }, false); })); + this._register(this._replaceInputBox.onDidHeightChange((e) => { + this._tryUpdateHeight(); + })); this._preserveCase = this._register(new Checkbox({ actionClassName: 'monaco-preserve-case', @@ -1059,8 +1087,13 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas let replacePart = document.createElement('div'); replacePart.className = 'replace-part'; replacePart.appendChild(replaceInput); - replacePart.appendChild(this._replaceBtn.domNode); - replacePart.appendChild(this._replaceAllBtn.domNode); + + const replaceActionsContainer = document.createElement('div'); + replaceActionsContainer.className = 'replace-actions'; + replacePart.appendChild(replaceActionsContainer); + + replaceActionsContainer.appendChild(this._replaceBtn.domNode); + replaceActionsContainer.appendChild(this._replaceAllBtn.domNode); // Toggle replace button this._toggleReplaceBtn = this._register(new SimpleButton({ @@ -1113,6 +1146,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas return; } this._domNode.style.width = `${width}px`; + this._findInput.inputBox.width = inputBoxWidth; if (this._isReplaceVisible) { this._replaceInputBox.width = inputBoxWidth; } From c6a7d840cb20d0d4e2cde7742aca464f2b8adc78 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 16 Aug 2019 10:52:48 -0700 Subject: [PATCH 309/613] Focus toggle find selection button --- src/vs/editor/contrib/find/findWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 40393e06533b1..fb73ecbd90c13 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -1044,7 +1044,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } else if (this._nextBtn.isEnabled()) { this._nextBtn.focus(); } else if (this._toggleSelectionFind.domNode.tabIndex >= 0) { - this._toggleSelectionFind.focus(); + this._toggleSelectionFind.domNode.focus(); } else if (this._closeBtn.isEnabled()) { this._closeBtn.focus(); } From ab62d23bf9103d1074b544d90b4a731397f8ae83 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 16 Aug 2019 10:55:34 -0700 Subject: [PATCH 310/613] toggle selection find box should align with other checkbox --- src/vs/editor/contrib/find/findWidget.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index fb73ecbd90c13..46414dd3a5607 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -1043,8 +1043,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._prevBtn.focus(); } else if (this._nextBtn.isEnabled()) { this._nextBtn.focus(); - } else if (this._toggleSelectionFind.domNode.tabIndex >= 0) { - this._toggleSelectionFind.domNode.focus(); + } else if (this._toggleSelectionFind.isEnabled()) { + this._toggleSelectionFind.focus(); } else if (this._closeBtn.isEnabled()) { this._closeBtn.focus(); } @@ -1209,6 +1209,10 @@ class SimpleCheckbox extends Widget { return this._domNode; } + public isEnabled(): boolean { + return (this._domNode.tabIndex >= 0); + } + public get checked(): boolean { return this._checkbox.checked; } @@ -1218,7 +1222,7 @@ class SimpleCheckbox extends Widget { } public focus(): void { - this._checkbox.focus(); + this._domNode.focus(); } private enable(): void { From 833507b823c74a94239d0f654ec1cf5aa0f59f37 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 16 Aug 2019 11:21:54 -0700 Subject: [PATCH 311/613] update textarea width when find widget or editor resizes --- src/vs/editor/contrib/find/findWidget.css | 6 ++++++ src/vs/editor/contrib/find/findWidget.ts | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index 1dd9a43b67186..f862d6eaf705b 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -80,6 +80,7 @@ } .monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input { + width: 100% !important; padding-right: 66px; } @@ -114,6 +115,11 @@ flex:1; } +.monaco-editor .find-widget .monaco-findInput .monaco-scrollable-element { + /* Make sure textarea inherits the width correctly */ + width: 100%; +} + .monaco-editor .find-widget .matchesCount { display: flex; display: -webkit-flex; diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 46414dd3a5607..dd7419811ae7f 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -635,7 +635,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (widgetWidth > FIND_WIDGET_INITIAL_WIDTH) { // as the widget is resized by users, we may need to change the max width of the widget as the editor width changes. this._domNode.style.maxWidth = `${editorWidth - 28 - minimapWidth - 15}px`; - this._replaceInputBox.inputElement.style.width = `${dom.getTotalWidth(this._findInput.inputBox.inputElement)}px`; + this._replaceInputBox.width = dom.getTotalWidth(this._findInput.inputBox.inputElement); return; } } @@ -659,9 +659,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } if (this._resized) { - let findInputWidth = dom.getTotalWidth(this._findInput.inputBox.inputElement); + this._findInput.inputBox.layout(); + let findInputWidth = this._findInput.inputBox.width; if (findInputWidth > 0) { - this._replaceInputBox.inputElement.style.width = `${findInputWidth}px`; + this._replaceInputBox.width = findInputWidth; } } } From dc5f96bc62a049300cf32e23af990cba27bef7a4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 16 Aug 2019 11:25:45 -0700 Subject: [PATCH 312/613] Pick up TS rc --- extensions/package.json | 2 +- extensions/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index 57d5f5da31157..5c73fcbd3d7cf 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.6.0-dev.20190810" + "typescript": "3.6.1-rc" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 233cf189e1462..26979cdf9d679 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.6.0-dev.20190810: - version "3.6.0-dev.20190810" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.0-dev.20190810.tgz#dda80279480131eec9b05e3b78182a1ba1efe105" - integrity sha512-gubcQ8Sn2G5AO1KhjvLpoFrutV7o/ZJ7wCDBC1IKgNI8R2vadIxTystJxAFqkb9boQ7tyRrZ6FwM5EL5ZYfJrg== +typescript@3.6.1-rc: + version "3.6.1-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.1-rc.tgz#9db650b25d8ef033d9e25b3057bdd1e102bb434b" + integrity sha512-u6AQN9AoocZKYSz8zcc1Qh/V/mbAO+BHc73fTiKlIdjzU60A8TesrK9/7kg3GM8o2RxNyCeOFpcevEtnfUyaLg== From 08bb8bae8354d88c09823bda3a30f4bea6cee527 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 16 Aug 2019 11:39:13 -0700 Subject: [PATCH 313/613] xterm-addon-search@0.2.0-beta5 - Better align search with how monaco does it Fixes #78281 --- package.json | 4 ++-- remote/package.json | 2 +- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index f8a973d504eef..b190a4b3a33f3 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "vscode-sqlite3": "4.0.8", "vscode-textmate": "^4.2.2", "xterm": "3.15.0-beta99", - "xterm-addon-search": "0.2.0-beta3", + "xterm-addon-search": "0.2.0-beta5", "xterm-addon-web-links": "0.1.0-beta10", "yauzl": "^2.9.2", "yazl": "^2.4.3" @@ -159,4 +159,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} \ No newline at end of file +} diff --git a/remote/package.json b/remote/package.json index ae754c1ae64a8..9bbc0f44c5c31 100644 --- a/remote/package.json +++ b/remote/package.json @@ -22,7 +22,7 @@ "vscode-ripgrep": "^1.5.6", "vscode-textmate": "^4.2.2", "xterm": "3.15.0-beta99", - "xterm-addon-search": "0.2.0-beta3", + "xterm-addon-search": "0.2.0-beta5", "xterm-addon-web-links": "0.1.0-beta10", "yauzl": "^2.9.2", "yazl": "^2.4.3" diff --git a/remote/yarn.lock b/remote/yarn.lock index 5d578d3130bfc..54e0619915e23 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -1217,10 +1217,10 @@ vscode-windows-registry@1.0.1: dependencies: nan "^2.12.1" -xterm-addon-search@0.2.0-beta3: - version "0.2.0-beta3" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta3.tgz#710ce14658e269c5a4791f5a9e2f520883a2e62b" - integrity sha512-KzVdkEtGbKJe9ER2TmrI7XjF/wUq1lir9U63vPJi0t2ymQvIECl1V63f9QtOp1vvpdhbZiXBxO+vGTj+y0tRow== +xterm-addon-search@0.2.0-beta5: + version "0.2.0-beta5" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta5.tgz#258d7cb1511d9060cd4520f0f82e408000fd4f53" + integrity sha512-Tg+d8scch0rYOVmzBbX35Y1GXtq+eu/YlzbXznmTo/yD83j3BQlXOhgECu/Yv8EX5JwFmzbfVRWC+JWnfigwGg== xterm-addon-web-links@0.1.0-beta10: version "0.1.0-beta10" diff --git a/yarn.lock b/yarn.lock index 7f3c5365e14a1..5dfdc062d4f3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9987,10 +9987,10 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm-addon-search@0.2.0-beta3: - version "0.2.0-beta3" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta3.tgz#710ce14658e269c5a4791f5a9e2f520883a2e62b" - integrity sha512-KzVdkEtGbKJe9ER2TmrI7XjF/wUq1lir9U63vPJi0t2ymQvIECl1V63f9QtOp1vvpdhbZiXBxO+vGTj+y0tRow== +xterm-addon-search@0.2.0-beta5: + version "0.2.0-beta5" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.2.0-beta5.tgz#258d7cb1511d9060cd4520f0f82e408000fd4f53" + integrity sha512-Tg+d8scch0rYOVmzBbX35Y1GXtq+eu/YlzbXznmTo/yD83j3BQlXOhgECu/Yv8EX5JwFmzbfVRWC+JWnfigwGg== xterm-addon-web-links@0.1.0-beta10: version "0.1.0-beta10" From f2334a79d34986f4ab5834f69a961d7249303fd0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 16 Aug 2019 11:39:23 -0700 Subject: [PATCH 314/613] Format file --- .../workbench/contrib/debug/browser/variablesView.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 3c3890b574d11..052f2506db109 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -89,11 +89,11 @@ export class VariablesView extends ViewletPanel { this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, treeContainer, new VariablesDelegate(), [this.instantiationService.createInstance(VariablesRenderer), new ScopesRenderer()], new VariablesDataSource(), { - ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"), - accessibilityProvider: new VariablesAccessibilityProvider(), - identityProvider: { getId: (element: IExpression | IScope) => element.getId() }, - keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IExpression | IScope) => e } - }); + ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"), + accessibilityProvider: new VariablesAccessibilityProvider(), + identityProvider: { getId: (element: IExpression | IScope) => element.getId() }, + keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IExpression | IScope) => e } + }); this.tree.setInput(this.debugService.getViewModel()).then(null, onUnexpectedError); From 1aa34f97fb0f80fd80934ed8f5062115cc9f0601 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 16 Aug 2019 11:46:10 -0700 Subject: [PATCH 315/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b190a4b3a33f3..2a43502df641e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "32d06dfa691431ba32bbe11ac39ab4b30663b342", + "distro": "481202cd856f5dbbb010996544e4fa7498154f2e", "author": { "name": "Microsoft Corporation" }, From 1990b5a69ce112353b6e86627e567df5d7bc4825 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 16 Aug 2019 13:47:03 -0700 Subject: [PATCH 316/613] Max height and hidden vertical scrollbar --- src/vs/base/browser/ui/findinput/findInput.ts | 5 ++++- src/vs/editor/contrib/find/findWidget.css | 6 ++++++ src/vs/editor/contrib/find/findWidget.ts | 6 ++++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index 2a2964beeef82..0c64d37c49ed5 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -24,6 +24,7 @@ export interface IFindInputOptions extends IFindInputStyles { readonly validation?: IInputValidator; readonly label: string; readonly flexibleHeight?: boolean; + readonly flexibleMaxHeight?: number; readonly appendCaseSensitiveLabel?: string; readonly appendWholeWordsLabel?: string; @@ -119,6 +120,7 @@ export class FindInput extends Widget { const appendRegexLabel = options.appendRegexLabel || ''; const history = options.history || []; const flexibleHeight = !!options.flexibleHeight; + const flexibleMaxHeight = options.flexibleMaxHeight; this.domNode = document.createElement('div'); dom.addClass(this.domNode, 'monaco-findInput'); @@ -142,7 +144,8 @@ export class FindInput extends Widget { inputValidationErrorForeground: this.inputValidationErrorForeground, inputValidationErrorBorder: this.inputValidationErrorBorder, history, - flexibleHeight + flexibleHeight, + flexibleMaxHeight })); this.regex = this._register(new RegexCheckbox({ diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index f862d6eaf705b..ac0a74d55b908 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -120,6 +120,12 @@ width: 100%; } +.monaco-editor .find-widget .monaco-findInput .monaco-scrollable-element .scrollbar.vertical, +.monaco-editor .find-widget .replace-input .monaco-scrollable-element .scrollbar.vertical { + /* Hide vertical scrollbar */ + opacity: 0; +} + .monaco-editor .find-widget .matchesCount { display: flex; display: -webkit-flex; diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index dd7419811ae7f..3e845b4d1ce9c 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -888,7 +888,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas return { content: e.message }; } }, - flexibleHeight: true + flexibleHeight: true, + flexibleMaxHeight: 118 }, this._contextKeyService, true)); this._findInput.setRegex(!!this._state.isRegex); this._findInput.setCaseSensitive(!!this._state.matchCase); @@ -1014,7 +1015,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas ariaLabel: NLS_REPLACE_INPUT_LABEL, placeholder: NLS_REPLACE_INPUT_PLACEHOLDER, history: [], - flexibleHeight: true + flexibleHeight: true, + flexibleMaxHeight: 118 }, this._contextKeyService)); From 8b6c996439dcf2f0e5632e2180c8ba34693b5418 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 16 Aug 2019 11:48:04 -0700 Subject: [PATCH 317/613] Remove not null suppression --- src/vs/base/browser/markdownRenderer.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 7fd9f63b75a4d..493f60ef8a1b1 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -143,8 +143,9 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende }; } - if (options.actionHandler) { - options.actionHandler.disposeables.add(DOM.addStandardDisposableListener(element, 'click', event => { + const actionHandler = options.actionHandler; + if (actionHandler) { + actionHandler.disposeables.add(DOM.addStandardDisposableListener(element, 'click', event => { let target: HTMLElement | null = event.target; if (target.tagName !== 'A') { target = target.parentElement; @@ -155,7 +156,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende try { const href = target.dataset['href']; if (href) { - options.actionHandler!.callback(href, event); + actionHandler.callback(href, event); } } catch (err) { onUnexpectedError(err); From ac5887deee760cb2686225080215263bf794ba78 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 16 Aug 2019 14:40:14 -0700 Subject: [PATCH 318/613] Use dispoablestore --- src/vs/workbench/api/common/menusExtensionPoint.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 65395d5ed4cd7..2ce62f2a36775 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -12,7 +12,7 @@ import { IExtensionPointUser, ExtensionMessageCollector, ExtensionsRegistry } fr import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { MenuId, MenuRegistry, ILocalizedString, IMenuItem } from 'vs/platform/actions/common/actions'; import { URI } from 'vs/base/common/uri'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; namespace schema { @@ -329,7 +329,7 @@ namespace schema { }; } -let _commandRegistrations: IDisposable[] = []; +const _commandRegistrations = new DisposableStore(); export const commandsExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'commands', @@ -338,7 +338,7 @@ export const commandsExtensionPoint = ExtensionsRegistry.registerExtensionPoint< commandsExtensionPoint.setHandler(extensions => { - function handleCommand(userFriendlyCommand: schema.IUserFriendlyCommand, extension: IExtensionPointUser, disposables: IDisposable[]) { + function handleCommand(userFriendlyCommand: schema.IUserFriendlyCommand, extension: IExtensionPointUser, disposables: DisposableStore) { if (!schema.isValidCommand(userFriendlyCommand, extension.collector)) { return; @@ -368,16 +368,16 @@ commandsExtensionPoint.setHandler(extensions => { precondition: ContextKeyExpr.deserialize(enablement), iconLocation: absoluteIcon }); - disposables.push(registration); + disposables.add(registration); } // remove all previous command registrations - _commandRegistrations = dispose(_commandRegistrations); + _commandRegistrations.clear(); - for (let extension of extensions) { + for (const extension of extensions) { const { value } = extension; if (Array.isArray(value)) { - for (let command of value) { + for (const command of value) { handleCommand(command, extension, _commandRegistrations); } } else { From 445ad7021bcb00af3a7654f73ecaeecd9884473f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 16 Aug 2019 14:41:24 -0700 Subject: [PATCH 319/613] Use closure instead of parameters --- src/vs/workbench/api/common/menusExtensionPoint.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/common/menusExtensionPoint.ts b/src/vs/workbench/api/common/menusExtensionPoint.ts index 2ce62f2a36775..cb6cddcb57d8f 100644 --- a/src/vs/workbench/api/common/menusExtensionPoint.ts +++ b/src/vs/workbench/api/common/menusExtensionPoint.ts @@ -338,7 +338,7 @@ export const commandsExtensionPoint = ExtensionsRegistry.registerExtensionPoint< commandsExtensionPoint.setHandler(extensions => { - function handleCommand(userFriendlyCommand: schema.IUserFriendlyCommand, extension: IExtensionPointUser, disposables: DisposableStore) { + function handleCommand(userFriendlyCommand: schema.IUserFriendlyCommand, extension: IExtensionPointUser) { if (!schema.isValidCommand(userFriendlyCommand, extension.collector)) { return; @@ -368,7 +368,7 @@ commandsExtensionPoint.setHandler(extensions => { precondition: ContextKeyExpr.deserialize(enablement), iconLocation: absoluteIcon }); - disposables.add(registration); + _commandRegistrations.add(registration); } // remove all previous command registrations @@ -376,12 +376,12 @@ commandsExtensionPoint.setHandler(extensions => { for (const extension of extensions) { const { value } = extension; - if (Array.isArray(value)) { + if (Array.isArray(value)) { for (const command of value) { - handleCommand(command, extension, _commandRegistrations); + handleCommand(command, extension); } } else { - handleCommand(value, extension, _commandRegistrations); + handleCommand(value, extension); } } }); From 4707bc535e1e1141f962ec895c465a7d5a4b1803 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 16 Aug 2019 14:48:23 -0700 Subject: [PATCH 320/613] Use type for IExtensionPointHandler and mark array readonly --- .../services/extensions/common/extensionsRegistry.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 9776a42da6ab4..6fba325635f67 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -61,9 +61,7 @@ export interface IExtensionPointUser { collector: ExtensionMessageCollector; } -export interface IExtensionPointHandler { - (extensions: IExtensionPointUser[], delta: ExtensionPointUserDelta): void; -} +export type IExtensionPointHandler = (extensions: readonly IExtensionPointUser[], delta: ExtensionPointUserDelta) => void; export interface IExtensionPoint { name: string; From 70dc55955d586ebd427658b43cdb344f2047f9c2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 16 Aug 2019 14:49:53 -0700 Subject: [PATCH 321/613] Marking arrays readonly in ExtensionPointUserDelta --- src/vs/workbench/api/browser/viewsExtensionPoint.ts | 8 ++++---- .../services/extensions/common/extensionsRegistry.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 2a5030ae9f87c..e6621fc362cae 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -210,7 +210,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { }); } - private addCustomViewContainers(extensionPoints: IExtensionPointUser[], existingViewContainers: ViewContainer[]): void { + private addCustomViewContainers(extensionPoints: readonly IExtensionPointUser[], existingViewContainers: ViewContainer[]): void { const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); let order = TEST_VIEW_CONTAINER_ORDER + viewContainersRegistry.all.filter(v => !!v.extensionId).length + 1; for (let { value, collector, description } of extensionPoints) { @@ -227,7 +227,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } } - private removeCustomViewContainers(extensionPoints: IExtensionPointUser[]): void { + private removeCustomViewContainers(extensionPoints: readonly IExtensionPointUser[]): void { const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); const removedExtensions: Set = extensionPoints.reduce((result, e) => { result.add(ExtensionIdentifier.toKey(e.description.identifier)); return result; }, new Set()); for (const viewContainer of viewContainersRegistry.all) { @@ -378,7 +378,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { }); } - private addViews(extensions: IExtensionPointUser[]): void { + private addViews(extensions: readonly IExtensionPointUser[]): void { for (const extension of extensions) { const { value, collector } = extension; @@ -442,7 +442,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return this.viewContainersRegistry.get(EXPLORER)!; } - private removeViews(extensions: IExtensionPointUser[]): void { + private removeViews(extensions: readonly IExtensionPointUser[]): void { const removedExtensions: Set = extensions.reduce((result, e) => { result.add(ExtensionIdentifier.toKey(e.description.identifier)); return result; }, new Set()); for (const viewContainer of this.viewContainersRegistry.all) { const removedViews = this.viewsRegistry.getViews(viewContainer).filter((v: ICustomViewDescriptor) => v.extensionId && removedExtensions.has(ExtensionIdentifier.toKey(v.extensionId))); diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 6fba325635f67..f5fd3137e472e 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -71,7 +71,7 @@ export interface IExtensionPoint { export class ExtensionPointUserDelta { - private static _toSet(arr: IExtensionPointUser[]): Set { + private static _toSet(arr: readonly IExtensionPointUser[]): Set { const result = new Set(); for (let i = 0, len = arr.length; i < len; i++) { result.add(ExtensionIdentifier.toKey(arr[i].description.identifier)); @@ -79,7 +79,7 @@ export class ExtensionPointUserDelta { return result; } - public static compute(previous: IExtensionPointUser[] | null, current: IExtensionPointUser[]): ExtensionPointUserDelta { + public static compute(previous: readonly IExtensionPointUser[] | null, current: readonly IExtensionPointUser[]): ExtensionPointUserDelta { if (!previous || !previous.length) { return new ExtensionPointUserDelta(current, []); } @@ -97,8 +97,8 @@ export class ExtensionPointUserDelta { } constructor( - public readonly added: IExtensionPointUser[], - public readonly removed: IExtensionPointUser[], + public readonly added: readonly IExtensionPointUser[], + public readonly removed: readonly IExtensionPointUser[], ) { } } From 45dd218d90b01fa88f24613dfbb668e7e303515b Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 16 Aug 2019 16:06:35 -0700 Subject: [PATCH 322/613] Update extra space on top of editor when find widget state changes --- src/vs/editor/contrib/find/findWidget.ts | 91 ++++++++++++++++-------- 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 3e845b4d1ce9c..6b59dafaba9fb 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -64,9 +64,7 @@ const REPLACE_INPUT_AREA_WIDTH = FIND_INPUT_AREA_WIDTH; let MAX_MATCHES_COUNT_WIDTH = 69; let FIND_ALL_CONTROLS_WIDTH = 17/** Find Input margin-left */ + (MAX_MATCHES_COUNT_WIDTH + 3 + 1) /** Match Results */ + 23 /** Button */ * 4 + 2/** sash */; -const FIND_INPUT_AREA_HEIGHT = 34; // The height of Find Widget when Replace Input is not visible. -const FIND_REPLACE_AREA_HEIGHT = 64; // The height of Find Widget when Replace Input is visible. - +const FIND_INPUT_AREA_HEIGHT = 33; // The height of Find Widget when Replace Input is not visible. export class FindWidgetViewZone implements IViewZone { public readonly afterLineNumber: number; @@ -110,6 +108,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private readonly _contextKeyService: IContextKeyService; private _domNode!: HTMLElement; + private _cachedHeight: number | null; private _findInput!: FindInput; private _replaceInputBox!: HistoryInputBox; @@ -313,7 +312,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } } if ((e.isRevealed || e.isReplaceRevealed) && (this._state.isRevealed || this._state.isReplaceRevealed)) { - this._tryUpdateHeight(); + if (this._tryUpdateHeight()) { + this._showViewZone(); + } } if (e.isRegex) { @@ -532,12 +533,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } this._codeEditor.changeViewZones((accessor) => { - if (this._state.isReplaceRevealed) { - viewZone.heightInPx = FIND_REPLACE_AREA_HEIGHT; - } else { - viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT; - } - + viewZone.heightInPx = this._getHeight(); this._viewZoneId = accessor.addZone(viewZone); // scroll top adjust to make sure the editor doesn't scroll when adding viewzone at the beginning. this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + viewZone.heightInPx); @@ -545,30 +541,47 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } private _showViewZone(adjustScroll: boolean = true) { - const viewZone = this._viewZone; - if (!this._isVisible || !viewZone) { + if (!this._isVisible) { return; } - this._codeEditor.changeViewZones((accessor) => { - let scrollAdjustment = FIND_INPUT_AREA_HEIGHT; + const addExtraSpaceOnTop = this._codeEditor.getConfiguration().contribInfo.find.addExtraSpaceOnTop; + + if (!addExtraSpaceOnTop) { + return; + } + if (this._viewZone === undefined) { + this._viewZone = new FindWidgetViewZone(0); + } + + const viewZone = this._viewZone; + + this._codeEditor.changeViewZones((accessor) => { if (this._viewZoneId !== undefined) { - if (this._state.isReplaceRevealed) { - viewZone.heightInPx = FIND_REPLACE_AREA_HEIGHT; - scrollAdjustment = FIND_REPLACE_AREA_HEIGHT - FIND_INPUT_AREA_HEIGHT; - } else { - viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT; - scrollAdjustment = FIND_INPUT_AREA_HEIGHT - FIND_REPLACE_AREA_HEIGHT; + // the view zone already exists, we need to update the height + const newHeight = this._getHeight(); + if (newHeight === viewZone.heightInPx) { + return; } - accessor.removeZone(this._viewZoneId); + + let scrollAdjustment = newHeight - viewZone.heightInPx; + viewZone.heightInPx = newHeight; + accessor.layoutZone(this._viewZoneId); + + if (adjustScroll) { + this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + scrollAdjustment); + } + + return; } else { - viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT; - } - this._viewZoneId = accessor.addZone(viewZone); + const scrollAdjustment = this._getHeight(); + viewZone.heightInPx = scrollAdjustment; + this._viewZoneId = accessor.addZone(viewZone); - if (adjustScroll) { - this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + scrollAdjustment); + if (adjustScroll) { + this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + scrollAdjustment); + } } }); } @@ -667,7 +680,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } } - private _tryUpdateHeight() { + private _getHeight(): number { let totalheight = 0; // find input margin top @@ -685,8 +698,19 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas // margin bottom totalheight += 4; + return totalheight; + } + + private _tryUpdateHeight(): boolean { + const totalHeight = this._getHeight(); + if (this._cachedHeight !== null && this._cachedHeight === totalHeight) { + return false; + } + + this._cachedHeight = totalHeight; + this._domNode.style.height = `${totalHeight}px`; - this._domNode.style.height = `${totalheight}px`; + return true; } // ----- Public @@ -925,7 +949,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } })); this._register(this._findInput.inputBox.onDidHeightChange((e) => { - this._tryUpdateHeight(); + if (this._tryUpdateHeight()) { + this._showViewZone(); + } })); if (platform.isLinux) { this._register(this._findInput.onMouseDown((e) => this._onFindInputMouseDown(e))); @@ -1025,7 +1051,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._state.change({ replaceString: this._replaceInputBox.value }, false); })); this._register(this._replaceInputBox.onDidHeightChange((e) => { - this._tryUpdateHeight(); + if (this._isReplaceVisible && this._tryUpdateHeight()) { + this._showViewZone(); + } })); this._preserveCase = this._register(new Checkbox({ @@ -1153,6 +1181,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (this._isReplaceVisible) { this._replaceInputBox.width = inputBoxWidth; } + + this._findInput.inputBox.layout(); + this._tryUpdateHeight(); })); } From 98467c3b7cacf53103f56519673dc9ae81e7ee84 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 17 Aug 2019 10:17:09 +0200 Subject: [PATCH 323/613] web - storage/lifecycle :lipstick: --- src/vs/base/browser/dom.ts | 1 + .../lifecycle/browser/lifecycleService.ts | 12 ++++++--- .../storage/browser/storageService.ts | 27 ++++++++++++------- src/vs/workbench/browser/workbench.ts | 6 ++--- .../textfile/browser/textFileService.ts | 6 ++--- .../textfile/common/textFileService.ts | 4 +-- 6 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index a97f27a51e736..3967d3b3f0825 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -855,6 +855,7 @@ export const EventType = { KEY_UP: 'keyup', // HTML Document LOAD: 'load', + BEFORE_UNLOAD: 'beforeunload', UNLOAD: 'unload', ABORT: 'abort', ERROR: 'error', diff --git a/src/vs/platform/lifecycle/browser/lifecycleService.ts b/src/vs/platform/lifecycle/browser/lifecycleService.ts index 9a85a58262ec3..79301acb261f6 100644 --- a/src/vs/platform/lifecycle/browser/lifecycleService.ts +++ b/src/vs/platform/lifecycle/browser/lifecycleService.ts @@ -8,6 +8,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { AbstractLifecycleService } from 'vs/platform/lifecycle/common/lifecycleService'; import { localize } from 'vs/nls'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { addDisposableListener, EventType } from 'vs/base/browser/dom'; export class BrowserLifecycleService extends AbstractLifecycleService { @@ -22,10 +23,10 @@ export class BrowserLifecycleService extends AbstractLifecycleService { } private registerListeners(): void { - window.onbeforeunload = () => this.beforeUnload(); + addDisposableListener(window, EventType.BEFORE_UNLOAD, () => this.onBeforeUnload()); } - private beforeUnload(): string | null { + private onBeforeUnload(): string | null { let veto = false; // Before Shutdown @@ -34,7 +35,7 @@ export class BrowserLifecycleService extends AbstractLifecycleService { if (value === true) { veto = true; } else if (value instanceof Promise && !veto) { - console.warn(new Error('Long running onBeforeShutdown currently not supported')); + console.warn(new Error('Long running onBeforeShutdown currently not supported in the web')); veto = true; } }, @@ -49,11 +50,14 @@ export class BrowserLifecycleService extends AbstractLifecycleService { // No Veto: continue with Will Shutdown this._onWillShutdown.fire({ join() { - console.warn(new Error('Long running onWillShutdown currently not supported')); + console.warn(new Error('Long running onWillShutdown currently not supported in the web')); }, reason: ShutdownReason.QUIT }); + // Finally end with Shutdown event + this._onShutdown.fire(); + return null; } } diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index cec2d7dc9c773..5648772339f11 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -37,7 +37,7 @@ export class BrowserStorageService extends Disposable implements IStorageService private workspaceStorageFile: URI; private initializePromise: Promise; - private periodicSaveScheduler = this._register(new RunOnceScheduler(() => this.saveState(), 5000)); + private periodicSaveScheduler = this._register(new RunOnceScheduler(() => this.collectState(), 5000)); get hasPendingUpdate(): boolean { return this.globalStorageDatabase.hasPendingUpdate || this.workspaceStorageDatabase.hasPendingUpdate; @@ -57,14 +57,19 @@ export class BrowserStorageService extends Disposable implements IStorageService this.periodicSaveScheduler.schedule(); } - private saveState(): void { + private collectState(): void { runWhenIdle(() => { // this event will potentially cause new state to be stored // since new state will only be created while the document // has focus, one optimization is to not run this when the // document has no focus, assuming that state has not changed - if (document.hasFocus()) { + // + // another optimization is to not collect more state if we + // have a pending update already running which indicates + // that the connection is either slow or disconnected and + // thus unhealthy. + if (document.hasFocus() && !this.hasPendingUpdate) { this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE }); } @@ -197,7 +202,7 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase this._register(this.fileService.watch(this.file)); this._register(this.fileService.onFileChanges(e => { if (document.hasFocus()) { - return; // ignore changes from ourselves by checking for focus + return; // optimization: ignore changes from ourselves by checking for focus } if (!e.contains(this.file, FileChangeType.UPDATED)) { @@ -251,15 +256,17 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase await this.pendingUpdate; - this._hasPendingUpdate = true; + this.pendingUpdate = (async () => { + try { + this._hasPendingUpdate = true; + + await this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(mapToSerializable(items)))); - this.pendingUpdate = this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(mapToSerializable(items)))) - .then(() => { this.ensureWatching(); // now that the file must exist, ensure we watch it for changes - }) - .finally(() => { + } finally { this._hasPendingUpdate = false; - }); + } + })(); return this.pendingUpdate; } diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 0e61b26920de0..e6dde918a2dab 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -50,12 +50,12 @@ export class Workbench extends Layout { private readonly _onBeforeShutdown = this._register(new Emitter()); readonly onBeforeShutdown: Event = this._onBeforeShutdown.event; - private readonly _onShutdown = this._register(new Emitter()); - readonly onShutdown: Event = this._onShutdown.event; - private readonly _onWillShutdown = this._register(new Emitter()); readonly onWillShutdown: Event = this._onWillShutdown.event; + private readonly _onShutdown = this._register(new Emitter()); + readonly onShutdown: Event = this._onShutdown.event; + constructor( parent: HTMLElement, private readonly serviceCollection: ServiceCollection, diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index ac16d811705c3..3d2d91f5996d8 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -17,15 +17,15 @@ export class BrowserTextFileService extends TextFileService { } }; - protected beforeShutdown(reason: ShutdownReason): boolean { + protected onBeforeShutdown(reason: ShutdownReason): boolean { // Web: we cannot perform long running in the shutdown phase // As such we need to check sync if there are any dirty files // that have not been backed up yet and then prevent the shutdown // if that is the case. - return this.doBeforeShutdownSync(reason); + return this.doBeforeShutdownSync(); } - private doBeforeShutdownSync(reason: ShutdownReason): boolean { + private doBeforeShutdownSync(): boolean { const dirtyResources = this.getDirty(); if (!dirtyResources.length) { return false; // no dirty: no veto diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 713ef13e3950a..f9147e602e805 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -107,7 +107,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer private registerListeners(): void { // Lifecycle - this.lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown(event.reason))); + this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(event.reason))); this.lifecycleService.onShutdown(this.dispose, this); // Files configuration changes @@ -118,7 +118,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer })); } - protected beforeShutdown(reason: ShutdownReason): boolean | Promise { + protected onBeforeShutdown(reason: ShutdownReason): boolean | Promise { // Dirty files need treatment on shutdown const dirty = this.getDirty(); From 759d117d03f56e1fe62b5fe32d1b59c0dc0449a9 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Sun, 18 Aug 2019 20:09:13 +0200 Subject: [PATCH 324/613] Fix #79326, cleanup rendering Octicons bugs on Windows/Linux --- .../ui/octiconLabel/octicons/octicons2.css | 4 +- .../ui/octiconLabel/octicons/octicons2.svg | 49 ++++++++---------- .../ui/octiconLabel/octicons/octicons2.ttf | Bin 35276 -> 35564 bytes 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css index 6da9003b49a79..2e45f1805216f 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.css @@ -1,7 +1,7 @@ @font-face { font-family: "octicons2"; - src: url("./octicons2.ttf?064476e75412ccad4ae99d4bf24539b4") format("truetype"), -url("./octicons2.svg?064476e75412ccad4ae99d4bf24539b4#octicons2") format("svg"); + src: url("./octicons2.ttf?dee9935d8deb764a470d194d9f0d07f7") format("truetype"), +url("./octicons2.svg?dee9935d8deb764a470d194d9f0d07f7#octicons2") format("svg"); } .octicon, .mega-octicon { diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg index e7d0d05d458b0..4028dd4566626 100644 --- a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg +++ b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.svg @@ -9,7 +9,7 @@ + horiz-adv-x="1000" d=" M527.5 757.5H472.5L62.5 -8.75L90 -55H908.75L936.2499999999998 -8.75zM142.5 7.5L499.9999999999999 677.5L856.25 7.5zM468.7499999999999 445H531.25V195H468.75zM468.7499999999999 132.5H531.25V70H468.75z" /> @@ -198,7 +198,7 @@ horiz-adv-x="1000" d=" M125 132.5H187.5V383.125H125V132.5zM187.5 445.625V476.25L218.75 507.5H495.625L522.5 491.875L549.375 445H812.5H906.25L937.5 413.75V-86.25L906.25 -117.5H218.75L187.5 -86.25V70H93.75L62.5 101.25V663.75L93.75 695H370L397.5 679.375L424.375 632.5H781.25L812.5 601.25V445L750 446.875V570H406.25L379.375 585.625L352.5 632.5H125V445.625H187.5zM504.375 398.125L477.5 445H250V257.5H446.875L472.5 303.75L500 320H875V382.5H531.25L504.375 398.125zM465.625 195H250V-55H875V257.5H518.75L493.125 211.25L465.625 195z" /> + horiz-adv-x="1000" d=" M481.87625 632.5H906.25L938.125 601.25V257.5V-23.75L906.875 -55H94.375625L63.125625 -23.75V413.75V663.75L94.375625 695H406.875625L428.749375 685.62375L481.87625 632.5zM874.375 7.5V101.875L875 351.875625V445.625625H481.250625L427.501875 391.87625L405.624375 382.5H125V351.875625V101.875V7.5H874.375zM468.124375 507.5H874.375L875 569.374375H468.75L446.250625 578.750625L393.12375 631.874375H125V444.374375H392.501875L446.250625 498.12375L468.124375 507.5zM611.253125 253.128125L526.250625 338.753125L572.5 381.874375L711.25 243.1262500000001V198.75375L568.128125 60.625L524.3775 105.61875L611.875 190H526.250625C494.281875 188.5625 464.085 174.9187499999999 441.876875 151.88125C431.1175 141.175 422.6368750000001 128.4 416.94375 114.3249999999999C411.2512500000001 100.25625 408.4625000000001 85.1750000000001 408.75 70H346.25C345.860625 93.1687499999999 350.1443750000001 116.175 358.8425000000001 137.65C367.5406250000001 159.125 380.47375 178.6312499999999 396.875 195C429.649375 229.5450000000001 474.339375 250.32875 521.875 253.128125H611.253125z" /> @@ -225,7 +225,7 @@ horiz-adv-x="1000" d=" M568.75 545L537.5 695H462.5L431.25 545L387.5 526.25L262.5 607.5L206.25 557.5L287.5 432.5L275 388.75L125 357.5V282.5L275 251.25L293.75 201.25L212.5 76.25L262.5 26.25L387.5 107.5L437.5 88.75L462.5 -55H537.5L568.75 95L618.75 113.75L743.75 32.5L793.75 82.5L712.5 207.5L731.25 257.5L875 282.5V357.5L725 388.75L706.25 438.75L787.5 563.75L737.5 613.75L612.5 532.5L568.75 545zM587.5 757.5L618.75 607.5L750 688.75L875 563.75L787.5 432.5L937.5 407.5V232.5L787.5 201.25L875 70L750 -55L618.75 32.5L587.5 -117.5H412.5L381.25 32.5L250 -48.75L125 76.25L212.5 207.5L62.5 232.5V407.5L212.5 438.75L131.25 570L256.25 695L387.5 607.5L412.5 757.5H587.5zM625 320C625 251.25 568.75 195 500 195C431.25 195 375 251.25 375 320C375 388.75 431.25 445 500 445C568.75 445 625 388.75 625 320zM500 257.5C537.5 257.5 562.5 282.5 562.5 320C562.5 357.5 537.5 382.5 500 382.5C462.5 382.5 437.5 357.5 437.5 320C437.5 282.5 462.5 257.5 500 257.5z" /> + horiz-adv-x="1000" d=" M843.75 570H743.75C750 595 750 620 750 645C743.75 663.75 737.5 682.5 725 701.25C712.5 720 700 732.5 681.25 738.75C662.5 745 643.75 757.5 625 757.5C606.25 757.5 587.5 757.5 568.75 745C525 732.5 493.75 701.25 468.75 663.75C443.75 701.25 412.5 732.5 368.75 745C350 751.25 331.25 757.5 312.5 757.5C293.75 757.5 275 751.25 256.25 738.75C237.5 732.5 225 720 212.5 701.25C200 688.75 193.75 663.75 187.5 645C187.5 620 187.5 595 193.75 570H93.75L62.5 538.75V-23.75L93.75 -55H843.75L875 -23.75V538.75L843.75 570zM437.5 7.5H125V507.5H437.5V7.5zM437.5 570H250C250 576.25 250 576.25 250 582.5C243.75 601.25 243.75 613.75 243.75 632.5C250 645 250 657.5 262.5 663.75C268.75 676.25 281.25 682.5 293.75 688.75C300 695 312.5 695 325 695C337.5 695 350 695 362.5 688.75C381.25 682.5 400 670 412.5 651.25C425 632.5 437.5 613.75 437.5 588.75V570zM500 588.75C500 613.75 512.5 632.5 525 651.25C537.5 670 556.25 682.5 575 688.75C587.5 695 600 695 612.5 695C625 695 637.5 695 650 688.75C662.5 682.5 668.75 676.25 681.25 663.75C687.5 657.5 687.5 645 693.75 632.5C693.75 613.75 693.75 601.25 687.5 582.5C687.5 576.25 687.5 576.25 681.25 570H500V588.75zM812.5 7.5H500V507.5H812.5V7.5z" /> @@ -237,16 +237,16 @@ horiz-adv-x="1000" d=" M875.28125 477.43C875.425 506.336875 867.51875 534.713125 852.4562500000001 559.3812499999999C837.3812499999999 584.049375 815.74375 604.035625 789.9562500000001 617.10125C764.1687499999999 630.166875 735.2562499999999 635.795625 706.45625 633.358125C677.64375 630.9200000000001 650.09375 620.51125 626.86875 603.2975C603.645625 586.084375 585.671875 562.7462499999999 574.96375 535.89625C564.255625 509.045625 561.2287500000001 479.744375 566.2318750000001 451.273125C571.235 422.8025 584.060625 396.28375 603.2800000000001 374.693125C622.5 353.09875 647.3562499999999 337.28375 675.0500000000001 329.016875C664.825 308.2325 649 290.706875 629.3625000000001 278.420625C609.724375 266.13125 587.05 259.5625 563.8818749999999 259.450625H439.3225C393.21 259.2887500000001 348.80875 241.9706250000001 314.7625 210.8718749999999V511.74625C352.5675 519.463125 386.160625 540.9425 409.029375 572.020625C431.8981250000001 603.098125 442.41125 641.56 438.5331250000001 679.9493749999999C434.655625 718.33875 416.66125 753.920625 388.039375 779.797C359.4175 805.673125 322.2075 820 283.6225 820C245.0375 820 207.8275 805.673125 179.205 779.797C150.583125 753.920625 132.589375 718.33875 128.71125 679.9493749999999C124.8325 641.56 135.3475 603.098125 158.215625 572.020625C181.08375 540.9425 214.6775 519.463125 252.4825 511.74625V131.8375C214.748125 124.625 180.95875 103.84375 157.488125 73.43125C134.0175 43.0124999999999 122.4875 5.0625 125.073125 -33.2687500000001C127.65875 -71.5999999999999 144.18125 -107.6624999999999 171.524375 -134.6437500000001C198.8675 -161.6312499999999 235.140625 -177.68125 273.5025 -179.7687499999999C311.8643750000001 -181.8499999999999 349.6625 -169.8249999999999 379.7675 -145.9562499999999C409.873125 -122.09375 430.2043750000001 -88.03125 436.9262500000001 -50.20625C443.64875 -12.3812499999999 436.299375 26.59375 416.260625 59.375C396.223125 92.15625 364.883125 116.46875 328.1525000000001 127.7312500000001C338.3975000000001 148.49375 354.226875 165.9875 373.8643750000001 178.25625C393.501875 190.525 416.16875 197.070625 439.3225 197.170625H563.8818749999999C602.744375 197.349375 640.58125 209.643125 672.1312499999999 232.338125C703.6750000000001 255.0325000000001 727.35625 286.99875 739.8875 323.7856250000001C777.2125 328.693125 811.5 346.96625 836.4 375.21625C861.2937499999999 403.46625 875.1062499999999 439.7775 875.28125 477.43zM190.2025 664.27C190.2025 682.746875 195.68125 700.808125 205.94625 716.17125C216.211875 731.53375 230.801875 743.5074999999999 247.871875 750.57875C264.9425000000001 757.649 283.72625 759.499125 301.8475 755.894375C319.969375 752.29 336.615 743.3925 349.68 730.3275C362.745625 717.2625 371.6425 700.6168749999999 375.2475 682.495C378.851875 664.3731250000001 377.001875 645.589375 369.93125 628.5193750000001C362.86 611.449375 350.88625 596.85875 335.52375 586.59375C320.160625 576.32875 302.099375 570.85 283.6225 570.85C258.8456250000001 570.85 235.084375 580.6925 217.564375 598.211875C200.045 615.73125 190.2025 639.493125 190.2025 664.27zM377.0425 -20.80625C377.0425 -39.2875 371.563125 -57.34375 361.2981250000001 -72.7062500000001C351.033125 -88.06875 336.443125 -100.04375 319.3725 -107.11875C302.3025 -114.1875 283.51875 -116.0375 265.3975000000001 -112.4375C247.275625 -108.8312499999999 230.63 -99.93125 217.564375 -86.86875C204.499375 -73.8000000000001 195.6025 -57.15625 191.9975 -39.0375C188.393125 -20.9124999999999 190.243125 -2.13125 197.31375 14.9375C204.384375 32.0125 216.35875 46.6 231.72125 56.86875C247.08375 67.13125 265.145625 72.6125000000001 283.6225 72.6125000000001C308.39875 72.6125000000001 332.160625 62.76875 349.68 45.25C367.2000000000001 27.725 377.0425 3.96875 377.0425 -20.80625zM719.58125 384.01C701.10625 384.01 683.0437499999999 389.490625 667.68125 399.754375C652.3187499999999 410.0193750000001 640.34375 424.61 633.275 441.68C626.20625 458.75 624.351875 477.53375 627.95625 495.655C631.5625 513.776875 640.45625 530.423125 653.525 543.488125C666.5875 556.5531249999999 683.2375000000001 565.45 701.35625 569.0550000000001C719.475 572.659375 738.2625 570.809375 755.33125 563.73875C772.40625 556.668125 786.9937500000001 544.694375 797.25625 529.33125C807.51875 513.96875 813 495.906875 813 477.43C813 452.65375 803.15625 428.8918750000001 785.6375 411.3725C768.11875 393.854375 744.35625 384.01 719.58125 384.01z" /> + horiz-adv-x="1000" d=" M686.175 507.5C652.6875 541.7506249999999 608.665625 563.76125 561.1725 570V820H498.6725V570C447.8575 562.346875 401.35875 537.05125 367.330625 498.543125C333.3025 460.035 313.916875 410.776875 312.5743750000001 359.405625C311.2325 308.035 328.02 257.8306250000001 359.99125 217.598125C391.9625 177.36875 437.076875 149.6687499999999 487.423125 139.375H498.6725V-110.625H561.1725V139.375C579.265 141.65625 596.941875 146.49375 613.6724999999999 153.75C653.7937499999999 170.0625 688.0125 198.18 711.8 234.375C735.75 270.28375 748.575 312.4575 748.675 355.6218750000001C748.61875 412.495625 726.16875 467.06375 686.175 507.5V507.5zM642.4250000000001 242.500625C613.02375 213.2731250000001 573.2525 196.87125 531.7975 196.8768750000001C500.914375 196.8562500000001 470.7175 205.983125 445.02125 223.114375C419.3243750000001 240.245 399.281875 264.60625 387.423125 293.1218750000001C378.4468750000001 314.4525 374.340625 337.51625 375.404375 360.634375C376.468125 383.7518750000001 382.6756250000001 406.341875 393.5718750000001 426.758125C404.46875 447.174375 419.7806250000001 464.91 438.395 478.660625C457.00875 492.411875 478.45625 501.833125 501.173125 506.24875C511.360625 507.17 521.61 507.17 531.7975 506.24875C553.2325000000001 506.709375 574.525625 502.6675 594.2975 494.3775C622.813125 482.518125 647.175 462.471875 664.30625 436.775625C681.4375 411.0793750000001 690.5687499999999 380.881875 690.55 349.9987500000001C689.81875 329.375 685.025 309.0975 676.44375 290.3293750000001C667.8625000000001 271.5606250000001 655.66875 254.67 640.55 240.62375L642.4250000000001 242.500625z" /> + horiz-adv-x="1000" d=" M461.808125 8.0875L382.431875 87.46875L424.308125 131.2125L557.433125 -1.9125V-45.65625L424.308125 -178.78125L379.933125 -134.4125L459.3075 -55.0375H343.6825C323.14 -55.11875 302.78625 -51.13125 283.791875 -43.3062500000001C264.796875 -35.4875 247.538125 -23.98125 233.0125 -9.45625C218.486875 5.06875 206.981875 22.33125 199.15875 41.325C191.335625 60.31875 187.349375 80.675 187.4325 101.2125V448.0925C157.4875 454.468125 129.965 469.200625 108.058125 490.588125C86.3 512.5725 71.495625 540.4762499999999 65.49375 570.819375C59.4916875 601.161875 62.5575 632.6 74.3075 661.211875C86.166875 689.7275 106.21125 714.08875 131.9075 731.219375C157.60375 748.350625 187.799375 757.485 218.6825 757.464375C240.1175 757.925375 261.41 753.883125 281.1825 745.593125C300.123125 737.80125 317.3325000000001 726.330625 331.8150000000001 711.84875C346.296875 697.36625 357.765625 680.153125 365.5575 661.211875C373.848125 641.44 377.89375 620.146875 377.433125 598.711875C377.45375 567.82875 368.3225 537.631875 351.1912500000001 511.935625C334.060625 486.239375 309.6975 466.2 281.1825 454.34125C271.9106250000001 450.485 262.280625 447.553125 252.433125 445.59V101.8375C252.433125 76.9749999999999 262.309375 53.13125 279.89125 35.5500000000001C297.4725 17.96875 321.31875 8.0875 346.183125 8.0875H461.808125zM169.30875 523.7149999999999C184.685625 513.515 202.73 508.08 221.183125 508.09C238.14125 508.08375 254.78375 512.67625 269.3375000000001 521.380625C283.8918750000001 530.085 295.813125 542.576875 303.83 557.52125C311.8475000000001 572.465 315.660625 589.3025 314.864375 606.2425000000001C314.0675 623.1825 308.691875 639.586875 299.308125 653.7125C288.8243750000001 669.24375 274.061875 681.405625 256.808125 688.71625C239.778125 695.653125 221.074375 697.39 203.0575 693.713125C184.808125 690.184375 168.0325 681.275 154.889375 668.131875C141.745625 654.98875 132.836875 638.2168750000001 129.3075 619.9675C125.63125 601.95 127.37 583.24875 134.306875 566.21875C141.6175 548.9649999999999 153.776875 534.19875 169.30875 523.7149999999999zM814.93125 129.9625C844.975 123.8499999999999 872.575 109.08125 894.3125 87.4625000000001C923.5375 58.0625 939.9375 18.29375 939.93125 -23.1625C939.95625 -54.04375 930.825 -84.24375 913.69375 -109.9375C896.5625 -135.6375 872.1999999999999 -155.6812500000001 843.6875 -167.54375C815.1 -179.46875 783.6125000000001 -182.6312500000001 753.225 -176.625C722.8375 -170.625 694.91875 -155.73125 673.01875 -133.825C651.11875 -111.9249999999999 636.21875 -84.00625 630.21875 -53.61875C624.2162500000001 -23.2312499999999 627.38125 8.25 639.30625 36.8375C651.3625 65.1999999999999 671.3249999999999 89.5 696.80625 106.8375C713.60625 118.2125 732.525 126.0749999999999 752.43125 129.9625V476.8375C752.43125 501.70125 742.55625 525.5475 724.975 543.129375C707.3937500000001 560.7106249999999 683.55 570.5875 658.68125 570.5875H543.06L622.4325 491.21125L578.0600000000001 446.83875L444.933125 579.96375V623.71125L578.0600000000001 756.83625L622.4325 712.46375L543.06 633.0875H658.68125C679.225 633.1700000000001 699.5812500000001 629.184375 718.5749999999999 621.3612499999999C737.56875 613.538125 754.8249999999999 602.035 769.35 587.509375C783.875 572.98375 795.3875 555.7225000000001 803.20625 536.728125C811.03125 517.73375 815.0187500000001 497.379375 814.93125 476.8375V129.9625zM792.9937500000001 -116.4312500000001C814.5250000000001 -114.2687499999999 834.6375 -104.725 849.93125 -89.41875C865.24375 -74.125 874.7875 -54 876.95 -32.46875C879.11875 -10.9375 873.76875 10.6750000000001 861.80625 28.7125C851.325 44.24375 836.5625 56.4000000000001 819.30625 63.7125C802.28125 70.6500000000001 783.575 72.3874999999999 765.5625 68.7125C747.3125 65.18125 730.5374999999999 56.275 717.3937500000001 43.13125C704.25 29.9875 695.3375000000001 13.2125 691.8125 -5.0375C688.13125 -23.05 689.86875 -41.75625 696.80625 -58.78125C704.11875 -76.0375 716.275 -90.8062500000001 731.80625 -101.2875C749.84375 -113.2437500000001 771.4625000000001 -118.59375 792.9937500000001 -116.4312500000001z" /> + horiz-adv-x="1000" d=" M829.55625 336.840625C800.15625 366.068125 760.38125 382.4700000000001 718.9250000000001 382.464375C688.0437499999999 382.485 657.85 373.350625 632.15 356.219375C606.454375 339.08875 586.411875 314.7275 574.5525 286.2118750000001C570.90625 277.3493749999999 568.183125 268.1375 566.4275 258.7156250000001C502.53125 265.7275000000001 442.93875 294.318125 397.4856250000001 339.770625C352.033125 385.223125 323.43875 444.8193750000001 316.4275 508.715625C325.84875 510.47125 335.0643750000001 513.194375 343.9275 516.84125C372.443125 528.7 396.8037500000001 548.739375 413.935 574.435625C431.065625 600.131875 440.196875 630.32875 440.17625 661.211875C440.636875 682.646875 436.59125 703.94 428.3012500000001 723.711875C420.50875 742.653125 409.0425 759.866125 394.56 774.3484375C380.0775 788.83075 362.8687500000001 800.30125 343.9275 808.09325C324.1550000000001 816.38343125 302.8625 820.4254025 281.4275 819.9645975C250.544375 819.9851908125 220.346875 810.850375 194.650625 793.7195C168.954375 776.5885625 148.911875 752.2275 137.0525 723.711875C125.3025 695.1 122.23875 663.661875 128.240625 633.319375C134.2425 602.97625 149.043125 575.0725 170.80125 553.088125C192.708125 531.700625 220.2325 516.968125 250.1775 510.5925V191.8375000000001C230.268125 187.9500000000001 211.346875 180.0875 194.551875 168.7125C169.065625 151.3812499999999 149.10625 127.08125 137.0525 98.7125C125.123125 70.125 121.96 38.6437500000001 127.9625 8.25625C133.964375 -22.13125 148.86125 -50.05 170.763125 -71.95C192.665 -93.85 220.58375 -108.75 250.97125 -114.75C281.3581250000001 -120.75 312.844375 -117.59375 341.42875 -105.6625C369.944375 -93.8062500000001 394.305625 -73.75625 411.4362500000001 -48.0625C428.5668750000001 -22.36875 437.698125 7.83125 437.6775 38.7125C438.138125 60.15 434.0925 81.44375 425.8025 101.2125C418.010625 120.15625 406.54375 137.3625 392.06125 151.84375C377.579375 166.3250000000001 360.37 177.7937500000001 341.42875 185.5875C332.156875 189.4437499999999 322.525625 192.375 312.6775 194.3375V344.340625C344.968125 298.703125 387.865625 261.598125 437.6775 236.2168750000001C478.380625 215.3768749999999 522.745 202.6425000000001 568.3043749999999 198.718125C572.82375 176.05625 582.311875 154.675 596.086875 136.125C609.86125 117.5750000000001 627.58125 102.3125 647.9625 91.43125C668.34375 80.5562500000001 690.8874999999999 74.33125 713.96875 73.2125C737.05 72.09375 760.0875 76.1062499999999 781.4250000000001 84.9625C809.9437499999999 96.8249999999999 834.30625 116.86875 851.4375 142.5687499999999C868.56875 168.2625000000001 877.69375 198.459375 877.675 229.3425000000001C876.20625 270.06625 858.95 308.618125 829.55625 336.840625V336.840625zM317.053125 125.5875C334.3062500000001 118.28125 349.06875 106.11875 359.5525 90.5875C371.56625 72.5562500000001 376.96125 50.9250000000001 374.8225 29.3625C372.684375 7.8 363.14375 -12.3625 347.8225 -27.6812500000001C332.500625 -43.00625 312.346875 -52.5437499999999 290.7850000000001 -54.6875C269.223125 -56.8249999999999 247.58375 -51.425 229.55125 -39.4125C214.02 -28.9250000000001 201.8625 -14.1625 194.551875 3.09375C187.615 20.125 185.8775 38.825 189.554375 56.84375C193.083125 75.09375 201.9925 91.8625 215.135625 105.00625C228.27875 118.15 245.054375 127.05625 263.30375 130.5875C281.32125 134.2625000000001 300.023125 132.5250000000001 317.053125 125.5875V125.5875zM281.4275 569.964375C262.975 569.954375 244.92875 575.389375 229.55125 585.589375C214.02 596.073125 201.8625 610.839375 194.551875 628.093125C187.615 645.1231250000001 185.8775 663.8243749999999 189.554375 681.8418750000001C193.083125 700.09125 201.9925 716.863125 215.135625 730.00625C228.27875 743.149375 245.054375 752.05875 263.30375 755.5875C281.32125 759.2644375 300.023125 757.5274375 317.053125 750.590625C334.3062500000001 743.28 349.06875 731.118125 359.5525 715.586875C368.9362500000001 701.4612500000001 374.3125 685.056875 375.10875 668.1168749999999C375.905 651.176875 372.09375 634.339375 364.0768750000001 619.395625C356.059375 604.45125 344.13875 591.959375 329.584375 583.255C315.03 574.550625 298.38625 569.958125 281.4275 569.964375V569.964375zM785.175 159.9625C769.8812499999999 144.65625 749.76875 135.1125000000001 728.2375 132.9437499999999C706.70625 130.78125 685.0875 136.13125 667.0500000000001 148.0875C651.5187500000001 158.5749999999999 639.3625 173.3375 632.05 190.59375C625.1125 207.623125 623.3775 226.3243749999999 627.05625 244.341875C630.58125 262.59125 639.49375 279.363125 652.6374999999999 292.50625C665.78125 305.6493750000001 682.55625 314.5587500000001 700.80625 318.0875000000001C718.8187499999999 321.764375 737.525 320.0275000000001 754.5500000000001 313.090625C771.80625 305.78 786.56875 293.618125 797.0500000000001 278.086875C809.0125 260.051875 814.3625 238.436875 812.19375 216.906875C810.03125 195.3762499999999 800.4875000000001 175.25625 785.175 159.9625V159.9625z" /> + horiz-adv-x="1000" d=" M382.431875 149.96875L461.808125 70.5875H346.183125C321.31875 70.5875 297.4725 80.46875 279.89125 98.0500000000001C262.309375 115.63125 252.433125 139.4749999999999 252.433125 164.3375V508.09C262.280625 510.053125 271.9106250000001 512.985 281.1825 516.84125C309.6975 528.7 334.060625 548.739375 351.1912500000001 574.435625C368.3225 600.131875 377.45375 630.32875 377.433125 661.211875C377.89375 682.646875 373.848125 703.94 365.5575 723.711875C357.765625 742.653125 346.296875 759.866125 331.8150000000001 774.3484375C317.3325000000001 788.83075 300.123125 800.30125 281.1825 808.09325C261.41 816.38343125 240.1175 820.4254025 218.6825 819.9645975C187.799375 819.9851908125 157.60375 810.850375 131.9075 793.7195C106.21125 776.5885625 86.166875 752.2275 74.3075 723.711875C62.5575 695.1 59.4916875 663.661875 65.49375 633.319375C71.495625 602.97625 86.3 575.0725 108.058125 553.088125C129.965 531.700625 157.4875 516.968125 187.4325 510.5925V163.7125C187.349375 143.175 191.335625 122.81875 199.15875 103.8250000000001C206.981875 84.83125 218.486875 67.56875 233.0125 53.04375C247.538125 38.51875 264.796875 27.0125 283.791875 19.1937499999999C302.78625 11.36875 323.14 7.38125 343.6825 7.4625H459.3075L379.933125 -71.9125L424.308125 -116.28125L557.433125 16.84375V60.5875L424.308125 193.7125L382.431875 149.96875zM221.183125 570.59C202.73 570.5799999999999 184.685625 576.015 169.30875 586.215C153.776875 596.69875 141.6175 611.465 134.306875 628.71875C127.37 645.74875 125.63125 664.45 129.3075 682.4675C132.836875 700.716875 141.745625 717.48875 154.889375 730.631875C168.0325 743.775 184.808125 752.684375 203.0575 756.213125C221.074375 759.8900625 239.778125 758.1530625 256.808125 751.21625C274.061875 743.905625 288.8243750000001 731.74375 299.308125 716.2125C308.691875 702.086875 314.0675 685.6825 314.864375 668.7425000000001C315.660625 651.8025 311.8475000000001 634.965 303.83 620.02125C295.813125 605.076875 283.8918750000001 592.585 269.3375000000001 583.880625C254.78375 575.17625 238.14125 570.58375 221.183125 570.59V570.59z M894.3125 149.9625000000001C872.575 171.58125 844.975 186.3499999999999 814.93125 192.4625V539.3375000000001C815.0187500000001 559.879375 811.03125 580.23375 803.20625 599.228125C795.3875 618.2225 783.875 635.48375 769.35 650.009375C754.8249999999999 664.535 737.56875 676.038125 718.5749999999999 683.8612499999999C699.5812500000001 691.684375 679.225 695.67 658.68125 695.5875H543.06L622.4325 774.9636875L578.0600000000001 819.33624375L444.933125 686.21125V642.46375L578.0600000000001 509.33875L622.4325 553.7112500000001L543.06 633.0875H658.68125C683.55 633.0875 707.3937500000001 623.210625 724.975 605.629375C742.55625 588.0475 752.43125 564.20125 752.43125 539.3375000000001V192.4625C732.525 188.5749999999999 713.60625 180.7125 696.80625 169.3375C671.3249999999999 152 651.3625 127.6999999999999 639.30625 99.3375C627.38125 70.75 624.2162500000001 39.2687500000001 630.21875 8.88125C636.21875 -21.50625 651.11875 -49.425 673.01875 -71.325C694.91875 -93.23125 722.8375 -108.125 753.225 -114.125C783.6125000000001 -120.13125 815.1 -116.96875 843.6875 -105.04375C872.1999999999999 -93.1812500000001 896.5625 -73.1375 913.69375 -47.4375C930.825 -21.74375 939.95625 8.45625 939.93125 39.3375C939.9375 80.7937500000001 923.5375 120.5625 894.3125 149.9625000000001V149.9625000000001zM849.93125 -26.91875C834.6375 -42.225 814.5250000000001 -51.76875 792.9937500000001 -53.9312500000001C771.4625000000001 -56.09375 749.84375 -50.7437500000001 731.80625 -38.7875C716.275 -28.3062500000001 704.11875 -13.5375 696.80625 3.71875C689.86875 20.74375 688.13125 39.45 691.8125 57.4625C695.3375000000001 75.7125 704.25 92.4875 717.3937500000001 105.63125C730.5374999999999 118.775 747.3125 127.68125 765.5625 131.2125C783.575 134.8874999999999 802.28125 133.1500000000001 819.30625 126.2125C836.5625 118.9000000000001 851.325 106.74375 861.80625 91.2125C873.76875 73.1750000000001 879.11875 51.5625 876.95 30.03125C874.7875 8.5 865.24375 -11.625 849.93125 -26.91875V-26.91875z" /> @@ -261,7 +261,7 @@ horiz-adv-x="1000" d=" M93.75 -55H937.5V7.5H125V820H62.5V-23.75L93.75 -55zM187.5 101.25V601.25L218.75 632.5H343.75L375 601.25V101.25L343.75 70H218.75L187.5 101.25zM312.5 132.5V570H250V132.5H312.5zM687.5 726.25V101.25L718.75 70H843.75L875 101.25V726.25L843.75 757.5H718.75L687.5 726.25zM812.5 695V132.5H750V695H812.5zM437.5 101.25V476.25L468.75 507.5H593.75L625 476.25V101.25L593.75 70H468.75L437.5 101.25zM562.5 132.5V445H500V132.5H562.5z" /> + horiz-adv-x="1000" d=" M930 521.24625C924.95625 540.984375 917.175 559.9200000000001 906.875 577.498125C896.9875000000001 595.78375 884.3312500000001 612.439375 869.375 626.875625C847.6875 648.4918749999999 822 665.681875 793.75 677.4962499999999C736.8875 700.834375 673.1125 700.834375 616.249375 677.4962499999999C589.5475 666.19375 565.016875 650.3325 543.75125 630.6212499999999L540.6268749999999 626.875625L500.000625 586.24875L459.374375 626.875625L456.25 630.6212499999999C434.984375 650.3325 410.45375 666.19375 383.75125 677.4962499999999C326.88625 700.834375 263.115 700.834375 206.25 677.4962499999999C177.99875 665.681875 152.31625 648.4918749999999 130.6275 626.875625C115.656875 612.294375 102.82125 595.669375 92.50125 577.498125C82.66125 559.770625 75.100625 540.869375 70 521.24625C64.70125 500.845 62.1788125 479.821875 62.500625 458.74625C62.533125 438.9262500000001 65.0525 419.1925 70 400C75.24 380.6225 82.795 361.945 92.50125 344.374375C102.98375 326.31125 115.803125 309.7075 130.6275 294.996875L500.000625 -74.38125L869.375 294.996875C884.19375 309.556875 896.8249999999999 326.190625 906.875 344.374375C917.05625 361.78875 924.8375 380.500625 930 400C934.95 419.1925 937.46875 438.9262500000001 937.5 458.74625C937.825 479.821875 935.3 500.845 930 521.24625V521.24625zM867.5 419.3712500000001C863.8249999999999 405.395 858.3625000000001 391.949375 851.25 379.37V379.37C843.825 366.411875 834.8 354.445625 824.375 343.74875L498.749375 18.75L173.126875 343.74875C162.515625 354.435 153.2775 366.39875 145.626875 379.37C138.426875 392.2025 132.761875 405.8400000000001 128.750625 419.996875C125.548125 434.151875 123.8725 448.613125 123.75125 463.125625C123.83625 478.053125 125.51125 492.92625 128.750625 507.498125C132.644375 521.6956250000001 138.315 535.346875 145.626875 548.125C153.123125 561.2 162.37625 573.183125 173.126875 583.7462499999999C189.183125 599.5875 208.0425 612.301875 228.74875 621.245C270.475 637.936875 317.024375 637.936875 358.75 621.245C379.323125 612.676875 398.01125 600.15 413.750625 584.371875L498.749375 498.7475L583.7525 584.371875C599.49125 600.15 618.175625 612.676875 638.75 621.245C680.475 637.936875 727.025 637.936875 768.75 621.245C789.45625 612.301875 808.31875 599.5875 824.375 583.7462499999999C835.26875 573.46 844.35 561.42125 851.25 548.125V548.125C858.3625000000001 535.545 863.8249999999999 522.1 867.5 508.12375V508.12375C871.2562499999999 493.84375 873.14375 479.141875 873.125 464.376875C873.98125 449.275625 872.71875 434.1225 869.375 419.3712500000001H867.5z" /> @@ -279,13 +279,13 @@ horiz-adv-x="1000" d=" M93.75 -55H906.25L937.5 -23.75V257.5L764.375 736.25L735 757.5H266.875L237.5 736.875L62.5 273.75V-23.75L93.75 -55zM875 7.5H125V193.75H284.375625L330.625625 115.625L357.500625 100.625H643.125L670.625 116.875L713.75 193.75H875V7.5zM873.625 256.24875H695L668.125 239.99875L625 163.125H375.625625L328.750625 241.24875L301.875625 256.24875H125V257.5L288.75 695H712.5L873.625 256.24875z" /> + horiz-adv-x="1000" d=" M535.52375 755.545C634.96875 745.600625 727.7249999999999 700.92625 797.5 629.3775C873.51875 552.153125 918.46875 449.6550000000001 923.76875 341.42125C929.06875 233.188125 894.3625 126.7937499999999 826.25625 42.5125C763.6125000000001 -35.35625 675.70625 -88.85 577.7681249999999 -108.7125000000001C479.829375 -128.5749999999999 378.0275000000001 -113.55625 290.0025 -66.25C201.788125 -17.9124999999999 132.923125 59.3375 95.0025 152.5C56.912375 246.1462499999999 51.8418125 349.973125 80.626875 446.8825C109.35 543.4143750000001 170.498125 627.075 253.75125 683.7525C336.313125 740.0675 436.080625 765.489375 535.52375 755.545zM565.0037500000001 -47.49375C648.93125 -30.46875 724.2937499999999 15.275 778.125 81.875C836.4125 154.31875 866.06875 245.649375 861.4625 338.5143750000001C856.8562499999999 431.38 818.3000000000001 519.316875 753.125 585.630625C693.4312500000001 646.5675 614.216875 684.583125 529.3325 693.0375C444.448125 701.49125 359.293125 679.845625 288.7512500000001 631.88C235.653125 595.293125 192.7775 545.755 164.181875 487.959375C135.586875 430.16375 122.220625 366.025625 125.350625 301.61875C128.48 237.2125 148.001875 174.6687499999999 182.065625 119.9187500000001C216.129375 65.1687500000001 263.60625 20.0187500000001 320.0012500000001 -11.25C394.89625 -51.66875 481.6125 -64.49375 565.0037500000001 -47.49375zM531.87625 382.501875L469.37625 382.501875L469.37625 132.5L531.87625 132.5L531.87625 382.501875zM531.87625 507.501875L469.37625 507.501875L469.37625 445.001875L531.87625 445.001875L531.87625 507.501875z" /> + horiz-adv-x="1000" d=" M468.749375 757.5C388.40125 757.5 309.856875 733.673125 243.049375 689.03375C176.241875 644.3943750000001 124.17125 580.946875 93.42375 506.714375C62.675625 432.481875 54.631125 350.8006250000001 70.30625 271.995625C85.981875 193.19375 124.6725 120.8 181.4875 63.9875000000001C238.3025 7.175 310.690625 -31.51875 389.495625 -47.19375C468.3 -62.86875 549.9812499999999 -54.8249999999999 624.21375 -24.075C698.44375 6.66875 761.8937500000001 58.74375 806.53125 125.55C851.1750000000001 192.3562500000001 875 270.90125 875 351.25C875 458.994375 832.2 562.325 756.0124999999999 638.511875C679.825 714.69875 576.49375 757.5 468.749375 757.5zM468.749375 7.5C400.7625000000001 7.5 334.3 27.65625 277.770625 65.43125C221.24125 103.2000000000001 177.1825 156.89375 151.164375 219.70375C125.146875 282.51625 118.339375 351.63125 131.603125 418.3125C144.86625 484.993125 177.606875 546.2406249999999 225.680625 594.315C273.7550000000001 642.389375 335.00625 675.129375 401.6875 688.393125C468.368125 701.656875 537.48375 694.84875 600.295625 668.83125C663.10625 642.81375 716.79375 598.7581250000001 754.56875 542.22875C792.3375 485.699375 812.5 419.2375 812.5 351.25C812.5 260.081875 776.28125 172.65 711.8187499999999 108.1875C647.35 43.71875 559.9174999999999 7.5 468.749375 7.5zM500 570H437.5V257.5H500V570zM437.5 195H500V132.5H437.5V195z" /> @@ -306,7 +306,7 @@ horiz-adv-x="1000" d=" M709.4375 751.81C763.99375 740.70125 814.0625 713.746875 853.375 674.3199999999999C886.6125000000001 640.7975 911.03125 599.564375 924.44375 554.3043749999999C937.85625 509.04375 939.85 461.165 930.25 414.945C916.3375 351.58 881.325 294.8143750000001 830.9499999999999 253.94C780.56875 213.0650000000001 717.8125 190.5 652.9375 189.94375C625.56875 189.9 598.3375 193.88125 572.125 201.7574999999999L522.125 143.1937499999999L498.4375 132.25625H437.5V38.50625L406.25 7.25625H312.5V-86.49375L281.25 -117.74375H93.75L62.5 -86.49375V57.69375L71.625 79.75625L382.5 390.5700000000001C373.6775 418.9875 369.4575 448.6325 370 478.3825C370.76375 534.05375 387.9325 588.263125 419.3556250000001 634.224375C450.779375 680.185 495.061875 715.85625 546.660625 736.771875C598.2593750000001 757.6875 654.88125 762.918375 709.4375 751.81zM791.83125 301.8368750000001C831.08125 333.6287500000001 858.38125 377.805 869.25 427.1325L869.5 426.8200000000001C877.21875 462.88625 875.80625 500.309375 865.39375 535.6912500000001C854.975 571.073125 835.8875 603.293125 809.8625000000001 629.425625C783.8375 655.5587499999999 751.69375 674.77625 716.35625 685.334375C681.0125 695.8925 643.5999999999999 697.455625 607.5 689.8824999999999C558.79875 679.07375 515.114375 652.281875 483.40125 613.7731249999999C451.688125 575.264375 433.77 527.2525 432.5 477.3825C431.824375 448.950625 436.9056250000001 420.675625 447.4375 394.2575L440.5625 360.3825L125 44.75625V-55.24375H250V38.50625L281.25 69.75625H375V163.50625L406.25 194.75625H484.0625L538.8125 258.2575L573.875 267.0075000000001C599.149375 257.13125 626.05 252.0856250000001 653.1875 252.1325000000001C703.7 252.520625 752.58125 270.0450000000001 791.83125 301.8368750000001zM739.46875 472.5325C746.3375 482.810625 750 494.894375 750 507.255625C750 523.831875 743.4125 539.72875 731.69375 551.45C719.975 563.17125 704.075 569.755625 687.5 569.755625C675.1374999999999 569.755625 663.05625 566.0899999999999 652.775 559.2225000000001C642.5 552.355 634.4875 542.59375 629.75625 531.1737499999999C625.0250000000001 519.753125 623.789375 507.18625 626.1999999999999 495.0625C628.6125000000001 482.93875 634.5625 471.8025 643.30625 463.061875C652.04375 454.320625 663.1812500000001 448.368125 675.30625 445.9568750000001C687.4312500000001 443.545 700 444.783125 711.41875 449.513125C722.8375 454.24375 732.6 462.254375 739.46875 472.5325z" /> + horiz-adv-x="1000" d=" M875 632.5H187.5C170.92375 632.5 155.0275 625.918125 143.306875 614.196875C131.585625 602.47625 125 586.57625 125 570V132.5C125 115.925 131.585625 100.025 143.306875 88.3C155.0275 76.58125 170.92375 70 187.5 70H875C891.575 70 907.475 76.58125 919.19375 88.3C930.9125 100.025 937.5 115.925 937.5 132.5V570C937.5 586.57625 930.9125 602.47625 919.19375 614.196875C907.475 625.918125 891.575 632.5 875 632.5zM875 132.5H187.5V570H875V132.5zM687.5 507.5H625V445H687.5V507.5zM625 382.5H562.5V320H625V382.5zM750 507.5H812.5V445H750V507.5zM812.5 257.5H750V195H812.5V257.5zM375 257.5H687.5V195H375V257.5zM812.5 382.5H687.5V320H812.5V382.5zM500 507.5H562.5V445H500V507.5zM500 382.5H437.5V320H500V382.5zM250 257.5H312.5V195H250V257.5zM250 507.5H312.5V445H250V507.5zM437.5 507.5H375V445H437.5V507.5zM250 382.5H375V320H250V382.5z" /> @@ -330,7 +330,7 @@ horiz-adv-x="1000" d=" M677.04375 652.025625C630.3562499999999 698.714375 567.29375 725.344375 501.27125 726.25H498.77125C432.7487500000001 725.344375 369.685 698.714375 322.995625 652.025625C276.30625 605.3362500000001 249.676875 542.2725 248.77125 476.25C247.931875 429.3575 261.1212500000001 383.283125 286.64625 343.9375L483.361875 -55H516.680625L713.3937500000001 343.9375C738.9187499999999 383.283125 752.1125 429.3575 751.2687500000001 476.25C750.36875 542.2725 723.7375000000001 605.3362500000001 677.04375 652.025625zM495.27125 663.75L500.39625 663.125L505.02125 663.75C554.119375 661.611875 600.5325 640.745625 634.71875 605.441875C668.90625 570.138125 688.275 523.0787499999999 688.83125 473.9375C689.30625 439.0612500000001 679.125 404.8712500000001 659.64375 375.9375L658.39375 373.8125L657.3312500000001 371.625L500.02125 52.5625L342.70875 371.3125L341.64625 373.75L340.39625 375.875C320.9175 404.80875 310.734375 438.99875 311.20875 473.875C311.741875 523.073125 331.1287500000001 570.191875 365.374375 605.51875C399.62 640.8456249999999 446.1131250000001 661.688125 495.27125 663.75zM533.4875 528.2168750000001C523.209375 535.084375 511.125625 538.75 498.764375 538.75C482.188125 538.75 466.29125 532.165 454.57 520.444375C442.84875 508.723125 436.264375 492.82625 436.264375 476.25C436.264375 463.88875 439.929375 451.805 446.7975 441.526875C453.665 431.2487500000001 463.42625 423.2381250000001 474.84625 418.5075000000001C486.266875 413.776875 498.83375 412.539375 510.9575 414.950625C523.0812500000001 417.3625 534.2175 423.315 542.958125 432.055625C551.69875 440.796875 557.651875 451.933125 560.063125 464.056875C562.4749999999999 476.180625 561.2368749999999 488.7475 556.5068749999999 500.1675C551.77625 511.588125 543.7650000000001 521.349375 533.4875 528.2168750000001zM429.318125 580.18375C449.8737500000001 593.91875 474.04125 601.25 498.764375 601.25C531.91625 601.25 563.710625 588.080625 587.1524999999999 564.638125C610.594375 541.19625 623.7643750000001 509.401875 623.7643750000001 476.25C623.7643750000001 451.5275 616.433125 427.36 602.698125 406.8037500000001C588.9625000000001 386.2475 569.4399999999999 370.22625 546.599375 360.765C523.7587500000001 351.304375 498.625625 348.82875 474.3775 353.651875C450.13 358.475 427.8575 370.38 410.375625 387.861875C392.8943750000001 405.3431250000001 380.989375 427.6162500000001 376.16625 451.86375C371.3425000000001 476.11125 373.818125 501.244375 383.279375 524.0856249999999C392.74 546.92625 408.761875 566.44875 429.318125 580.18375z" /> + horiz-adv-x="1000" d=" M812.5 382.5H750V507.5C750 573.8043749999999 723.6625 637.38875 676.775 684.2731249999999C629.8937500000001 731.156875 566.3043749999999 757.5 500 757.5C433.695625 757.5 370.1075 731.156875 323.223125 684.2731249999999C276.33875 637.38875 250 573.8043749999999 250 507.5V382.5H187.5L125 320V-55L187.5 -117.5H812.5L875 -55V320L812.5 382.5zM312.5 507.5C312.5 557.2281250000001 332.253125 604.920625 367.41625 640.08375C402.5793750000001 675.246875 450.271875 695 500 695C549.728125 695 597.4206250000001 675.246875 632.58125 640.08375C667.74375 604.920625 687.5 557.2281250000001 687.5 507.5V382.5H312.5V507.5zM812.5 -55H187.5V320H812.5V-55z" /> @@ -378,16 +378,16 @@ horiz-adv-x="1000" d=" M538.125 632.5L896.875 536.875L937.5 507.5V86.25L914.375 56.25L531.25 -49.375L147.5 56.25L125 86.25V507.5L163.125 536.875L521.25 632.5H538.125zM532.5 570L282.5 507.5L316.875 495L531.25 438.75L718.75 489.375L778.125 507.5L532.5 570zM187.5 110L500 24.375V382.5L187.5 466.25V110zM562.5 382.5V24.375L875 110V468.125L748.75 433.5325V273.124375L686.25 256.874375V416.4075L562.5 382.5z" /> + horiz-adv-x="1000" d=" M908.7875 81.80625L837.5375 276.8143750000001V280.560625L530.6625 587.4375V664.936875C530.555 677.334375 528.2275 689.60875 523.78875 701.184375C518.92625 712.565 511.8287500000001 722.85375 502.918125 731.4425C494.0075 740.03125 483.464375 746.7475 471.9125 751.1875C460.398125 755.779375 448.04875 757.907 435.66125 757.435625C423.433125 757.6960625 411.289375 755.35375 400.035625 750.561875C388.8125 745.943125 378.615 739.146875 370.0331250000001 730.565C361.45125 721.983125 354.655 711.7825 350.036875 700.55875C345.245 689.305625 342.9025 677.165 343.1625 664.936875V461.18625L151.9125 273.68625C134.655625 255.95125 125 232.1781249999999 125 207.433125C125 182.6875 134.655625 158.9250000000001 151.9125 141.1875L378.788125 -85.6875C387.53875 -94.5124999999999 397.945625 -101.5187500000001 409.4125 -106.3125C432.275625 -115.4875000000001 457.8 -115.4875000000001 480.663125 -106.3125C492.13 -101.5187500000001 502.536875 -94.5124999999999 511.2875 -85.6875L819.4125 221.8143749999999L778.1625 84.3125C776.29375 74.81875 776.29375 65.05625 778.1625 55.5625C780.1687499999999 46.05625 784.21875 37.09375 790.0374999999999 29.30625C796.09375 21.75 803.5124999999999 15.3937500000001 811.9125 10.5625C820.68125 6.1875 830.25 3.6375 840.0375 3.0625C850.35 2.4312500000001 860.65625 4.3625 870.0375 8.6875C879.36875 12.90625 887.6875 19.09375 894.4125 26.8125C901.2125 34.69375 905.9375 44.14375 908.1625 54.3125C910.125 63.3562500000001 910.3375 72.6875 908.7875 81.80625V81.80625zM408.786875 664.936875C407.87 669.053125 407.87 673.3175 408.786875 677.43375C410.5456250000001 681.4449999999999 413.09625 685.061875 416.2862500000001 688.061875C419.674375 690.73875 423.473125 692.8475 427.53625 694.31C431.861875 695.239375 436.336875 695.239375 440.6625 694.31C448.248125 693.72375 455.36 690.389375 460.663125 684.93375C465.574375 679.19125 468.238125 671.8668749999999 468.1625 664.31125V585.560625L405.6625 523.0606250000001L408.786875 664.936875zM471.286875 -44.4375C468.64625 -47.6 465.18875 -49.975 461.2887500000001 -51.3125C457.548125 -52.9625 453.503125 -53.81875 449.4131250000001 -53.81875C445.32375 -53.81875 441.27875 -52.9625 437.538125 -51.3125C433.638125 -49.975 430.176875 -47.6 427.53625 -44.4375L200.660625 181.8125C197.81375 184.7312499999999 195.489375 188.1062499999999 193.786875 191.80625C192.15375 195.6637499999999 191.315 199.8125 191.315 203.999375C191.315 208.186875 192.15375 212.328125 193.786875 216.1837499999999C195.489375 219.885625 197.81375 223.27 200.660625 226.185625L472.538125 498.059375V319.935625C467.775625 315.57125 463.97875 310.2543750000001 461.395 304.33375C458.811875 298.4131250000001 457.499375 292.0175 457.53875 285.5575C456.77625 277.3318750000001 458.201875 269.053125 461.67 261.555625C465.138125 254.058125 470.525 247.60875 477.2875 242.86375C484.049375 238.11875 491.945 235.24375 500.175625 234.5325000000001C508.405625 233.8212500000001 516.6787499999999 235.2981249999999 524.154375 238.8125C531.630625 242.326875 538.0443750000001 247.753125 542.7475000000001 254.544375C547.450625 261.3356250000001 550.276875 269.2512500000001 550.9375 277.4856250000001C551.5981250000001 285.7206250000001 550.0693749999999 293.988125 546.50875 301.441875C542.948125 308.89625 537.4825 315.274375 530.6625 319.935625V496.808125L771.2875 254.933125L471.286875 -44.4375z" /> + horiz-adv-x="1000" d=" M826.875 757.5H735.625L220.00125 241.875L209.999375 228.1268750000001L62.5 -29.375L150.623125 -117.5L408.126875 30L421.875 40L937.5 555.62625V646.87375L826.875 757.5zM150.623125 -29.375L244.99875 158.125L335.625 67.5L150.623125 -29.375zM389.999375 99.375L279.373125 209.999375L779.375 709.999375L890 599.373125L389.999375 99.375z" /> + horiz-adv-x="1000" d=" M615.001875 757.5L558.124375 735.000625V601.25L344.375625 446.876875C312.088125 462.23125 276.160625 468.293125 240.62375 464.37875C205.404375 460.41 172.033125 446.54 144.375 424.3775V378.74625L321.87625 206.253125L60.001375 -55H154.99875L369.99875 159.99375L525.623125 8.125H573.1237500000001C595.604375 34.58125 609.7418749999999 67.1 613.746875 101.5875C617.75125 136.06875 611.44625 170.96875 595.626875 201.874375L755 409.378125H892.5L915.625 464.37875L615.001875 757.5zM740 474.373125L713.125 461.250625L533.74875 226.875625L529.99875 195C547.994375 160.61875 553.0962499999999 120.9375 544.37625 83.125L419.3762500000001 208.1225000000001L371.875625 254.371875L224.99875 397.499375C263.8625 405.660625 304.34125 400.6025 340 383.125625L373.1268750000001 386.8718750000001L614.3762499999999 561.249375L627.5 586.87625V658.7525L815 476.25L740 474.373125z" /> @@ -411,16 +411,13 @@ horiz-adv-x="1000" d=" M737.5 257.5L625 632.5H562.5L447.375625 217.5L374.375 526.875H313.75L240.625 257.5H62.5V195.625H264.375L295 218.75L341.25 387.5L411.875 70H476.25L593.125 510L684.375 217.5L714.375 195H937.5V257.5H737.5z" /> + horiz-adv-x="1000" d=" M468.750625 757.5C388.401875 757.5 309.8575 733.673125 243.05 689.03375C176.2425 644.3943750000001 124.174375 580.946875 93.42625 506.714375C62.678125 432.481875 54.6301875 350.8006250000001 70.305625 271.995625C85.980625 193.19375 124.67375 120.8 181.48875 63.9875000000001C238.30375 7.175 310.691875 -31.51875 389.49625 -47.19375C468.30125 -62.86875 549.9825000000001 -54.8249999999999 624.2149999999999 -24.075C698.45 6.66875 761.8937500000001 58.74375 806.53125 125.55C851.1750000000001 192.3562500000001 875 270.90125 875 351.25C875 458.994375 832.2 562.325 756.0124999999999 638.511875C679.825 714.69875 576.495 757.5 468.750625 757.5zM468.750625 7.5C400.763125 7.5 334.3006250000001 27.65625 277.77125 65.43125C221.241875 103.2000000000001 177.183125 156.89375 151.165625 219.70375C125.148125 282.51625 118.34 351.63125 131.60375 418.3125C144.8675 484.993125 177.6075 546.2406249999999 225.681875 594.315C273.75625 642.389375 335.0075 675.129375 401.688125 688.393125C468.369375 701.656875 537.484375 694.84875 600.29625 668.83125C663.10625 642.81375 716.79375 598.7581250000001 754.56875 542.22875C792.3375 485.699375 812.5 419.2375 812.5 351.25C812.5 260.081875 776.2875 172.65 711.8187499999999 108.1875C647.3562499999999 43.71875 559.91875 7.5 468.750625 7.5zM565.624375 533.7537500000001C554.82625 544.9975 541.85625 553.923125 527.5 559.99875C509.4375 567.2306249999999 490.073125 570.638125 470.626875 570.000625C451.7525 570.34375 433.015625 566.72125 415.62625 559.3731250000001C400.71625 553.1075000000001 387.455 543.485625 376.876875 531.25125C366.52625 519.9937500000001 358.46 506.83375 353.12625 492.50125C348.1675 477.9425 345.0175 462.83125 343.75 447.503125H420.6237500000001C421.078125 460.94125 426.6706250000001 473.68875 436.2487500000001 483.125C440.745625 487.805625 446.2025 491.455625 452.24375 493.82875C458.285 496.2025 464.768125 497.24125 471.24875 496.873125C476.83875 497.761875 482.535 497.761875 488.125 496.873125C493.26 494.93125 497.9425 491.951875 501.876875 488.1225C506.2187500000001 484.365 509.63875 479.6625 511.8749999999999 474.374375C514.435625 468.459375 515.713125 462.073125 515.625 455.62875C515.648125 444.834375 513.301875 434.166875 508.750625 424.3787500000001C504.211875 414.198125 498.550625 404.55 491.875 395.62375L470.00125 369.378125C462.50125 361.253125 454.99875 352.50125 448.12375 343.7512500000001C441.4575 335.041875 435.799375 325.61 431.25125 315.6293750000001C426.9156250000001 305.7924999999999 424.78 295.126875 424.999375 284.379375V243.1268750000001H500V273.7512500000001C500.211875 282.9181249999999 502.565625 291.910625 506.874375 300.004375C511.989375 309.015 517.844375 317.580625 524.375625 325.62375L546.875 353.1275C555.06625 362.590625 562.58375 372.6137500000001 569.374375 383.12625C576.556875 393.84625 582.4337499999999 405.3900000000001 586.875625 417.504375C591.43375 430.3475 593.7581250000001 443.87 593.75 457.498125C593.86125 471.696875 591.7543750000001 485.828125 587.50125 499.375625C582.781875 512.28125 575.31625 524.0125 565.624375 533.7537500000001zM425 205.6275000000001H498.125V132.5H425V205.6275000000001z" /> - + horiz-adv-x="1000" d=" M187.400625 471.2525C188.040625 562.236875 224.4025 649.326875 288.650625 713.753125L244.274375 757.5C206.480625 720.0231249999999 176.485 675.435 156.01375 626.3050000000001C135.541875 577.174375 125 524.4775 125 471.2525C125 418.0281250000001 135.541875 365.330625 156.01375 316.200625C176.485 267.07 206.480625 222.4825 244.274375 185.00625L288.650625 228.7525C224.4025 293.178125 188.040625 380.26875 187.400625 471.2525zM253.649375 471.2525C253.488125 434.3950000000001 260.705625 397.88 274.874375 363.8537500000001C289.0431250000001 329.8275 309.876875 298.9756250000001 336.15 273.125L380.52625 317.505C359.984375 337.270625 343.8037500000001 361.114375 333.025625 387.505C321.87125 414.0168750000001 316.1325 442.49 316.149375 471.2525C316.061875 499.820625 321.805 528.103125 333.025625 554.375C343.643125 581.03375 359.83875 605.114375 380.52625 625L336.15 669.380625C309.989375 643.445625 289.229375 612.5825 275.06875 578.575625C260.90875 544.5687499999999 253.6275 508.09 253.649375 471.2525zM731.775 270.63L687.4 315.0025C708.0125 334.9500000000001 724.1999999999999 359.01375 734.9 385.628125C746.05625 412.14 751.79375 440.613125 751.775 469.375625C751.8625 497.94375 746.125 526.23375 734.9 552.505625C724.28125 579.1643750000001 708.0875 603.245 687.4 623.13125L731.775 666.878125C757.8937500000001 640.915 778.61875 610.044375 792.75625 576.0425C806.9 542.040625 814.18125 505.575625 814.18125 468.75C814.18125 431.925 806.9 395.4675000000001 792.75625 361.465625C778.61875 327.46375 757.8937500000001 296.59375 731.775 270.63zM816.775 757.5L772.4 713.1275C804.4875000000001 681.54625 829.975 643.896875 847.36875 602.37125C864.7625 560.8456249999999 873.71875 516.274375 873.71875 471.2525C873.71875 426.23125 864.7625 381.659375 847.36875 340.13375C829.975 298.6081250000001 804.4875000000001 260.95875 772.4 229.3781250000001L816.775 185.00625C854.56875 222.4825 884.56875 267.07 905.04375 316.200625C925.5125 365.330625 936.05 418.0281250000001 936.05 471.2525C936.05 524.4775 925.5125 577.174375 905.04375 626.3050000000001C884.56875 675.435 854.56875 720.0231249999999 816.775 757.5zM624.54625 480.60625C626.6875 459.044375 621.2893750000001 437.413125 609.275625 419.380625C604.0875 414.09875 598.439375 409.281875 592.399375 404.999375L807.4 -78.125L749.9 -103.11875L701.7750000000001 5.00625H355.52625L307.4000000000001 -103.11875L249.900625 -78.125L464.90125 404.999375C451.840625 418.1925 442.9337500000001 434.924375 439.2775 453.125625C435.60125 471.1425 437.3381250000001 489.844375 444.275 506.874375C451.585625 524.128125 463.743125 538.894375 479.275 549.378125C497.306875 561.39125 518.9462500000001 566.79 540.508125 564.651875C562.0706250000001 562.51375 582.224375 552.973125 597.545625 537.65125C612.866875 522.3299999999999 622.4075 502.16875 624.54625 480.60625zM524.9025 501.8775C518.9775 500.48125 513.56375 497.44875 509.2775 493.12625C505.9193749999999 489.15875 503.55875 484.445 502.399375 479.378125C500.530625 473.48125 500.530625 467.148125 502.399375 461.250625C504.861875 455.578125 508.7343749999999 450.630625 513.64875 446.876875C518.9143750000001 443.639375 524.969375 441.9125 531.150625 441.8793750000001C539.38875 442.039375 547.24375 445.384375 553.0699999999999 451.2106250000001C558.89625 457.036875 562.240625 464.891875 562.400625 473.129375C562.368125 479.310625 560.6375 485.36625 557.4 490.63125C553.64625 495.54625 548.69875 499.415 543.02625 501.8775C537.1287500000001 503.74625 530.7993749999999 503.74625 524.9025 501.8775zM539.27625 373.749375H522.4L468.025 252.503125H593.025L539.27625 373.749375zM676.775 65L621.15125 190H441.150625L385.5250000000001 65H676.775z" /> @@ -429,7 +426,7 @@ horiz-adv-x="1000" d=" M394.153125 685.845L142.903125 434.594375V390.400625L394.153125 139.1500000000001L438.346875 183.34375L237.940625 383.75125H355.625C531.4475 383.75125 643.43125 345.945625 712.2062500000001 275.715625C781.0875000000001 205.370625 813.125 95.8937500000001 813.125 -63.125V-85H875.625V-63.125C875.625 102.2312500000001 842.6625 231.81875 756.85625 319.443125C670.9437499999999 407.1818750000001 538.5525 446.25125 355.625 446.25125H242.9475L438.346875 641.650625L394.153125 685.845z" /> + horiz-adv-x="1000" d=" M812.5 195H249.998125V695H499.998125V757.5H233.7475C219.413125 757.34125 205.226875 754.588125 191.87375 749.375C178.3075 743.470625 166.16 734.731875 156.248125 723.7475C146.12 713.150625 138.2525 700.606875 133.12375 686.875C127.9825 674.5899999999999 125.225 661.44 124.998125 648.125V54.375C124.848125 40.0125 127.6125 25.7624999999999 133.12375 12.5C144.080625 -14.2874999999999 165.20625 -35.6375 191.87375 -46.875C205.226875 -52.0875 219.413125 -54.84375 233.7475 -55H249.998125V7.5H233.7475C227.51125 7.48125 221.340625 8.7625 215.62375 11.25625C204.455625 16.1625 195.53375 25.08125 190.6225 36.25C189.674375 42.25 189.674375 48.36875 190.6225 54.375V85.625C189.674375 91.63125 189.674375 97.75 190.6225 103.75C195.53375 114.9187500000001 204.455625 123.8375 215.62375 128.74375C221.340625 131.2375 227.51125 132.51875 233.7475 132.5H812.5V7.5H562.498125V-55H843.75L875 -23.75V257.5H812.5V195zM375 632.5H312.5V570H375V632.5zM312.5 507.5H375V445H312.5V507.5zM312.5 382.5H375V320H312.5V382.5zM330 -117.5H312.5V70H500V-117.5H482.5L406.25 -23.125L330 -117.5zM625 757.5H906.25L937.5 726.25V351.25L906.25 320H750V257.5H687.5V320H625C608.42375 320 592.5275 326.5818750000001 580.806875 338.303125C569.085625 350.02375 562.5 365.92375 562.5 382.5V695C562.5 711.57625 569.085625 727.4762499999999 580.806875 739.196875C592.5275 750.918125 608.42375 757.5 625 757.5zM656.25 382.5H687.5V445H656.25C647.9625 445 640.0124999999999 441.705 634.15 435.845C628.2937499999999 429.984375 625 422.038125 625 413.75C625 405.461875 628.2937499999999 397.515625 634.15 391.6550000000001C640.0124999999999 385.795 647.9625 382.5 656.25 382.5zM750 382.5H875V445H750V382.5zM687.5 507.5H875V695H687.5V507.5z" /> @@ -468,7 +465,7 @@ horiz-adv-x="1000" d=" M218.75 570H62.5V632.5H187.5V757.5H250V601.25L218.75 570zM812.5 632.5V757.5H750V601.25L781.25 570H937.5V632.5H812.5zM750 38.75V-117.5H812.5V7.5H937.5V70H781.25L750 38.75zM62.5 70V7.5H187.5V-117.5H250V38.75L218.75 70H62.5zM750 163.75L718.75 132.5H281.25L250 163.75V476.25L281.25 507.5H718.75L750 476.25V163.75zM625 382.5H375V257.5H625V382.5z" /> + horiz-adv-x="1000" d=" M622.2881249999999 757.5C559.8087499999999 757.5645625 498.645625 739.565625 446.156875 705.674375C393.668125 671.783125 352.0925 623.4456250000001 326.44 566.475C300.7875 509.505 292.1500000000001 446.33 301.5668750000001 384.564375C310.9831250000001 322.7987500000001 338.05375 265.071875 379.516875 218.33375L62.5 -141.10625L106.69375 -180L422.535 178.2687500000001C463.19125 146.425 510.8675 124.7562499999999 561.59375 115.06875C612.319375 105.3875000000001 664.625 107.96875 714.15625 122.59375C763.68125 137.21875 809 163.46875 846.325 199.1575C883.6500000000001 234.844375 911.9125 278.933125 928.75 327.7537500000001C945.5875 376.5743750000001 950.50625 428.71125 943.1125 479.82125C935.71875 530.9312500000001 916.21875 579.534375 886.2375 621.580625C856.25 663.6268749999999 816.65625 697.899375 770.74375 721.541875C724.8312500000001 745.185 673.93125 757.5123125 622.2881249999999 757.5V757.5zM622.2881249999999 168.25C569.84375 168.25 518.5775 183.8000000000001 474.971875 212.9393749999999C431.36625 242.075625 397.3787500000001 283.4881250000001 377.3087500000001 331.94C357.2393750000001 380.3925000000001 351.99 433.7075000000001 362.221875 485.14375C372.453125 536.580625 397.708125 583.828125 434.791875 620.911875C471.875625 657.995625 519.1231250000001 683.250625 570.56 693.481875C621.99625 703.71375 675.3125 698.4606249999999 723.7624999999999 678.39125C772.2125 658.321875 813.625 624.3375 842.7624999999999 580.731875C871.9 537.12625 887.4499999999999 485.85625 887.4499999999999 433.411875C887.53125 398.56875 880.725 364.053125 867.425 331.846875C854.125 299.640625 834.6 270.3775 809.9625 245.739375C785.325 221.1012499999999 756.0625 201.5725 723.8562499999999 188.275C691.65 174.975 657.1312499999999 168.1687500000001 622.2881249999999 168.25V168.25z" /> @@ -486,7 +483,7 @@ horiz-adv-x="1000" d=" M688.75 584.375V486.875L751.25 548.75V663.75L720 695H157.50125L126.25125 663.75V633.375625L125 632.498125V-10.625L147.5 -39.375L460 -146.875L500 -117.5V-54.99375H720L751.25 -23.74375V7.50625V89.38125L688.75 151.88125V7.50625H500V525.6231250000001L479.375 554.3731250000001L252.2618750000001 632.5H438.75125H688.75V584.375zM437.5 -72.5L187.5 11.25V587.4981250000001L437.5 503.748125V-72.5zM845 289.996875H534.374375V352.496875H842.5L742.5 452.496875L786.875 496.246875L941.25 342.4968750000001V298.1218750000001L785.625 143.11875L741.875 186.86875L845 289.996875z" /> + horiz-adv-x="1000" d=" M256.938125 683.769375C328.885 731.8425 413.4700000000001 757.5 499.999375 757.5C616.031875 757.5 727.3125 711.40375 809.3625 629.356875C891.40625 547.309375 937.5 436.0325 937.5 320C937.5 233.470625 911.84375 148.88125 863.76875 76.9375C815.69375 4.9875 747.3625000000001 -51.0812500000001 667.4250000000001 -84.2C587.48 -117.3125 499.5125 -125.975 414.645625 -109.09375C329.77875 -92.2125 251.824375 -50.5437499999999 190.63875 10.64375C129.45375 71.83125 87.788125 149.78125 70.906875 234.65C54.026 319.516875 62.688125 407.484375 95.801875 487.426875C128.915 567.37 184.991875 635.69625 256.938125 683.769375zM291.66 8.2C353.328125 -33.0062499999999 425.83125 -55 499.999375 -55C599.455625 -55 694.8375 -15.4937500000001 765.1625 54.8312500000001C835.4875000000001 125.15625 875 220.5437499999999 875 320C875 394.168125 853.0062499999999 466.6675 811.8 528.335625C770.59375 590.004375 712.025 638.075625 643.5062499999999 666.458125C574.9825 694.84125 499.58375 702.2675 426.84125 687.798125C354.098125 673.328125 287.280625 637.611875 234.83625 585.1675C182.39125 532.7225 146.675 465.90125 132.205625 393.1581250000001C117.73625 320.415625 125.161875 245.013125 153.545 176.4937500000001C181.9275 107.96875 229.99125 49.40625 291.66 8.2zM406.25 382.5C406.25 347.9825 378.2675 320 343.75 320C309.2325 320 281.25 347.9825 281.25 382.5C281.25 417.0175 309.2325 445 343.75 445C378.2675 445 406.25 417.0175 406.25 382.5zM718.75 382.5C718.75 347.9825 690.7687500000001 320 656.25 320C621.7318750000001 320 593.75 347.9825 593.75 382.5C593.75 417.0175 621.7318750000001 445 656.25 445C690.7687500000001 445 718.75 417.0175 718.75 382.5zM500 132.5C466.038125 132.4187499999999 432.689375 141.55625 403.51875 158.9499999999999C374.348125 176.34375 350.44875 201.3306250000001 334.3737500000001 231.2475L279.9987500000001 201.24875C301.883125 160.75 334.5250000000001 127.08125 374.3250000000001 103.9500000000001C414.124375 80.81875 459.536875 69.13125 505.558125 70.1687500000001C551.57875 71.1999999999999 596.423125 84.9187499999999 635.14375 109.8125C673.8625000000001 134.70625 704.95625 169.8125 725 211.250625L668.75 238.1212500000001C653.375 206.4475000000001 629.39375 179.74375 599.551875 161.0625C569.70875 142.3875000000001 535.2075 132.4875000000001 500 132.5z" /> @@ -495,7 +492,7 @@ horiz-adv-x="1000" d=" M599.65875 429.243125L500 757.5L400.34125 429.243125H62.5L335.819375 216.7206250000001L234.35 -117.5L500 89.0625L765.6500000000001 -117.5L664.18125 216.7206250000001L937.5 429.243125H599.65875z" /> + horiz-adv-x="1000" d=" M256.938125 683.769375C328.885 731.8425 413.4700000000001 757.5 499.999375 757.5C616.031875 757.5 727.3125 711.40375 809.3625 629.356875C891.40625 547.309375 937.5 436.0325 937.5 320C937.5 233.470625 911.84375 148.88125 863.76875 76.9375C815.69375 4.9875 747.3625000000001 -51.0812500000001 667.4250000000001 -84.2C587.48 -117.3125 499.5125 -125.975 414.645625 -109.09375C329.77875 -92.2125 251.824375 -50.5437499999999 190.63875 10.64375C129.45375 71.83125 87.788125 149.78125 70.906875 234.65C54.026 319.516875 62.688125 407.484375 95.801875 487.426875C128.915 567.37 184.991875 635.69625 256.938125 683.769375zM291.66 8.2C353.328125 -33.0062499999999 425.83125 -55 499.999375 -55C599.455625 -55 694.8375 -15.4937500000001 765.1625 54.8312500000001C835.4875000000001 125.15625 875 220.5437499999999 875 320C875 394.168125 853.0062499999999 466.6675 811.8 528.335625C770.59375 590.004375 712.025 638.075625 643.5062499999999 666.458125C574.9825 694.84125 499.58375 702.2675 426.84125 687.798125C354.098125 673.328125 287.280625 637.611875 234.83625 585.1675C182.39125 532.7225 146.675 465.90125 132.205625 393.1581250000001C117.73625 320.415625 125.161875 245.013125 153.545 176.4937500000001C181.9275 107.96875 229.99125 49.40625 291.66 8.2zM508.1250000000001 353.125L660.625 507.5L704.375 463.125L551.875 308.75L704.375 153.75L660.625 110L508.1250000000001 264.375L356.25 110L312.5 153.75L465 308.75L312.5 463.125L356.25 507.5L508.1250000000001 353.125z" /> @@ -513,7 +510,7 @@ horiz-adv-x="1000" d=" M62.5 757.5H937.5V-117.5H62.5V757.5zM125 -55H875V695H125V-55zM250.005 463.306875L294.19875 507.500625L515.1700000000001 286.53L470.975625 242.335625L470.9737500000001 242.3375L294.1943750000001 65.55625L250 109.75L426.78 286.531875L250.005 463.306875z" /> + horiz-adv-x="1000" d=" M209.999375 382.5L62.5 7.5H146.251875L178.12375 99.375H319.374375L352.500625 7.5H437.5L290.62625 382.5H209.999375zM200.62625 161.875L249.374375 295.624375L298.12625 161.875H200.62625z M738.75 570H638.75L437.5 7.5H535L581.875 150.625H791.875L840 7.5H937.5L738.75 570zM604.3737500000001 226.25L678.125 450.623125C681.8125 462.21625 684.3249999999999 474.155625 685.625 486.251875C687.3125 474.268125 689.6062499999999 462.37375 692.5 450.623125L770.625 226.25H604.3737500000001z" /> @@ -528,7 +525,7 @@ horiz-adv-x="1000" d=" M333.584375 539.8824999999999L439.93 433.53625L386.4525 380.075L280.115 486.413125L226.023125 487.035625L115.971875 650.55625L169.440625 704.025625L332.961875 593.974375L333.584375 539.8824999999999zM513.393125 253.134375L513.3950000000001 253.1331250000001L566.871875 306.594375L566.870625 306.595625L636.8125 376.518125C668.00625 361.5487500000001 703.075 356.6125 737.19375 362.3893750000001C771.3125 368.166875 802.8 384.3725 827.31875 408.77625C851.73125 433.28625 867.95 464.75875 873.725 498.86C879.50625 532.961875 874.56875 568.015625 859.59375 599.19625L748.23125 487.66125L668.91875 566.9375L780.78125 678.24875C749.59375 693.385625 714.46875 698.435625 680.275 692.6975C646.0875000000001 686.95875 614.53125 670.71625 590.0006249999999 646.2275C565.47 621.73875 549.180625 590.22 543.396875 556.051875C537.6125000000001 521.88375 542.62125 486.7625 557.725625 455.570625L119.981875 17.95625V-21.56875L159.525625 -61.09375H199.07L513.393125 253.134375zM682.33125 -22.74375L507.78875 151.8L654.76875 298.733125C665.38125 296.2050000000001 676.15625 294.419375 687.0125 293.3887500000001L842.7375000000001 137.6624999999999C864.5 115.9000000000001 876.925 86.5750000000001 877.28125 56.1375C877.63125 25.70625 865.875 -3.3375000000001 844.60625 -24.6125C770.29375 -100.0125 682.33125 -22.74375 682.33125 -22.74375z" /> + horiz-adv-x="1000" d=" M750 570H625V632.5C625 649.07625 618.414375 664.97625 606.693125 676.696875C594.9725 688.418125 579.07625 695 562.5 695H375C358.42375 695 342.5275 688.418125 330.806875 676.696875C319.085625 664.97625 312.5 649.07625 312.5 632.5V570H125V507.5H187.5V-55L250 -117.5H687.5L750 -55V507.5H812.5V570H750zM375 632.5H562.5V570H375V632.5zM687.5 -55H250V507.5H687.5V-55zM437.5 445H500V7.5H437.5V445zM562.5 445H625V7.5H562.5V445zM312.5 445H375V7.5H312.5V445z" /> diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.ttf b/src/vs/base/browser/ui/octiconLabel/octicons/octicons2.ttf index 654cfe91291a4b391122cf0b76277f89e8723e80..daa9a772ad2ea0963483e58ae0f3d30c2d558cc9 100644 GIT binary patch delta 4908 zcma)A3y>Vub$$1BPxt(1dOqDVv$HeP^XuK6@6L43?5w07NxNE$)uNS*erP3ODf$Hj z`j!MLc84Oc1_73om9UU-0>%(R2)_l4S>a$P7p8)+0~HYiaT#zq6lDmZWW}ttLvGJX zQQ=A@={Mc)_3M7G-@EtRd+wb#Cg|^u(<5^EuBE>r1nwq;topO-H}1Ns$J$N^`!XT+ zkGJi(Y3ps`xj{m_0Ya|t-@awzX3_LU&(RfG^O9d~zmX8((W7@nJh!YPgvNL6+`G^5 zlc1ZBTW>k~LNV5JD*f~|uDQP_Hs)iTo&2)VzvFWK)xd-q{s~^3d>6i?g)9N%5tAlN ztChvtip$6nmQLe?4P-tskw|(=`jPZw=_e+ssT~?S$S!g#mLudZ$uV+*{0sRN`8BX$ z2PgO-260G2`eNyTF33WqReE8z%!T<_24N|zf;F%fcEK+E)Cy8171Bc%kpZ%djFCIY zQS8}F4wJjdcgQ`&MGlZ*ay^MnKESP{mea<%@%Yh1(>y+T#F8;jyd;b@9&ml^Ug16> ztPpM#?iYUS>GAy5t9yUp3;Q1TFZKUgOp33FXQi0*9qDYq99SB7HtX-v!yS}tI98`W_3v2 zpzcy{RgbCfYpV8Q#a>xhxuf!Z!V>b;>&)NM^Qa{=v6IP~_wGpJb>{Er4^fXE5++3i znJfvstO!+E0?jMCKo&r*$_0@9a#d2QlAl+q2`3{ZRGn7gLwGb6z3_H43N;a4g64ir zZ2lXphI^rTt)w;2fn9reW#Q1_?3z`9jO>AWsCz+@-V~cT9F5YaqL!KX+6}O#Vrkw9 z=jK_p)0N+^%G{Mblv@ElIU^+&lp~Z(?kUYNHO`X)DWkFtKoLk(sueA*->`Au>S zA*oB?i|45(3Xo*KFq)N}?UUeT%&UoJ(c zpV8^;i&8^`HRFIvk6M7hX$+*-BMCI-T0e2~38Hyp^;qA+4zxPFZaf$W?Z z%ErVrA62T9b+_eIu@UgwI(pRRR!h7kxsgAt{=Y<3DMSBuU($rSp+v+ugS z=*tXizKywo(TGll$Xc?!rM^Vm5J#R-TB@t$DIa$NVp-sLtWjo~+OiqV0Dgo){Wdi0 zFyXD!kcdttCT>)Q{7~z7wtDe7$1}Rv8!ZfbIHs_AQ77keWK{@e=E#NNq^B<*2`VX1 z--gv%AlvSAb`Q;4#CPcfv8)C`aptR#;NgmWZ2=KC8%l-?My(1?8ffG~@COOECfYiy zn%m0=GvND1DEx)5ZTg@;zC1=kE0k3pj|~aBG4oRyC(dp1bq=Y z2Y9KfDPNaRTA2vXi8N5Ymn8*b%l0W%mlFll()n=G+m{Pud!u^|xcuYA?YmtxkPFcV z<-7rsOGr{GElKvTM@fp=z`{$C@o<5AhpTB=!~BUz9yV~humv}`DLM;3znTv4-6%olLAFvu;&PinzfQ zkzhCMmLe`N>zvKzZca5{bc;5d=w2PD_tgU{R|a&f1gp)&fr(lo6;V!jgm$*GlWi9~ z30bVn)q5*q^DyidE4}lgTm4=y&H{_vN>?tUxC5b(F$rGXKCj=OlO`Wp@G@;YKrav< z=_I+9w?n&#P-9PdR%g7eU7#)GjV{AxBH&H<%L1M>F!*h?cY(QFF5EJ4?6xg6K6CZx z(e3pcIkLpwfHnp_BDFOC#dq-FJWY=)sk#3ui<2 zzEb7(yKX7vdhF3;&r?7Bn?lqBQ`2yuKJj_~nTBE0KV^0xL;Fdl#diea=#5#OMZ2Yl zJ8VT*^lOw!2xY$PPryIV+q?P7yiKaDy!OD2JJ#cMZj*)Uvd zS2vEObmO4s6MY+Y-njqTRXqWFerU_yxyz65)n`2Iwc!n0e4E!UF8Zv>{FR%^{bL(d zT5A5RJ{cb9V;U~vB@&WElq869acxyHhtx?Q;zW*fe!~UJrHUd{(hL$Nk76^x>623^N?jkt>tMa!9Bi@U^P{89 z*NpCIL(r%3H#$mhN`;zBaC8u!4<(xpcuueD_{oaeLwMB!Av@jR}%gkZA8ychC z>jo5MU|siUy?Lju!-3POMQVGyx+r}!uKB>^2g5^-#$5!P ziy~QxNKzQKL=0X6_ z;v~*c!t=&d_{#7{4&LSBajHYkc6`YZXm`?Ya1Jv|L9j2a&%AC-^C4@u+gWfw!+R_a z$;lWK@=t$(*#ScM5W0}_#DXCv<{8K#JX4cWRnVX+RTYq!IU_R{tP^VhiJHCe>4uTY zPB^!do}ankY<{(7!b;hZnRQcZqed^8SiIri8^>@uFCvcs@8{DT@0XxlmC8CO)pAvF zQS>zhs7Q6HNpxH7Z1;}LGtKF1-nqY!um}G7kn~jBB$l3b0MC;CnW^Epwf=Ap{9 zDugU{fuaKjkkttII4^HpdCBPEMdS}JugJXO%>#yLs6ylxD3>Zmgo1HZ;hp}m?riP&gU4&x;#hyrHYL}-eD$(OR@wFy$wn?VcWr~0bH%Yc z#)_?1-;amQT(sF9+_57FANhJzj8qDC>JTu%c355fU)+R_XyV}JhZ+XAXk;!1Wd=?# z>rTl+S(6bFhLbdc2ra=GsY+E#8%al5Mt_JoN&+srLO+7To6zv8c#Uif{iW85NYskA zj4jS5R-A7FS&PLS9?x~7qx#g;t8lIkLd@^=%{<}nj0kmL&4R@evseT(J6exI2wBST zq|GH$QyUmZta)ct*Ha!|n5%uV!KHyi?!ZT^2{~9$q#+FS_S@$FGLY!A%-Hzx~OV7B}xx zU~4gw+j0E@s`sOk=m#7a^O1JIK<@W3m8)c@b7~d5?lvv2uT&w zEsPcoqHpEjWzFDygHd@5sJwVGc+$^CQ=zF)5~k`pJdem4JbfBTeh{9YM%qV;$9Sc; h9zoaHYri?6%q+3P|!gH;YMLH#uM-I+){TCXu4}$;z delta 4752 zcmZu#3v?6LnZ9>M(#W4trTMuAZ5ZqqcoNq3>$?#V9XbxscL+2nLnlJrozA@*ed zk@16^&Uoft-S_?O|9}5C{`eSiVU&0>DnHTvJB09IgwUG1x7~GL_h5fBLZ(*{vR&W2 z_2EtDyH6cK$lZX@R?lDGbJs=&*%uyNEm^axx(^^kpF4UW>?*Y*M8xjfv1_;e=YA(b z-@NnaxlE*r__r^HYTN&WYRHdZboBP#ji1h@Uv`aJ@o(_i@;mr*BAr6ms3IE>S;Lw<$V5y)#UPrG%qSecC;oi=RQ$yv&=V1w_aC7Bu$5Xe4JK$c9d+=Jk4)^1ocqdG?8mTCcn$aS(81Pd{3E zzV@nfo%3mW9X(8cpMKq?xlXwW_fzh7JR3dNy-#^BGY>K&%v-F+?q|=jx^JWJgztC$ zF8^Nt<$x`)De%J}6MQuIXR{qYl}_)GEMixx32t`q;G!P9W6;X=c;#@&q<=B=LhdLot> zPP~-3Dy@^Al+H_kOlFe9$*ZZ>)IX>6w3a@d*_t_!d98_Q+THZsrmI;;c7C=$`>I?o z@04Gb|5d&$|6ZQREzF%%V#;pil=5l58krFK@|WZb#C13@E3%<-qx*2U^d@!Rmyn2YCNp}2%pW5tdR~SN0PnR*x2wT$c}=#>T>3K#p#*9{x1j~# zAt{oAh43jX#vKGKh^J^as?s8#H#4lPKzRx*kFuB*4XgQV7_$szfnCv286M(e3$L^W zIhiKtTsYX4tGAeKn>dcR%5j_Rx`iZvVwO0%KE|3!j4cU{7P_N8GifEr>-rnGMZ+!n z8^jagY>2d1Cqkh_wVe!Q!wmvAc@ajDIl)I++BM~HB`RR z{4Oqy5TB4a8f9u z=2|5m&PgGy5cJ>H73KW%V}+(hRSpbv;0a7tbq@3?KBcI96sfY7N z%o7t--4fxEzZtL_{$iNP#+`Nwd2< z!?j4h8>Oj+Y3QQw$Lo!o>Tl< zgK=mqRwTQ_+_>nD%*+NV`@;*hu8hMGl6=bA0XSkY5KckTH46lR=YTi)$!tZ53NYg9|xMID;ls(5qI+r36SQRNRtBP`#@y-C| zu?TYjdt9s>uaa=o);j6{m&!4UelFeN7KZ0wQNkb3wVl+hW;OYTnWlMsX%@A)py@yV z%kccmT+6!Ekg8`5t*&Belm9f^*g1<|`Lec;D4he^J%_@m1Ki#4>6k%DU`z3s;rbqM z?J97o6sGbjcz#npCpN`JrP9)92py)|Ve(Kd6I(ryXoyHsq#-e|I>vAg2gev;li_Ny zV*_1NW42h#H8oDZwovo+_WD*r>UT0VH4Kdx5gh?G%DbGLBymm`A7z!c_Le-WAHnyt z`Ih#!-_UOC_If483n}db+gh{6c+=v%` zLZ(a_sowI71wNzTA)NDfrE<-QRojch_pfQ9;>)+}UEKAAa$AQy^yqh@39Q6Ke21xU$zCsz?KPOb_Ff}c1I@4)0L ztxy;*6yV)8mFNeH!ZJt$5C*Crwnd!Q4`$bFT9eh6b?!Q_tFxeK zlSg2_`|(7vnC;7E`{cdF&Rq*}=dR9TK|csE?kVV9c#nRt{F9#L_R=A8C-G;HlgSEB zVCZlj4#LYq;2>fSn2oY{IIyE^UWII!)QB`z1PJCHX1W;2*JFvrb1L4WVW*990_QM5Np#o&lU)g6>E*yOMEST z^3o8tywtcY`(GfZGwjIU`s|;KwyV)U+VOZ-;ANA8vRIE_!sdTWMQ!Yf{jC1BihrHi za^Z+AK)&c-@(Q;7ZzRxKhX~llugM7UO8NOa6GZ8kST42$J^Nu>&EzJKWb z!Tx+RXtn;q_lNrPB~{m!I=nuw!xl=%xHRX)%gF#!1*dKI_&oNdOYzV^Pj@PP>u`hH zp|3hKTr-SzrjfJzgLM%h(HQl3d5K|ymKwU64hQ|g&~r;G`+k{xkhqAv6&(gU^5*5y zckh0%^ehPHFW^l5D2`-wJNg=0i~500a(PuW?2%PdpkS0Rh5#sPI2Sk1smKXx(`*d| zY6|R^WYs9El7JPcx;YApvWS!n@>Kk(l1VmeT5~d^5Tzwji>9?mnOsiy(P2qUB*Y{a z!@D3iuR-FX(J@2Bc!>XQ$>8;wmP=L(M4T!oMLJxr8jRg$t2Mv(xQh8^{NBRhCaLAv z_m07E!#FSD>HLA=?F}&W=KD5_64%qW9kM$%!{9xAJz^MQnTOCrfog42%E(k z+HE&kYpd<94vZhsibXWK{hl9`3}LoWP8Dx8OiYuX3h8_tbX7Mm>7_z)Uh+cRoc+;{9+8%63Y_-J za#^dLZBC|vb6Tz8Mg~{kz}HSjBKn7c`uactR;WenE|)zrHm1K}@H$-d@)h*hFADnq zaD}32bolFRw4mFH9NrTF%6u~*XhqExa23ZipjxejE7xuwrb?^Gw?MIbKqMnP zYyDq!2@uL6_F9|na$@wgm@T;RZ|%Xb^zfUNSJkCd~G%mPDjK|dp6zO+TPxJ_ol0mX~XS< zkQD&&oFL%8f}oSDuYYXugAvuYcI!G{GxxAsiLTS#EAduqyV-rtIA;V|nEAW0q>W3e zt(oBb73EF4%D7~Z)C=UHN@Ry%^%vv18NO8DgTW5gW~BMScOzyK{EEW3G;r}!q4 zCn2u0n391FEm)+8(#AJ7Wd1XK*XXLz)!etDcyZT{y3GWsmztW0hbJ!{Ibwvw{(e06 z!3QpvOTV~(KPtcW(7gupCV_d$igK9}7ch;NkMag(@G(mWgF95dc4fs3U*~4<3%5-3 zZbKVK From 5a36d17ea6a3a56bd8e43eaf47caeeb5c2e53455 Mon Sep 17 00:00:00 2001 From: Andy Liu Date: Sun, 18 Aug 2019 20:11:46 -0700 Subject: [PATCH 325/613] fix Markdown Preview scroll remains same after clicking on some other link --- extensions/markdown-language-features/src/features/preview.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index c874f5791edcf..b79939d1bd4b0 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -286,6 +286,8 @@ export class MarkdownPreview extends Disposable { const editor = vscode.window.activeTextEditor; if (editor && editor.document.uri.fsPath === resource.fsPath) { this.line = getVisibleLine(editor); + } else { + this.line = 0; } // If we have changed resources, cancel any pending updates From 34daff0fd7e4a5710261e2c18ef27f0cf0a1f68b Mon Sep 17 00:00:00 2001 From: Aidan Dang Date: Mon, 19 Aug 2019 14:27:05 +1000 Subject: [PATCH 326/613] Fix trivial zsh completion typo --- resources/completions/zsh/_code | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/completions/zsh/_code b/resources/completions/zsh/_code index 25dfe7ca98485..c7abee1bc5841 100644 --- a/resources/completions/zsh/_code +++ b/resources/completions/zsh/_code @@ -17,7 +17,7 @@ arguments=( '--telemetry[show all telemetry events which VS code collects]' '--extensions-dir[set the root path for extensions]:root path:_directories' '--list-extensions[list the installed extensions]' - '--category[filters instaled extension list by category, when using --list-extension]' + '--category[filters installed extension list by category, when using --list-extension]' '--show-versions[show versions of installed extensions, when using --list-extension]' '--install-extension[install an extension]:id or path:_files -g "*.vsix(-.)"' '--uninstall-extension[uninstall an extension]:id or path:_files -g "*.vsix(-.)"' From 9a2001bc7e6734974a7192212ef68c00b7e8fd80 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 14 Aug 2019 12:54:59 -0700 Subject: [PATCH 327/613] Improvements --- .../editor/browser/services/openerService.ts | 60 ++++++++++++- .../standalone/browser/standaloneEditor.ts | 8 +- .../browser/services/openerService.test.ts | 90 ++++++++++++++++++- src/vs/platform/request/common/request.ts | 8 ++ .../contrib/url/common/url.contribution.ts | 50 ++++++++++- .../opener/electron-browser/openerService.ts | 8 +- 6 files changed, 212 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 608d6e3b5d9c2..2ffdcad0906a5 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -14,6 +14,9 @@ import { IOpenerService, IOpener } from 'vs/platform/opener/common/opener'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { IDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { localize } from 'vs/nls'; export class OpenerService implements IOpenerService { @@ -24,6 +27,8 @@ export class OpenerService implements IOpenerService { constructor( @ICodeEditorService private readonly _editorService: ICodeEditorService, @ICommandService private readonly _commandService: ICommandService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IDialogService private readonly _dialogService: IDialogService ) { // } @@ -51,12 +56,42 @@ export class OpenerService implements IOpenerService { private _doOpen(resource: URI, options?: { openToSide?: boolean }): Promise { - const { scheme, path, query, fragment } = resource; + const { scheme, authority, path, query, fragment } = resource; - if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https) || equalsIgnoreCase(scheme, Schemas.mailto)) { - // open http or default mail application + if (equalsIgnoreCase(scheme, Schemas.mailto)) { + // open default mail application return this.openExternal(resource); + } + if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) { + const trustedDomains = this._configurationService.getValue('http.trustedDomains'); + const domainToOpen = `${scheme}://${authority}`; + + if (isDomainTrusted(domainToOpen, trustedDomains)) { + return this.openExternal(resource); + } else { + return this._dialogService.confirm({ + title: localize('openExternalLink', 'Open External Link'), + type: 'question', + message: localize('openExternalLinkAt', 'Do you want to leave VS Code and open the external website at') + ` ${resource.toString()}?`, + detail: resource.toString(), + primaryButton: localize('openLink', 'Open Link'), + secondaryButton: localize('cance', 'Cancel'), + checkbox: { + label: localize('trustAllLinksOn', 'Trust all links on') + ` ${domainToOpen}`, + checked: false + } + }).then(({ confirmed, checkboxChecked }) => { + if (checkboxChecked) { + this._configurationService.updateValue('http.trustedDomains', [...trustedDomains, domainToOpen]); + } + if (confirmed) { + return this.openExternal(resource); + } + + return Promise.resolve(false); + }); + } } else if (equalsIgnoreCase(scheme, Schemas.command)) { // run command or bail out if command isn't known if (!CommandsRegistry.getCommand(path)) { @@ -106,3 +141,22 @@ export class OpenerService implements IOpenerService { return Promise.resolve(true); } } + +/** + * Check whether a domain like https://www.microsoft.com matches + * the list of trusted domains. + * + */ +function isDomainTrusted(domain: string, trustedDomains: string[]) { + for (let i = 0; i < trustedDomains.length; i++) { + if (trustedDomains[i] === '*') { + return true; + } + + if (trustedDomains[i] === domain) { + return true; + } + } + + return false; +} diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 8750db8f94301..4712be0dfb701 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -38,6 +38,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; type Omit = Pick>; @@ -51,7 +52,12 @@ function withAllStandaloneServices(domElement: H } if (!services.has(IOpenerService)) { - services.set(IOpenerService, new OpenerService(services.get(ICodeEditorService), services.get(ICommandService))); + services.set(IOpenerService, new OpenerService( + services.get(ICodeEditorService), + services.get(ICommandService), + services.get(IConfigurationService), + services.get(IDialogService), + )); } let result = callback(services); diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index eadf902357a35..c82f40ea429f2 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -7,6 +7,9 @@ import { URI } from 'vs/base/common/uri'; import { OpenerService } from 'vs/editor/browser/services/openerService'; import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices'; import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { deepClone } from 'vs/base/common/objects'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; suite('OpenerService', function () { @@ -24,19 +27,56 @@ suite('OpenerService', function () { } }; + function getConfigurationService(trustedDomainsSetting: string[]) { + let _settings = deepClone(trustedDomainsSetting); + + return new class implements IConfigurationService { + getValue = () => _settings; + updateValue = (key: string, val: string[]) => { + _settings = val; + return Promise.resolve(); + } + + // Don't care + _serviceBrand: any; + onDidChangeConfiguration = () => ({ dispose: () => { } }); + getConfigurationData = () => null; + reloadConfiguration = () => Promise.resolve(); + inspect = () => null as any; + keys = () => null as any; + }; + } + + function getDialogService() { + return new class implements IDialogService { + _confirmInvoked = 0; + confirm = () => { + this._confirmInvoked++; + return Promise.resolve({} as any); + } + get confirmInvoked() { return this._confirmInvoked; } + + // Don't care + _serviceBrand: any; + show = () => { + return Promise.resolve({} as any); + } + }; + } + setup(function () { lastCommand = undefined; }); test('delegate to editorService, scheme:///fff', function () { - const openerService = new OpenerService(editorService, NullCommandService); + const openerService = new OpenerService(editorService, NullCommandService, getConfigurationService([]), getDialogService()); openerService.open(URI.parse('another:///somepath')); assert.equal(editorService.lastInput!.options!.selection, undefined); }); test('delegate to editorService, scheme:///fff#L123', function () { - const openerService = new OpenerService(editorService, NullCommandService); + const openerService = new OpenerService(editorService, NullCommandService, getConfigurationService([]), getDialogService()); openerService.open(URI.parse('file:///somepath#L23')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); @@ -59,7 +99,7 @@ suite('OpenerService', function () { test('delegate to editorService, scheme:///fff#123,123', function () { - const openerService = new OpenerService(editorService, NullCommandService); + const openerService = new OpenerService(editorService, NullCommandService, getConfigurationService([]), getDialogService()); openerService.open(URI.parse('file:///somepath#23')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); @@ -78,7 +118,7 @@ suite('OpenerService', function () { test('delegate to commandsService, command:someid', function () { - const openerService = new OpenerService(editorService, commandService); + const openerService = new OpenerService(editorService, commandService, getConfigurationService([]), getDialogService()); const id = `aCommand${Math.random()}`; CommandsRegistry.registerCommand(id, function () { }); @@ -98,4 +138,46 @@ suite('OpenerService', function () { assert.equal(lastCommand!.args[0], 12); assert.equal(lastCommand!.args[1], true); }); + + test('links are protected by dialog confirmation', function () { + const dialogService = getDialogService(); + const openerService = new OpenerService(editorService, commandService, getConfigurationService([]), dialogService); + + openerService.open(URI.parse('https://www.microsoft.com')); + assert.equal(dialogService.confirmInvoked, 1); + }); + + test('links on the whitelisted domains can be opened without dialog confirmation', function () { + const dialogService = getDialogService(); + const openerService = new OpenerService(editorService, commandService, getConfigurationService(['https://microsoft.com']), dialogService); + + openerService.open(URI.parse('https://microsoft.com')); + openerService.open(URI.parse('https://microsoft.com/')); + openerService.open(URI.parse('https://microsoft.com/en-us/')); + openerService.open(URI.parse('https://microsoft.com/en-us/?foo=bar')); + openerService.open(URI.parse('https://microsoft.com/en-us/?foo=bar#baz')); + + assert.equal(dialogService.confirmInvoked, 0); + }); + + test('variations of links are protected by dialog confirmation', function () { + const dialogService = getDialogService(); + const openerService = new OpenerService(editorService, commandService, getConfigurationService(['https://microsoft.com']), dialogService); + + openerService.open(URI.parse('http://microsoft.com')); + openerService.open(URI.parse('https://www.microsoft.com')); + + assert.equal(dialogService.confirmInvoked, 2); + }); + + test('* removes all link protection', function () { + const dialogService = getDialogService(); + const openerService = new OpenerService(editorService, commandService, getConfigurationService(['*']), dialogService); + + openerService.open(URI.parse('https://code.visualstudio.com/')); + openerService.open(URI.parse('https://www.microsoft.com')); + openerService.open(URI.parse('https://www.github.com')); + + assert.equal(dialogService.confirmInvoked, 0); + }); }); diff --git a/src/vs/platform/request/common/request.ts b/src/vs/platform/request/common/request.ts index 31e3c314242c1..27601de0ef05d 100644 --- a/src/vs/platform/request/common/request.ts +++ b/src/vs/platform/request/common/request.ts @@ -117,6 +117,14 @@ Registry.as(Extensions.Configuration) type: 'boolean', default: true, description: localize('systemCertificates', "Controls whether CA certificates should be loaded from the OS. (On Windows and macOS a reload of the window is required after turning this off.)") + }, + 'http.trustedDomains': { + type: 'array', + default: ['https://code.visualstudio.com'], + description: localize('trustedDomains', "Controls whether a http/https link can be opened directly in browser.\n\nAdd `*` to the list to whitelist all domains."), + items: { + type: 'string' + } } } }); diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index cbed9bef6a339..66302cb5411f5 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -11,6 +11,7 @@ import { IURLService } from 'vs/platform/url/common/url'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class OpenUrlAction extends Action { @@ -34,5 +35,50 @@ export class OpenUrlAction extends Action { } } -Registry.as(ActionExtensions.WorkbenchActions) - .registerWorkbenchAction(new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), 'Open URL', localize('developer', "Developer")); \ No newline at end of file +export class ConfigureTrustedDomainsAction extends Action { + + static readonly ID = 'workbench.action.configureTrustedDomains'; + static readonly LABEL = localize('configureTrustedDomains', "Configure Trusted Domains"); + + constructor( + id: string, + label: string, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + super(id, label); + } + + run(): Promise { + const trustedDomains = this.configurationService.getValue('http.trustedDomains'); + + return this.quickInputService.pick(trustedDomains.map(d => { + return { + type: 'item', + label: d, + picked: true, + }; + }), { + canPickMany: true + }).then(result => { + if (result) { + this.configurationService.updateValue('http.trustedDomains', result.map(r => r.label)); + } + }); + } +} + +Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( + new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), + 'Open URL', + localize('developer', 'Developer') +); +Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( + new SyncActionDescriptor( + ConfigureTrustedDomainsAction, + ConfigureTrustedDomainsAction.ID, + ConfigureTrustedDomainsAction.LABEL + ), + 'Configure Trusted Domains' +); + diff --git a/src/vs/workbench/services/opener/electron-browser/openerService.ts b/src/vs/workbench/services/opener/electron-browser/openerService.ts index 3085bd78a1402..160ba1cbf7102 100644 --- a/src/vs/workbench/services/opener/electron-browser/openerService.ts +++ b/src/vs/workbench/services/opener/electron-browser/openerService.ts @@ -12,6 +12,8 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; export class OpenerService extends BaseOpenerService { @@ -20,9 +22,11 @@ export class OpenerService extends BaseOpenerService { constructor( @ICodeEditorService codeEditorService: ICodeEditorService, @ICommandService commandService: ICommandService, - @IWindowsService private readonly windowsService: IWindowsService + @IWindowsService private readonly windowsService: IWindowsService, + @IConfigurationService readonly configurationService: IConfigurationService, + @IDialogService readonly dialogService: IDialogService ) { - super(codeEditorService, commandService); + super(codeEditorService, commandService, configurationService, dialogService); } async openExternal(resource: URI): Promise { From 821519ddfb5e143c0de9150b88e5e2aa9f2d677f Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 14 Aug 2019 19:40:48 -0700 Subject: [PATCH 328/613] Drop Configure Trusted Domains command for now --- .../contrib/url/common/url.contribution.ts | 50 +------------------ 1 file changed, 2 insertions(+), 48 deletions(-) diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 66302cb5411f5..828ad0cf30b58 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -11,7 +11,6 @@ import { IURLService } from 'vs/platform/url/common/url'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class OpenUrlAction extends Action { @@ -35,50 +34,5 @@ export class OpenUrlAction extends Action { } } -export class ConfigureTrustedDomainsAction extends Action { - - static readonly ID = 'workbench.action.configureTrustedDomains'; - static readonly LABEL = localize('configureTrustedDomains', "Configure Trusted Domains"); - - constructor( - id: string, - label: string, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - super(id, label); - } - - run(): Promise { - const trustedDomains = this.configurationService.getValue('http.trustedDomains'); - - return this.quickInputService.pick(trustedDomains.map(d => { - return { - type: 'item', - label: d, - picked: true, - }; - }), { - canPickMany: true - }).then(result => { - if (result) { - this.configurationService.updateValue('http.trustedDomains', result.map(r => r.label)); - } - }); - } -} - -Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( - new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), - 'Open URL', - localize('developer', 'Developer') -); -Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( - new SyncActionDescriptor( - ConfigureTrustedDomainsAction, - ConfigureTrustedDomainsAction.ID, - ConfigureTrustedDomainsAction.LABEL - ), - 'Configure Trusted Domains' -); - +Registry.as(ActionExtensions.WorkbenchActions) + .registerWorkbenchAction(new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), 'Open URL', localize('developer', "Developer")); From 5979aced9077a0178a79bf79cf7aefe16126a2b3 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 14 Aug 2019 19:43:37 -0700 Subject: [PATCH 329/613] :lipstick: --- src/vs/editor/browser/services/openerService.ts | 4 ++-- src/vs/workbench/contrib/url/common/url.contribution.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 2ffdcad0906a5..25c232c09ea48 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -73,10 +73,10 @@ export class OpenerService implements IOpenerService { return this._dialogService.confirm({ title: localize('openExternalLink', 'Open External Link'), type: 'question', - message: localize('openExternalLinkAt', 'Do you want to leave VS Code and open the external website at') + ` ${resource.toString()}?`, + message: localize('openExternalLinkAt', 'Do you want to leave VS Code to open the external website at') + ` ${resource.toString()}?`, detail: resource.toString(), primaryButton: localize('openLink', 'Open Link'), - secondaryButton: localize('cance', 'Cancel'), + secondaryButton: localize('cancel', 'Cancel'), checkbox: { label: localize('trustAllLinksOn', 'Trust all links on') + ` ${domainToOpen}`, checked: false diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 828ad0cf30b58..cbed9bef6a339 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -35,4 +35,4 @@ export class OpenUrlAction extends Action { } Registry.as(ActionExtensions.WorkbenchActions) - .registerWorkbenchAction(new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), 'Open URL', localize('developer', "Developer")); + .registerWorkbenchAction(new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), 'Open URL', localize('developer', "Developer")); \ No newline at end of file From d41ffa2112c6b9bae157b6039b25df3c2f64bbe2 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Thu, 15 Aug 2019 08:32:19 -0700 Subject: [PATCH 330/613] Drop details & on -> from --- src/vs/editor/browser/services/openerService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 25c232c09ea48..48cf5fbd4ad32 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -74,11 +74,10 @@ export class OpenerService implements IOpenerService { title: localize('openExternalLink', 'Open External Link'), type: 'question', message: localize('openExternalLinkAt', 'Do you want to leave VS Code to open the external website at') + ` ${resource.toString()}?`, - detail: resource.toString(), primaryButton: localize('openLink', 'Open Link'), secondaryButton: localize('cancel', 'Cancel'), checkbox: { - label: localize('trustAllLinksOn', 'Trust all links on') + ` ${domainToOpen}`, + label: localize('trustAllLinksFrom', 'Trust all links from') + ` ${domainToOpen}`, checked: false } }).then(({ confirmed, checkboxChecked }) => { From 8ebb596b3d71efc431e1de4c538d3cc7ff34e197 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Thu, 15 Aug 2019 08:54:59 -0700 Subject: [PATCH 331/613] Address feedbacks --- src/vs/editor/browser/services/openerService.ts | 11 +++++++++-- src/vs/nls.d.ts | 7 +++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 48cf5fbd4ad32..f732093c32b3c 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -17,6 +17,7 @@ import { LinkedList } from 'vs/base/common/linkedList'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { localize } from 'vs/nls'; +import { IProductService } from 'vs/platform/product/common/product'; export class OpenerService implements IOpenerService { @@ -28,7 +29,8 @@ export class OpenerService implements IOpenerService { @ICodeEditorService private readonly _editorService: ICodeEditorService, @ICommandService private readonly _commandService: ICommandService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IDialogService private readonly _dialogService: IDialogService + @IDialogService private readonly _dialogService: IDialogService, + @IProductService private readonly _productService: IProductService ) { // } @@ -73,7 +75,12 @@ export class OpenerService implements IOpenerService { return this._dialogService.confirm({ title: localize('openExternalLink', 'Open External Link'), type: 'question', - message: localize('openExternalLinkAt', 'Do you want to leave VS Code to open the external website at') + ` ${resource.toString()}?`, + message: localize( + 'openExternalLinkAt', + 'Do you want {0} to open the external website at {1}?', + this._productService.productConfiguration.nameShort, + resource.toString(true) + ), primaryButton: localize('openLink', 'Open Link'), secondaryButton: localize('cancel', 'Cancel'), checkbox: { diff --git a/src/vs/nls.d.ts b/src/vs/nls.d.ts index 38bbd076068b4..3942ff08669b4 100644 --- a/src/vs/nls.d.ts +++ b/src/vs/nls.d.ts @@ -8,5 +8,12 @@ export interface ILocalizeInfo { comment: string[]; } +/** + * Localize a message. `message` can contain `{n}` notation where it is replaced by the nth value in `...args`. + */ export declare function localize(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string; + +/** + * Localize a message. `message` can contain `{n}` notation where it is replaced by the nth value in `...args`. + */ export declare function localize(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string; From b258e13432e74b32b3ad0bb235a05cfc7390e204 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Thu, 15 Aug 2019 09:06:57 -0700 Subject: [PATCH 332/613] Fix services --- .../standalone/browser/standaloneEditor.ts | 2 + .../browser/services/openerService.test.ts | 77 +++++++++++++++++-- .../opener/electron-browser/openerService.ts | 6 +- 3 files changed, 75 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 4712be0dfb701..b92f2d803f85f 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -39,6 +39,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IProductService } from 'vs/platform/product/common/product'; type Omit = Pick>; @@ -57,6 +58,7 @@ function withAllStandaloneServices(domElement: H services.get(ICommandService), services.get(IConfigurationService), services.get(IDialogService), + services.get(IProductService) )); } diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index c82f40ea429f2..187ddf901b8bd 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -10,6 +10,7 @@ import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platfo import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { deepClone } from 'vs/base/common/objects'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IProductService } from 'vs/platform/product/common/product'; suite('OpenerService', function () { @@ -64,19 +65,43 @@ suite('OpenerService', function () { }; } + function getProductService() { + return new class implements IProductService { + // Don't care + _serviceBrand: any; + + productConfiguration = { + nameShort: 'VS Code' + } as any; + }; + } + + setup(function () { lastCommand = undefined; }); test('delegate to editorService, scheme:///fff', function () { - const openerService = new OpenerService(editorService, NullCommandService, getConfigurationService([]), getDialogService()); + const openerService = new OpenerService( + editorService, + NullCommandService, + getConfigurationService([]), + getDialogService(), + getProductService() + ); openerService.open(URI.parse('another:///somepath')); assert.equal(editorService.lastInput!.options!.selection, undefined); }); test('delegate to editorService, scheme:///fff#L123', function () { - const openerService = new OpenerService(editorService, NullCommandService, getConfigurationService([]), getDialogService()); + const openerService = new OpenerService( + editorService, + NullCommandService, + getConfigurationService([]), + getDialogService(), + getProductService() + ); openerService.open(URI.parse('file:///somepath#L23')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); @@ -99,7 +124,13 @@ suite('OpenerService', function () { test('delegate to editorService, scheme:///fff#123,123', function () { - const openerService = new OpenerService(editorService, NullCommandService, getConfigurationService([]), getDialogService()); + const openerService = new OpenerService( + editorService, + NullCommandService, + getConfigurationService([]), + getDialogService(), + getProductService() + ); openerService.open(URI.parse('file:///somepath#23')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); @@ -118,7 +149,13 @@ suite('OpenerService', function () { test('delegate to commandsService, command:someid', function () { - const openerService = new OpenerService(editorService, commandService, getConfigurationService([]), getDialogService()); + const openerService = new OpenerService( + editorService, + commandService, + getConfigurationService([]), + getDialogService(), + getProductService() + ); const id = `aCommand${Math.random()}`; CommandsRegistry.registerCommand(id, function () { }); @@ -141,7 +178,13 @@ suite('OpenerService', function () { test('links are protected by dialog confirmation', function () { const dialogService = getDialogService(); - const openerService = new OpenerService(editorService, commandService, getConfigurationService([]), dialogService); + const openerService = new OpenerService( + editorService, + commandService, + getConfigurationService([]), + dialogService, + getProductService() + ); openerService.open(URI.parse('https://www.microsoft.com')); assert.equal(dialogService.confirmInvoked, 1); @@ -149,7 +192,13 @@ suite('OpenerService', function () { test('links on the whitelisted domains can be opened without dialog confirmation', function () { const dialogService = getDialogService(); - const openerService = new OpenerService(editorService, commandService, getConfigurationService(['https://microsoft.com']), dialogService); + const openerService = new OpenerService( + editorService, + commandService, + getConfigurationService(['https://microsoft.com']), + dialogService, + getProductService() + ); openerService.open(URI.parse('https://microsoft.com')); openerService.open(URI.parse('https://microsoft.com/')); @@ -162,7 +211,13 @@ suite('OpenerService', function () { test('variations of links are protected by dialog confirmation', function () { const dialogService = getDialogService(); - const openerService = new OpenerService(editorService, commandService, getConfigurationService(['https://microsoft.com']), dialogService); + const openerService = new OpenerService( + editorService, + commandService, + getConfigurationService(['https://microsoft.com']), + dialogService, + getProductService() + ); openerService.open(URI.parse('http://microsoft.com')); openerService.open(URI.parse('https://www.microsoft.com')); @@ -172,7 +227,13 @@ suite('OpenerService', function () { test('* removes all link protection', function () { const dialogService = getDialogService(); - const openerService = new OpenerService(editorService, commandService, getConfigurationService(['*']), dialogService); + const openerService = new OpenerService( + editorService, + commandService, + getConfigurationService(['*']), + dialogService, + getProductService() + ); openerService.open(URI.parse('https://code.visualstudio.com/')); openerService.open(URI.parse('https://www.microsoft.com')); diff --git a/src/vs/workbench/services/opener/electron-browser/openerService.ts b/src/vs/workbench/services/opener/electron-browser/openerService.ts index 160ba1cbf7102..b9faebbefbf58 100644 --- a/src/vs/workbench/services/opener/electron-browser/openerService.ts +++ b/src/vs/workbench/services/opener/electron-browser/openerService.ts @@ -14,6 +14,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IProductService } from 'vs/platform/product/common/product'; export class OpenerService extends BaseOpenerService { @@ -24,9 +25,10 @@ export class OpenerService extends BaseOpenerService { @ICommandService commandService: ICommandService, @IWindowsService private readonly windowsService: IWindowsService, @IConfigurationService readonly configurationService: IConfigurationService, - @IDialogService readonly dialogService: IDialogService + @IDialogService readonly dialogService: IDialogService, + @IProductService readonly productService: IProductService ) { - super(codeEditorService, commandService, configurationService, dialogService); + super(codeEditorService, commandService, configurationService, dialogService, productService); } async openExternal(resource: URI): Promise { From c62a21ffec7f880f1ecb5b98e71d5684be01173c Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Thu, 15 Aug 2019 11:13:03 -0700 Subject: [PATCH 333/613] Storage instead of setting. Add command for configuring --- .../editor/browser/services/openerService.ts | 14 ++-- .../standalone/browser/standaloneEditor.ts | 3 +- .../browser/services/openerService.test.ts | 42 ++++++------ .../contrib/url/common/url.contribution.ts | 68 ++++++++++++++++++- .../opener/electron-browser/openerService.ts | 6 +- 5 files changed, 100 insertions(+), 33 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index f732093c32b3c..e2d8dd2529edc 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -14,10 +14,10 @@ import { IOpenerService, IOpener } from 'vs/platform/opener/common/opener'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { IDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { localize } from 'vs/nls'; import { IProductService } from 'vs/platform/product/common/product'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; export class OpenerService implements IOpenerService { @@ -28,7 +28,7 @@ export class OpenerService implements IOpenerService { constructor( @ICodeEditorService private readonly _editorService: ICodeEditorService, @ICommandService private readonly _commandService: ICommandService, - @IConfigurationService private readonly _configurationService: IConfigurationService, + @IStorageService private readonly _storageService: IStorageService, @IDialogService private readonly _dialogService: IDialogService, @IProductService private readonly _productService: IProductService ) { @@ -66,7 +66,11 @@ export class OpenerService implements IOpenerService { } if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) { - const trustedDomains = this._configurationService.getValue('http.trustedDomains'); + let trustedDomains: string[] = []; + try { + trustedDomains = JSON.parse(this._storageService.get('http.trustedDomains', StorageScope.GLOBAL, '[]')); + } catch (err) { } + const domainToOpen = `${scheme}://${authority}`; if (isDomainTrusted(domainToOpen, trustedDomains)) { @@ -77,7 +81,7 @@ export class OpenerService implements IOpenerService { type: 'question', message: localize( 'openExternalLinkAt', - 'Do you want {0} to open the external website at {1}?', + 'Do you want {0} to open the external website?\n{1}', this._productService.productConfiguration.nameShort, resource.toString(true) ), @@ -89,7 +93,7 @@ export class OpenerService implements IOpenerService { } }).then(({ confirmed, checkboxChecked }) => { if (checkboxChecked) { - this._configurationService.updateValue('http.trustedDomains', [...trustedDomains, domainToOpen]); + this._storageService.store('http.trustedDomains', JSON.stringify([...trustedDomains, domainToOpen]), StorageScope.GLOBAL); } if (confirmed) { return this.openExternal(resource); diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index b92f2d803f85f..ddba66741d915 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -40,6 +40,7 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProductService } from 'vs/platform/product/common/product'; +import { IStorageService } from 'vs/platform/storage/common/storage'; type Omit = Pick>; @@ -56,7 +57,7 @@ function withAllStandaloneServices(domElement: H services.set(IOpenerService, new OpenerService( services.get(ICodeEditorService), services.get(ICommandService), - services.get(IConfigurationService), + services.get(IStorageService), services.get(IDialogService), services.get(IProductService) )); diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index 187ddf901b8bd..f42e3c399efdf 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -7,10 +7,10 @@ import { URI } from 'vs/base/common/uri'; import { OpenerService } from 'vs/editor/browser/services/openerService'; import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices'; import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { deepClone } from 'vs/base/common/objects'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProductService } from 'vs/platform/product/common/product'; +import { IStorageService } from 'vs/platform/storage/common/storage'; suite('OpenerService', function () { @@ -28,23 +28,23 @@ suite('OpenerService', function () { } }; - function getConfigurationService(trustedDomainsSetting: string[]) { + function getStorageService(trustedDomainsSetting: string[]) { let _settings = deepClone(trustedDomainsSetting); - return new class implements IConfigurationService { - getValue = () => _settings; - updateValue = (key: string, val: string[]) => { - _settings = val; - return Promise.resolve(); - } + return new class implements IStorageService { + get = () => JSON.stringify(_settings); + store = (key: string, val: string) => _settings = JSON.parse(val); // Don't care _serviceBrand: any; - onDidChangeConfiguration = () => ({ dispose: () => { } }); - getConfigurationData = () => null; - reloadConfiguration = () => Promise.resolve(); - inspect = () => null as any; - keys = () => null as any; + + onDidChangeStorage = () => ({ dispose: () => { } }); + onWillSaveState = () => ({ dispose: () => { } }); + + getBoolean = () => true; + getNumber = () => 0; + remove = () => { }; + logStorage = () => { }; }; } @@ -85,7 +85,7 @@ suite('OpenerService', function () { const openerService = new OpenerService( editorService, NullCommandService, - getConfigurationService([]), + getStorageService([]), getDialogService(), getProductService() ); @@ -98,7 +98,7 @@ suite('OpenerService', function () { const openerService = new OpenerService( editorService, NullCommandService, - getConfigurationService([]), + getStorageService([]), getDialogService(), getProductService() ); @@ -127,7 +127,7 @@ suite('OpenerService', function () { const openerService = new OpenerService( editorService, NullCommandService, - getConfigurationService([]), + getStorageService([]), getDialogService(), getProductService() ); @@ -152,7 +152,7 @@ suite('OpenerService', function () { const openerService = new OpenerService( editorService, commandService, - getConfigurationService([]), + getStorageService([]), getDialogService(), getProductService() ); @@ -181,7 +181,7 @@ suite('OpenerService', function () { const openerService = new OpenerService( editorService, commandService, - getConfigurationService([]), + getStorageService([]), dialogService, getProductService() ); @@ -195,7 +195,7 @@ suite('OpenerService', function () { const openerService = new OpenerService( editorService, commandService, - getConfigurationService(['https://microsoft.com']), + getStorageService(['https://microsoft.com']), dialogService, getProductService() ); @@ -214,7 +214,7 @@ suite('OpenerService', function () { const openerService = new OpenerService( editorService, commandService, - getConfigurationService(['https://microsoft.com']), + getStorageService(['https://microsoft.com']), dialogService, getProductService() ); @@ -230,7 +230,7 @@ suite('OpenerService', function () { const openerService = new OpenerService( editorService, commandService, - getConfigurationService(['*']), + getStorageService(['*']), dialogService, getProductService() ); diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index cbed9bef6a339..14ab53565097c 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -8,9 +8,10 @@ import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { IURLService } from 'vs/platform/url/common/url'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; export class OpenUrlAction extends Action { @@ -34,5 +35,66 @@ export class OpenUrlAction extends Action { } } -Registry.as(ActionExtensions.WorkbenchActions) - .registerWorkbenchAction(new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), 'Open URL', localize('developer', "Developer")); \ No newline at end of file +export class ConfigureTrustedDomainsAction extends Action { + + static readonly ID = 'workbench.action.configureTrustedDomains'; + static readonly LABEL = localize('configureTrustedDomains', "Configure Trusted Domains"); + + constructor( + id: string, + label: string, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IStorageService private readonly storageService: IStorageService + ) { + super(id, label); + } + + run(): Promise { + let trustedDomains: string[] = []; + try { + trustedDomains = JSON.parse(this.storageService.get('http.trustedDomains', StorageScope.GLOBAL, '[]')); + } catch (err) { } + + const quickPickItems: (IQuickPickItem | IQuickPickSeparator)[] = trustedDomains + .filter(d => d !== '*') + .map(d => { + return { + type: 'item', + label: d, + picked: true, + }; + }); + + quickPickItems.unshift({ + type: 'separator' + }); + quickPickItems.unshift({ + type: 'item', + label: '*', + description: 'Allow all links to be open without protection', + picked: trustedDomains.indexOf('*') !== -1 + }); + + return this.quickInputService.pick(quickPickItems, { + canPickMany: true + }).then(result => { + if (result) { + this.storageService.store('http.trustedDomains', JSON.stringify(result.map(r => r.label)), StorageScope.GLOBAL); + } + }); + } +} + +Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( + new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), + 'Open URL', + localize('developer', 'Developer') +); +Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( + new SyncActionDescriptor( + ConfigureTrustedDomainsAction, + ConfigureTrustedDomainsAction.ID, + ConfigureTrustedDomainsAction.LABEL + ), + 'Configure Trusted Domains' +); diff --git a/src/vs/workbench/services/opener/electron-browser/openerService.ts b/src/vs/workbench/services/opener/electron-browser/openerService.ts index b9faebbefbf58..d6cc8bb5bec10 100644 --- a/src/vs/workbench/services/opener/electron-browser/openerService.ts +++ b/src/vs/workbench/services/opener/electron-browser/openerService.ts @@ -12,9 +12,9 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProductService } from 'vs/platform/product/common/product'; +import { IStorageService } from 'vs/platform/storage/common/storage'; export class OpenerService extends BaseOpenerService { @@ -24,11 +24,11 @@ export class OpenerService extends BaseOpenerService { @ICodeEditorService codeEditorService: ICodeEditorService, @ICommandService commandService: ICommandService, @IWindowsService private readonly windowsService: IWindowsService, - @IConfigurationService readonly configurationService: IConfigurationService, + @IStorageService readonly storageService: IStorageService, @IDialogService readonly dialogService: IDialogService, @IProductService readonly productService: IProductService ) { - super(codeEditorService, commandService, configurationService, dialogService, productService); + super(codeEditorService, commandService, storageService, dialogService, productService); } async openExternal(resource: URI): Promise { From d521cf4d2bd36fda74e792a6812066da57b5fca1 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Thu, 15 Aug 2019 14:06:59 -0700 Subject: [PATCH 334/613] Don't add star to quickpick --- src/vs/workbench/contrib/url/common/url.contribution.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 14ab53565097c..f28a49ed47cef 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -70,8 +70,7 @@ export class ConfigureTrustedDomainsAction extends Action { }); quickPickItems.unshift({ type: 'item', - label: '*', - description: 'Allow all links to be open without protection', + label: 'Allow all links to be open without protection', picked: trustedDomains.indexOf('*') !== -1 }); From 970538a0c955fe62f8ef4dc0488f6ad703c8b89b Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Sun, 18 Aug 2019 21:55:59 -0700 Subject: [PATCH 335/613] Update UX --- .../editor/browser/services/openerService.ts | 44 ++++--- .../contrib/url/common/url.contribution.ts | 115 ++++++++++++------ 2 files changed, 103 insertions(+), 56 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index e2d8dd2529edc..7b30565fc123c 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -18,6 +18,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { localize } from 'vs/nls'; import { IProductService } from 'vs/platform/product/common/product'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import Severity from 'vs/base/common/severity'; export class OpenerService implements IOpenerService { @@ -76,31 +77,34 @@ export class OpenerService implements IOpenerService { if (isDomainTrusted(domainToOpen, trustedDomains)) { return this.openExternal(resource); } else { - return this._dialogService.confirm({ - title: localize('openExternalLink', 'Open External Link'), - type: 'question', - message: localize( + return this._dialogService.show( + Severity.Info, + localize( 'openExternalLinkAt', 'Do you want {0} to open the external website?\n{1}', this._productService.productConfiguration.nameShort, resource.toString(true) ), - primaryButton: localize('openLink', 'Open Link'), - secondaryButton: localize('cancel', 'Cancel'), - checkbox: { - label: localize('trustAllLinksFrom', 'Trust all links from') + ` ${domainToOpen}`, - checked: false - } - }).then(({ confirmed, checkboxChecked }) => { - if (checkboxChecked) { - this._storageService.store('http.trustedDomains', JSON.stringify([...trustedDomains, domainToOpen]), StorageScope.GLOBAL); - } - if (confirmed) { - return this.openExternal(resource); - } - - return Promise.resolve(false); - }); + [ + localize('openLink', 'Open Link'), + localize('cancel', 'Cancel'), + localize('configureLinkPermission', 'Configure Link Permission'), + ], + { + cancelId: 1 + }).then((choice) => { + if (choice === 0) { + return this.openExternal(resource); + } else if (choice === 2) { + return this._commandService.executeCommand('_workbench.action.configureTrustedDomains', domainToOpen).then((pickedDomains: string[]) => { + if (pickedDomains.indexOf(domainToOpen) !== -1) { + return this.openExternal(resource); + } + return Promise.resolve(false); + }); + } + return Promise.resolve(false); + }); } } else if (equalsIgnoreCase(scheme, Schemas.command)) { // run command or bail out if command isn't known diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index f28a49ed47cef..ef5eeb6a96aa3 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -12,6 +12,7 @@ import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/plat import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; export class OpenUrlAction extends Action { @@ -35,6 +36,70 @@ export class OpenUrlAction extends Action { } } +Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( + new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), + 'Open URL', + localize('developer', 'Developer') +); + +const configureTrustedDomainsHandler = ( + quickInputService: IQuickInputService, + storageService: IStorageService, + domainToConfigure?: string +) => { + let trustedDomains: string[] = []; + try { + trustedDomains = JSON.parse(storageService.get('http.trustedDomains', StorageScope.GLOBAL, '[]')); + } catch (err) { } + + const domainQuickPickItems: IQuickPickItem[] = trustedDomains + .filter(d => d !== '*') + .map(d => { + return { + type: 'item', + label: d, + picked: true, + }; + }); + + const specialQuickPickItems: IQuickPickItem[] = [ + { + type: 'item', + label: localize('allowAllLinks', 'Allow all links to be open without protection'), + picked: trustedDomains.indexOf('*') !== -1 + } + ]; + + let domainToConfigureItem: IQuickPickItem | undefined = undefined; + if (domainToConfigure) { + domainToConfigureItem = { + type: 'item', + label: domainToConfigure, + picked: true, + description: localize('trustDomainAndOpenLink', 'Trust domain and open link') + }; + specialQuickPickItems.push(domainToConfigureItem); + } + + const quickPickItems: (IQuickPickItem | IQuickPickSeparator)[] = domainQuickPickItems.length === 0 + ? specialQuickPickItems + : [...specialQuickPickItems, { type: 'separator' }, ...domainQuickPickItems]; + + return quickInputService.pick(quickPickItems, { + canPickMany: true, + activeItem: domainToConfigureItem + }).then(result => { + if (result) { + const pickedDomains = result.map(r => r.label); + storageService.store('http.trustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL); + + return pickedDomains; + } + + return []; + }); +}; + export class ConfigureTrustedDomainsAction extends Action { static readonly ID = 'workbench.action.configureTrustedDomains'; @@ -50,45 +115,10 @@ export class ConfigureTrustedDomainsAction extends Action { } run(): Promise { - let trustedDomains: string[] = []; - try { - trustedDomains = JSON.parse(this.storageService.get('http.trustedDomains', StorageScope.GLOBAL, '[]')); - } catch (err) { } - - const quickPickItems: (IQuickPickItem | IQuickPickSeparator)[] = trustedDomains - .filter(d => d !== '*') - .map(d => { - return { - type: 'item', - label: d, - picked: true, - }; - }); - - quickPickItems.unshift({ - type: 'separator' - }); - quickPickItems.unshift({ - type: 'item', - label: 'Allow all links to be open without protection', - picked: trustedDomains.indexOf('*') !== -1 - }); - - return this.quickInputService.pick(quickPickItems, { - canPickMany: true - }).then(result => { - if (result) { - this.storageService.store('http.trustedDomains', JSON.stringify(result.map(r => r.label)), StorageScope.GLOBAL); - } - }); + return configureTrustedDomainsHandler(this.quickInputService, this.storageService); } } -Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( - new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), - 'Open URL', - localize('developer', 'Developer') -); Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( new SyncActionDescriptor( ConfigureTrustedDomainsAction, @@ -97,3 +127,16 @@ Registry.as(ActionExtensions.WorkbenchActions).registe ), 'Configure Trusted Domains' ); +CommandsRegistry.registerCommand({ + id: '_workbench.action.configureTrustedDomains', + description: { + description: 'Configure Trusted Domains', + args: [{ name: 'domainToConfigure', schema: { type: 'string' } }] + }, + handler: (accessor, domainToConfigure?: string) => { + const quickInputService = accessor.get(IQuickInputService); + const storageService = accessor.get(IStorageService); + + return configureTrustedDomainsHandler(quickInputService, storageService, domainToConfigure); + } +}); From e9c0427ac561677e46aa0f4a74737d246c7463ef Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Sun, 18 Aug 2019 22:09:51 -0700 Subject: [PATCH 336/613] Build errors and test failures --- .../editor/browser/services/openerService.ts | 2 +- .../browser/services/openerService.test.ts | 27 +++++++++---------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 7b30565fc123c..f41ec79d480ae 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -82,7 +82,7 @@ export class OpenerService implements IOpenerService { localize( 'openExternalLinkAt', 'Do you want {0} to open the external website?\n{1}', - this._productService.productConfiguration.nameShort, + this._productService.nameShort, resource.toString(true) ), [ diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index f42e3c399efdf..3e1ef9e80cbe3 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -50,30 +50,27 @@ suite('OpenerService', function () { function getDialogService() { return new class implements IDialogService { - _confirmInvoked = 0; - confirm = () => { - this._confirmInvoked++; + _showInvoked = 0; + show = () => { + this._showInvoked++; return Promise.resolve({} as any); } - get confirmInvoked() { return this._confirmInvoked; } + get confirmInvoked() { return this._showInvoked; } // Don't care _serviceBrand: any; - show = () => { + confirm = () => { return Promise.resolve({} as any); } }; } - function getProductService() { - return new class implements IProductService { - // Don't care - _serviceBrand: any; + function getProductService(): IProductService { + return new class { + nameShort: 'VS Code'; - productConfiguration = { - nameShort: 'VS Code' - } as any; - }; + _serviceBrand: any; + } as IProductService; } @@ -176,7 +173,7 @@ suite('OpenerService', function () { assert.equal(lastCommand!.args[1], true); }); - test('links are protected by dialog confirmation', function () { + test('links are protected by dialog.show', function () { const dialogService = getDialogService(); const openerService = new OpenerService( editorService, @@ -190,7 +187,7 @@ suite('OpenerService', function () { assert.equal(dialogService.confirmInvoked, 1); }); - test('links on the whitelisted domains can be opened without dialog confirmation', function () { + test('links on the whitelisted domains can be opened without dialog.show', function () { const dialogService = getDialogService(); const openerService = new OpenerService( editorService, From f0398e992f5d6b2f9919216bd0c8aa0b935a85e4 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Sun, 18 Aug 2019 22:25:54 -0700 Subject: [PATCH 337/613] :lipstick: --- src/vs/editor/browser/services/openerService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index f41ec79d480ae..c6aa9891cf5fd 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -159,7 +159,6 @@ export class OpenerService implements IOpenerService { /** * Check whether a domain like https://www.microsoft.com matches * the list of trusted domains. - * */ function isDomainTrusted(domain: string, trustedDomains: string[]) { for (let i = 0; i < trustedDomains.length; i++) { From 6865ffbcceec1545980f6f70545a3d1cff32ded7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Aug 2019 08:06:06 +0200 Subject: [PATCH 338/613] debt - fix some layer breakers --- src/vs/workbench/browser/dnd.ts | 2 +- .../workbench/contrib/terminal/browser/terminalPanel.ts | 2 +- .../contrib/terminal/common/terminalEnvironment.ts | 2 +- .../workbench/contrib/welcome/page/browser/welcomePage.ts | 2 +- .../walkThrough/browser/editor/editorWalkThrough.ts | 2 +- .../walkThrough/browser/walkThrough.contribution.ts | 4 ++-- .../walkThrough/{common => browser}/walkThroughInput.ts | 0 .../welcome/walkThrough/browser/walkThroughPart.ts | 2 +- .../services/extensions/node/extensionHostProcessSetup.ts | 1 + .../workbench/services/files/common/workspaceWatcher.ts | 8 +++++--- .../workbench/services/integrity/node/integrityService.ts | 6 ++++-- .../services/keybinding/browser/keybindingService.ts | 2 +- .../services/keybinding/browser/keymapService.ts | 2 +- .../keybinding/{common => browser}/navigatorKeyboard.ts | 0 .../services/userData/common/inMemoryUserDataProvider.ts | 5 ++--- 15 files changed, 22 insertions(+), 18 deletions(-) rename src/vs/workbench/contrib/welcome/walkThrough/{common => browser}/walkThroughInput.ts (100%) rename src/vs/workbench/services/keybinding/{common => browser}/navigatorKeyboard.ts (100%) diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index cf1d6b5b60f46..b99f800164d1f 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -113,7 +113,7 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array r.resource.fsPath === file.path) /* prevent duplicates */) { + if (file && file.path /* Electron only */ && !resources.some(r => r.resource.fsPath === file.path) /* prevent duplicates */) { try { resources.push({ resource: URI.file(file.path), isExternal: true }); } catch (error) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 334c3ab0b1352..5c3eae374efb5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -276,7 +276,7 @@ export class TerminalPanel extends Panel { const resources = e.dataTransfer.getData(DataTransfers.RESOURCES); if (resources) { path = URI.parse(JSON.parse(resources)[0]).fsPath; - } else if (e.dataTransfer.files.length > 0) { + } else if (e.dataTransfer.files.length > 0 && e.dataTransfer.files[0].path /* Electron only */) { // Check if the file was dragged from the filesystem path = URI.file(e.dataTransfer.files[0].path).fsPath; } diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index e6936280a4603..6b9a3ec87b742 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -62,7 +62,7 @@ export function addTerminalEnvironmentKeys(env: platform.IProcessEnvironment, ve env['COLORTERM'] = 'truecolor'; } -function mergeNonNullKeys(env: platform.IProcessEnvironment, other: ITerminalEnvironment | NodeJS.ProcessEnv | undefined) { +function mergeNonNullKeys(env: platform.IProcessEnvironment, other: ITerminalEnvironment | undefined) { if (!other) { return; } diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index ab0d4193c4049..f549552738b0b 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import * as strings from 'vs/base/common/strings'; import { ICommandService } from 'vs/platform/commands/common/commands'; import * as arrays from 'vs/base/common/arrays'; -import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput'; +import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts index 7a95ffda9e419..8493b87f2cdf9 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts @@ -8,7 +8,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { Action } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; -import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput'; +import { WalkThroughInput, WalkThroughInputOptions } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { Schemas } from 'vs/base/common/network'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts index 9eed29a774f4c..8a91091c6b706 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput'; +import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { WalkThroughPart } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart'; import { WalkThroughArrowUp, WalkThroughArrowDown, WalkThroughPageUp, WalkThroughPageDown } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughActions'; import { WalkThroughContentProvider, WalkThroughSnippetContentProvider } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider'; @@ -55,4 +55,4 @@ MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { title: localize({ key: 'miInteractivePlayground', comment: ['&& denotes a mnemonic'] }, "I&&nteractive Playground") }, order: 2 -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput.ts similarity index 100% rename from src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts rename to src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput.ts diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index afd74dc859ba9..71faece75fd57 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -12,7 +12,7 @@ import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/com import { EditorOptions, IEditorMemento } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput'; +import { WalkThroughInput } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughInput'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import * as marked from 'vs/base/common/marked/marked'; import { IModelService } from 'vs/editor/common/services/modelService'; diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index 1e88709312390..8b5028acac6be 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -62,6 +62,7 @@ function patchProcess(allowExit: boolean) { } } as (code?: number) => never; + // override Electron's process.crash() method process.crash = function () { const err = new Error('An extension called process.crash() and this was prevented.'); console.warn(err.stack); diff --git a/src/vs/workbench/services/files/common/workspaceWatcher.ts b/src/vs/workbench/services/files/common/workspaceWatcher.ts index 46badeabf8069..1f9049858d3a7 100644 --- a/src/vs/workbench/services/files/common/workspaceWatcher.ts +++ b/src/vs/workbench/services/files/common/workspaceWatcher.ts @@ -16,6 +16,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { INotificationService, Severity, NeverShowAgainScope } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; import { FileService } from 'vs/platform/files/common/fileService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; export class WorkspaceWatcher extends Disposable { @@ -25,7 +26,8 @@ export class WorkspaceWatcher extends Disposable { @IFileService private readonly fileService: FileService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @INotificationService private readonly notificationService: INotificationService + @INotificationService private readonly notificationService: INotificationService, + @IOpenerService private readonly openerService: IOpenerService ) { super(); @@ -77,7 +79,7 @@ export class WorkspaceWatcher extends Disposable { localize('netVersionError', "The Microsoft .NET Framework 4.5 is required. Please follow the link to install it."), [{ label: localize('installNet', "Download .NET Framework 4.5"), - run: () => window.open('https://go.microsoft.com/fwlink/?LinkId=786533') + run: () => this.openerService.openExternal(URI.parse('https://go.microsoft.com/fwlink/?LinkId=786533')) }], { sticky: true, @@ -93,7 +95,7 @@ export class WorkspaceWatcher extends Disposable { localize('enospcError', "Unable to watch for file changes in this large workspace. Please follow the instructions link to resolve this issue."), [{ label: localize('learnMore', "Instructions"), - run: () => window.open('https://go.microsoft.com/fwlink/?linkid=867693') + run: () => this.openerService.openExternal(URI.parse('https://go.microsoft.com/fwlink/?linkid=867693')) }], { sticky: true, diff --git a/src/vs/workbench/services/integrity/node/integrityService.ts b/src/vs/workbench/services/integrity/node/integrityService.ts index 021fb9543b545..81f5317fe99fa 100644 --- a/src/vs/workbench/services/integrity/node/integrityService.ts +++ b/src/vs/workbench/services/integrity/node/integrityService.ts @@ -15,6 +15,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; interface IStorageData { dontShowPrompt: boolean; @@ -64,7 +65,8 @@ export class IntegrityServiceImpl implements IIntegrityService { constructor( @INotificationService private readonly notificationService: INotificationService, @IStorageService storageService: IStorageService, - @ILifecycleService private readonly lifecycleService: ILifecycleService + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IOpenerService private readonly openerService: IOpenerService ) { this._storage = new IntegrityStorage(storageService); @@ -91,7 +93,7 @@ export class IntegrityServiceImpl implements IIntegrityService { [ { label: nls.localize('integrity.moreInformation', "More Information"), - run: () => window.open(URI.parse(product.checksumFailMoreInfoUrl).toString(true)) + run: () => this.openerService.openExternal(URI.parse(product.checksumFailMoreInfoUrl)) }, { label: nls.localize('integrity.dontShowAgain', "Don't Show Again"), diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index c9394b52fc833..b90651c4f3362 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -45,7 +45,7 @@ import * as objects from 'vs/base/common/objects'; import { IKeymapService } from 'vs/workbench/services/keybinding/common/keymapInfo'; import { getDispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; import { isArray } from 'vs/base/common/types'; -import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/common/navigatorKeyboard'; +import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/browser/navigatorKeyboard'; import { ScanCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/base/common/scanCode'; interface ContributedKeyBinding { diff --git a/src/vs/workbench/services/keybinding/browser/keymapService.ts b/src/vs/workbench/services/keybinding/browser/keymapService.ts index faacc11fbf5e2..c90e58fc0a967 100644 --- a/src/vs/workbench/services/keybinding/browser/keymapService.ts +++ b/src/vs/workbench/services/keybinding/browser/keymapService.ts @@ -25,7 +25,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ConfigExtensions, IConfigurationRegistry, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/common/navigatorKeyboard'; +import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/browser/navigatorKeyboard'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IStorageService } from 'vs/platform/storage/common/storage'; diff --git a/src/vs/workbench/services/keybinding/common/navigatorKeyboard.ts b/src/vs/workbench/services/keybinding/browser/navigatorKeyboard.ts similarity index 100% rename from src/vs/workbench/services/keybinding/common/navigatorKeyboard.ts rename to src/vs/workbench/services/keybinding/browser/navigatorKeyboard.ts diff --git a/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts b/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts index ffed9c53d5325..622da2547a3f5 100644 --- a/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts +++ b/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts @@ -209,8 +209,7 @@ export class InMemoryUserDataProvider extends Disposable implements IFileSystemP readonly onDidChangeFile: Event = this._onDidChangeFile.event; private _bufferedChanges: IFileChange[] = []; - private _fireSoonHandle?: NodeJS.Timer; - + private _fireSoonHandle?: any; watch(resource: URI, opts: IWatchOptions): IDisposable { // ignore, fires for all changes... @@ -229,4 +228,4 @@ export class InMemoryUserDataProvider extends Disposable implements IFileSystemP this._bufferedChanges.length = 0; }, 5); } -} \ No newline at end of file +} From dd2a230a561f1b86e020e35dbc9324feb25dbf87 Mon Sep 17 00:00:00 2001 From: skprabhanjan Date: Mon, 19 Aug 2019 11:46:30 +0530 Subject: [PATCH 339/613] Added buildReplaceStringWithCasePreserved generic function to be used in both search side and the find side --- src/vs/base/common/search.ts | 23 ++++++++++++++++++ src/vs/editor/contrib/find/replacePattern.ts | 15 +++--------- .../contrib/find/test/replacePattern.test.ts | 24 +++++++++++++++++++ .../services/search/common/replace.ts | 15 +++--------- 4 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 src/vs/base/common/search.ts diff --git a/src/vs/base/common/search.ts b/src/vs/base/common/search.ts new file mode 100644 index 0000000000000..c18233d61b9dd --- /dev/null +++ b/src/vs/base/common/search.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as strings from './strings'; + +export function buildReplaceStringWithCasePreserved(matches: string[] | null, pattern: string): string { + if (matches && (matches[0] !== '')) { + if (matches[0].toUpperCase() === matches[0]) { + return pattern.toUpperCase(); + } else if (matches[0].toLowerCase() === matches[0]) { + return pattern.toLowerCase(); + } else if (strings.containsUppercaseCharacter(matches[0][0])) { + return pattern[0].toUpperCase() + pattern.substr(1); + } else { + // we don't understand its pattern yet. + return pattern; + } + } else { + return pattern; + } +} diff --git a/src/vs/editor/contrib/find/replacePattern.ts b/src/vs/editor/contrib/find/replacePattern.ts index 50e90d7f3eac6..12a05d93536fe 100644 --- a/src/vs/editor/contrib/find/replacePattern.ts +++ b/src/vs/editor/contrib/find/replacePattern.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from 'vs/base/common/charCode'; -import { containsUppercaseCharacter } from 'vs/base/common/strings'; +import { buildReplaceStringWithCasePreserved } from 'vs/base/common/search'; const enum ReplacePatternKind { StaticValue = 0, @@ -51,17 +51,8 @@ export class ReplacePattern { public buildReplaceString(matches: string[] | null, preserveCase?: boolean): string { if (this._state.kind === ReplacePatternKind.StaticValue) { - if (preserveCase && matches && (matches[0] !== '')) { - if (matches[0].toUpperCase() === matches[0]) { - return this._state.staticValue.toUpperCase(); - } else if (matches[0].toLowerCase() === matches[0]) { - return this._state.staticValue.toLowerCase(); - } else if (containsUppercaseCharacter(matches[0][0])) { - return this._state.staticValue[0].toUpperCase() + this._state.staticValue.substr(1); - } else { - // we don't understand its pattern yet. - return this._state.staticValue; - } + if (preserveCase) { + return buildReplaceStringWithCasePreserved(matches, this._state.staticValue); } else { return this._state.staticValue; } diff --git a/src/vs/editor/contrib/find/test/replacePattern.test.ts b/src/vs/editor/contrib/find/test/replacePattern.test.ts index d252ac74c6aa3..e4c78422b8fe3 100644 --- a/src/vs/editor/contrib/find/test/replacePattern.test.ts +++ b/src/vs/editor/contrib/find/test/replacePattern.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { ReplacePattern, ReplacePiece, parseReplaceString } from 'vs/editor/contrib/find/replacePattern'; +import { buildReplaceStringWithCasePreserved } from 'vs/base/common/search'; suite('Replace Pattern test', () => { @@ -154,6 +155,29 @@ suite('Replace Pattern test', () => { assert.equal(actual, 'a{}'); }); + test('buildReplaceStringWithCasePreserved test', () => { + let replacePattern = 'Def'; + let actual: string | string[] = 'abc'; + + assert.equal(buildReplaceStringWithCasePreserved([actual], replacePattern), 'def'); + actual = 'Abc'; + assert.equal(buildReplaceStringWithCasePreserved([actual], replacePattern), 'Def'); + actual = 'ABC'; + assert.equal(buildReplaceStringWithCasePreserved([actual], replacePattern), 'DEF'); + + actual = ['abc', 'Abc']; + assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'def'); + actual = ['Abc', 'abc']; + assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def'); + actual = ['ABC', 'abc']; + assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'DEF'); + + actual = ['AbC']; + assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def'); + actual = ['aBC']; + assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def'); + }); + test('preserve case', () => { let replacePattern = parseReplaceString('Def'); let actual = replacePattern.buildReplaceString(['abc'], true); diff --git a/src/vs/workbench/services/search/common/replace.ts b/src/vs/workbench/services/search/common/replace.ts index 3f58ea89fe0e1..99f610bbcf5ce 100644 --- a/src/vs/workbench/services/search/common/replace.ts +++ b/src/vs/workbench/services/search/common/replace.ts @@ -6,6 +6,7 @@ import * as strings from 'vs/base/common/strings'; import { IPatternInfo } from 'vs/workbench/services/search/common/search'; import { CharCode } from 'vs/base/common/charCode'; +import { buildReplaceStringWithCasePreserved } from 'vs/base/common/search'; export class ReplacePattern { @@ -72,18 +73,8 @@ export class ReplacePattern { } public buildReplaceString(matches: string[] | null, preserveCase?: boolean): string { - - if (preserveCase && matches && (matches[0] !== '')) { - if (matches[0].toUpperCase() === matches[0]) { - return this._replacePattern.toUpperCase(); - } else if (matches[0].toLowerCase() === matches[0]) { - return this._replacePattern.toLowerCase(); - } else if (strings.containsUppercaseCharacter(matches[0][0])) { - return this._replacePattern[0].toUpperCase() + this._replacePattern.substr(1); - } else { - // we don't understand its pattern yet. - return this._replacePattern; - } + if (preserveCase) { + return buildReplaceStringWithCasePreserved(matches, this._replacePattern); } else { return this._replacePattern; } From 6f5198cfb4bee67961e0aad7401e28c224c9dd02 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Aug 2019 08:41:58 +0200 Subject: [PATCH 340/613] :lipstick: imports --- src/vs/workbench/contrib/debug/browser/linkDetector.ts | 2 +- src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts | 2 +- tslint.json | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/linkDetector.ts b/src/vs/workbench/contrib/debug/browser/linkDetector.ts index 58cbb394e5f1e..df911b7d7c9f3 100644 --- a/src/vs/workbench/contrib/debug/browser/linkDetector.ts +++ b/src/vs/workbench/contrib/debug/browser/linkDetector.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import strings = require('vs/base/common/strings'); +import * as strings from 'vs/base/common/strings'; import { isAbsolute } from 'vs/base/common/path'; import { URI as uri } from 'vs/base/common/uri'; import { isMacintosh } from 'vs/base/common/platform'; diff --git a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts index 18c24058910eb..46a2661d1ffe6 100644 --- a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts +++ b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts @@ -7,7 +7,7 @@ import * as platform from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; import { ITerminalInstance, IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { Terminal as XTermTerminal } from 'xterm'; -import WindowsProcessTreeType = require('windows-process-tree'); +import * as WindowsProcessTreeType from 'windows-process-tree'; import { Disposable } from 'vs/base/common/lifecycle'; const SHELL_EXECUTABLES = [ diff --git a/tslint.json b/tslint.json index 4b23918f0fcdf..059d2cea868d2 100644 --- a/tslint.json +++ b/tslint.json @@ -5,6 +5,7 @@ "rules": { "no-arg": true, "no-construct": true, + "no-debugger": true, "no-duplicate-super": true, "no-duplicate-switch-case": true, "no-duplicate-variable": true, From b0a495e8663a3127c9845af07021c0c5247afbf7 Mon Sep 17 00:00:00 2001 From: DiamondYuan Date: Mon, 19 Aug 2019 17:17:45 +0800 Subject: [PATCH 341/613] refactor: GlobalStorageDatabaseChannel should dependents in IStorageMainService (#79387) --- src/vs/code/electron-main/app.ts | 2 +- src/vs/platform/storage/node/storageIpc.ts | 6 +++--- src/vs/platform/storage/node/storageMainService.ts | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index f60f4f73c44c2..1953c269a6b3a 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -560,7 +560,7 @@ export class CodeApplication extends Disposable { electronIpcServer.registerChannel('url', urlChannel); const storageMainService = accessor.get(IStorageMainService); - const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService as StorageMainService)); + const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService)); electronIpcServer.registerChannel('storage', storageChannel); // Log level management diff --git a/src/vs/platform/storage/node/storageIpc.ts b/src/vs/platform/storage/node/storageIpc.ts index aaba475a2695f..8ed04953e41ca 100644 --- a/src/vs/platform/storage/node/storageIpc.ts +++ b/src/vs/platform/storage/node/storageIpc.ts @@ -5,7 +5,7 @@ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event, Emitter } from 'vs/base/common/event'; -import { StorageMainService, IStorageChangeEvent } from 'vs/platform/storage/node/storageMainService'; +import { IStorageChangeEvent, IStorageMainService } from 'vs/platform/storage/node/storageMainService'; import { IUpdateRequest, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/parts/storage/common/storage'; import { mapToSerializable, serializableToMap, values } from 'vs/base/common/map'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -38,7 +38,7 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC constructor( private logService: ILogService, - private storageMainService: StorageMainService + private storageMainService: IStorageMainService ) { super(); @@ -203,4 +203,4 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS dispose(this.onDidChangeItemsOnMainListener); } -} \ No newline at end of file +} diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts index d1f06b6ecb200..e1bb217356619 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/node/storageMainService.ts @@ -34,6 +34,10 @@ export interface IStorageMainService { */ readonly onWillSaveState: Event; + readonly items: Map; + + initialize(): Promise; + /** * Retrieve an element stored with the given key from storage. Use * the provided defaultValue if the element is null or undefined. From 84dc5c035c31d35fd0c76c4a8b6d6339e3a45371 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Aug 2019 11:18:47 +0200 Subject: [PATCH 342/613] :lipstick: --- src/vs/platform/storage/node/storageMainService.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts index e1bb217356619..2f2dd9e3bd2df 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/node/storageMainService.ts @@ -34,8 +34,14 @@ export interface IStorageMainService { */ readonly onWillSaveState: Event; + /** + * Access to all cached items of this storage service. + */ readonly items: Map; + /** + * Required call to ensure the service can be used. + */ initialize(): Promise; /** From 3e25e2688f016bb9ee1e0ae20f689f1c25dd5fcc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 19 Aug 2019 11:38:01 +0200 Subject: [PATCH 343/613] build - have different worker extension host entrypoint, copy worker bootstrap file --- build/gulpfile.vscode.web.js | 3 +++ src/buildfile.js | 8 +------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index 81e08f8e0a659..c09f27252dd4b 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -41,6 +41,9 @@ const vscodeWebResources = [ // Webview 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', + // Extension Worker + 'vs/workbench/services/extensions/worker/extensionHostWorkerMain.js', + // Excludes '!out-build/vs/**/{node,electron-browser,electron-main}/**', '!out-build/vs/editor/standalone/**', diff --git a/src/buildfile.js b/src/buildfile.js index 27928e946ebd7..746975249eda4 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -23,13 +23,7 @@ exports.serviceWorker = [{ dest: 'vs/workbench/contrib/resources/browser/resourceServiceWorkerMain.js' }]; -exports.workerExtensionHost = [{ - name: 'vs/workbench/services/extensions/worker/extensionHostWorker', - // include: [], - prepend: ['vs/loader.js', 'vs/nls.js'], - append: ['vs/workbench/services/extensions/worker/extensionHostWorkerMain'], - dest: 'vs/workbench/services/extensions/worker/extensionHostWorkerMain.js' -}]; +exports.workerExtensionHost = [entrypoint('vs/workbench/services/extensions/worker/extensionHostWorker')]; exports.workbench = require('./vs/workbench/buildfile').collectModules(['vs/workbench/workbench.desktop.main']); exports.workbenchWeb = entrypoint('vs/workbench/workbench.web.api'); From 1b16063cca17e19af49dfd006c400ec360b9d113 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Aug 2019 11:54:37 +0200 Subject: [PATCH 344/613] fix #76573 --- src/vs/workbench/browser/labels.ts | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 9db4979a3aaa8..7216a12b82f5b 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -317,40 +317,31 @@ class ResourceLabelWidget extends IconLabel { } notifyFormattersChange(): void { - if (this.label && this.label.resource) { - this.setFile(this.label.resource, this.options); - } this.render(false); } setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void { - const hasResourceChanged = this.hasResourceChanged(label, options); - this.label = label; this.options = options; - if (hasResourceChanged) { + if (this.hasPathLabelChanged(label, options)) { this.computedPathLabel = undefined; // reset path label due to resource change } - this.render(hasResourceChanged); + this.render(this.clearIconCache(label, options)); } - private hasResourceChanged(label: IResourceLabelProps, options?: IResourceLabelOptions): boolean { - const newResource = label ? label.resource : undefined; + private clearIconCache(newLabel: IResourceLabelProps, newOptions?: IResourceLabelOptions): boolean { + const newResource = newLabel ? newLabel.resource : undefined; const oldResource = this.label ? this.label.resource : undefined; - const newFileKind = options ? options.fileKind : undefined; + const newFileKind = newOptions ? newOptions.fileKind : undefined; const oldFileKind = this.options ? this.options.fileKind : undefined; if (newFileKind !== oldFileKind) { return true; // same resource but different kind (file, folder) } - if (newResource && this.computedPathLabel !== this.labelService.getUriLabel(newResource)) { - return true; - } - if (newResource && oldResource) { return newResource.toString() !== oldResource.toString(); } @@ -362,6 +353,12 @@ class ResourceLabelWidget extends IconLabel { return true; } + private hasPathLabelChanged(newLabel: IResourceLabelProps, newOptions?: IResourceLabelOptions): boolean { + const newResource = newLabel ? newLabel.resource : undefined; + + return !!newResource && this.computedPathLabel !== this.labelService.getUriLabel(newResource); + } + setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void { this.setResource({ resource: toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }), From de881573e78d712b66edef59191e6122133f21de Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 19 Aug 2019 12:22:06 +0200 Subject: [PATCH 345/613] fix path --- build/gulpfile.vscode.web.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index c09f27252dd4b..8f63d7b7d0bf9 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -42,7 +42,7 @@ const vscodeWebResources = [ 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', // Extension Worker - 'vs/workbench/services/extensions/worker/extensionHostWorkerMain.js', + 'out-build/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js', // Excludes '!out-build/vs/**/{node,electron-browser,electron-main}/**', From 7226b82a187ca84ed98c43e556b41aee2c9387bf Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 19 Aug 2019 12:41:20 +0200 Subject: [PATCH 346/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2a43502df641e..e01ec81fc9710 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "481202cd856f5dbbb010996544e4fa7498154f2e", + "distro": "79dcce2b3cd0772344a028cd703b7da03f3ffa83", "author": { "name": "Microsoft Corporation" }, From 815f7b05dd3fe25965863cad6814d0acabd3c7d6 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 19 Aug 2019 13:01:21 +0200 Subject: [PATCH 347/613] Localization test fails on remote smoketest. Fixes #78412 --- test/smoke/src/application.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/application.ts b/test/smoke/src/application.ts index 874a17421b2c7..60ed794ae9a4a 100644 --- a/test/smoke/src/application.ts +++ b/test/smoke/src/application.ts @@ -142,7 +142,7 @@ export class Application { await this.code.waitForElement('.monaco-workbench'); if (this.remote) { - await this.code.waitForElement('.monaco-workbench .statusbar-item[title="Editing on TestResolver"]'); + await this.code.waitForElement('.monaco-workbench .statusbar-item[id="status.host"]'); } // wait a bit, since focus might be stolen off widgets From 57379e0f97931249a88f483bb63128c7848e8c2c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 19 Aug 2019 14:19:06 +0200 Subject: [PATCH 348/613] Fix microsoft/vscode-remote-release/issues/1027 --- .../download/common/downloadService.ts | 2 +- .../common/extensionManagement.ts | 1 + .../common/extensionManagementIpc.ts | 7 +++- .../node/extensionManagementService.ts | 8 +++- .../common/extensionManagementService.ts | 39 +++++++++++++++++-- .../node/extensionManagementService.ts | 32 ++++++--------- 6 files changed, 61 insertions(+), 28 deletions(-) diff --git a/src/vs/platform/download/common/downloadService.ts b/src/vs/platform/download/common/downloadService.ts index a2b8365df4c91..54d2f1a98357d 100644 --- a/src/vs/platform/download/common/downloadService.ts +++ b/src/vs/platform/download/common/downloadService.ts @@ -20,7 +20,7 @@ export class DownloadService implements IDownloadService { ) { } async download(resource: URI, target: URI, cancellationToken: CancellationToken = CancellationToken.None): Promise { - if (resource.scheme === Schemas.file) { + if (resource.scheme === Schemas.file || resource.scheme === Schemas.vscodeRemote) { await this.fileService.copy(resource, target); return; } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index b075553498dc7..dd19996782dfe 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -195,6 +195,7 @@ export interface IExtensionManagementService { zip(extension: ILocalExtension): Promise; unzip(zipLocation: URI, type: ExtensionType): Promise; + getManifest(vsix: URI): Promise; install(vsix: URI): Promise; installFromGallery(extension: IGalleryExtension): Promise; uninstall(extension: ILocalExtension, force?: boolean): Promise; diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index b88350f44c714..349223b9210d1 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -9,7 +9,7 @@ import { Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IURITransformer, DefaultURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc'; import { cloneAndChange } from 'vs/base/common/objects'; -import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI { return URI.revive(transformer ? transformer.transformIncoming(uri) : uri); @@ -62,6 +62,7 @@ export class ExtensionManagementChannel implements IServerChannel { case 'zip': return this.service.zip(transformIncomingExtension(args[0], uriTransformer)).then(uri => transformOutgoingURI(uri, uriTransformer)); case 'unzip': return this.service.unzip(transformIncomingURI(args[0], uriTransformer), args[1]); case 'install': return this.service.install(transformIncomingURI(args[0], uriTransformer)); + case 'getManifest': return this.service.getManifest(transformIncomingURI(args[0], uriTransformer)); case 'installFromGallery': return this.service.installFromGallery(args[0]); case 'uninstall': return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), args[1]); case 'reinstallFromGallery': return this.service.reinstallFromGallery(transformIncomingExtension(args[0], uriTransformer)); @@ -99,6 +100,10 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer return Promise.resolve(this.channel.call('install', [vsix])).then(local => transformIncomingExtension(local, null)); } + getManifest(vsix: URI): Promise { + return Promise.resolve(this.channel.call('getManifest', [vsix])); + } + installFromGallery(extension: IGalleryExtension): Promise { return Promise.resolve(this.channel.call('installFromGallery', [extension])).then(local => transformIncomingExtension(local, null)); } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 70cd46c824a20..7d0b568fc7f75 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -161,6 +161,12 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.install(zipLocation, type).then(local => local.identifier); } + async getManifest(vsix: URI): Promise { + const downloadLocation = await this.downloadVsix(vsix); + const zipPath = path.resolve(downloadLocation.fsPath); + return getManifest(zipPath); + } + private collectFiles(extension: ILocalExtension): Promise { const collectFilesFromDirectory = async (dir: string): Promise => { @@ -983,4 +989,4 @@ export class ExtensionManagementService extends Disposable implements IExtension */ this.telemetryService.publicLog(eventName, assign(extensionData, { success: !error, duration, errorcode })); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index 337f52b278a1c..f1831bdbc19b4 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -8,7 +8,7 @@ import { IExtensionManagementService, ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { ExtensionType, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, isLanguagePackExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -17,6 +17,8 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens import { localize } from 'vs/nls'; import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IProductService } from 'vs/platform/product/common/product'; +import { Schemas } from 'vs/base/common/network'; +import { IDownloadService } from 'vs/platform/download/common/download'; export class ExtensionManagementService extends Disposable implements IExtensionManagementService { @@ -34,6 +36,7 @@ export class ExtensionManagementService extends Disposable implements IExtension @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @IConfigurationService protected readonly configurationService: IConfigurationService, @IProductService protected readonly productService: IProductService, + @IDownloadService protected readonly downloadService: IDownloadService, ) { super(); if (this.extensionManagementServerService.localExtensionManagementServer) { @@ -142,15 +145,43 @@ export class ExtensionManagementService extends Disposable implements IExtension } async install(vsix: URI): Promise { + if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { + const manifest = await this.getManifest(vsix); + if (isLanguagePackExtension(manifest)) { + // Install on both servers + const [local] = await Promise.all(this.servers.map(server => this.installVSIX(vsix, server))); + return local; + } + if (isUIExtension(manifest, this.productService, this.configurationService)) { + // Install only on local server + return this.installVSIX(vsix, this.extensionManagementServerService.localExtensionManagementServer); + } + // Install only on remote server + return this.installVSIX(vsix, this.extensionManagementServerService.remoteExtensionManagementServer); + } if (this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); + return this.installVSIX(vsix, this.extensionManagementServerService.localExtensionManagementServer); } if (this.extensionManagementServerService.remoteExtensionManagementServer) { - return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.install(vsix); + return this.installVSIX(vsix, this.extensionManagementServerService.remoteExtensionManagementServer); } return Promise.reject('No Servers to Install'); } + protected installVSIX(vsix: URI, server: IExtensionManagementServer): Promise { + return server.extensionManagementService.install(vsix); + } + + getManifest(vsix: URI): Promise { + if (vsix.scheme === Schemas.file && this.extensionManagementServerService.localExtensionManagementServer) { + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getManifest(vsix); + } + if (vsix.scheme === Schemas.vscodeRemote && this.extensionManagementServerService.remoteExtensionManagementServer) { + return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.getManifest(vsix); + } + return Promise.reject('No Servers'); + } + async installFromGallery(gallery: IGalleryExtension): Promise { if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None); @@ -191,4 +222,4 @@ export class ExtensionManagementService extends Disposable implements IExtension private getServer(extension: ILocalExtension): IExtensionManagementServer | null { return this.extensionManagementServerService.getExtensionManagementServer(extension.location); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/extensionManagement/node/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/node/extensionManagementService.ts index b2b5e78f90e09..143026c2b91e9 100644 --- a/src/vs/workbench/services/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/node/extensionManagementService.ts @@ -3,35 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { tmpdir } from 'os'; +import { generateUuid } from 'vs/base/common/uuid'; import { ILocalExtension, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; -import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; -import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { ExtensionManagementService as BaseExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { Schemas } from 'vs/base/common/network'; +import * as path from 'vs/base/common/path'; export class ExtensionManagementService extends BaseExtensionManagementService { - async install(vsix: URI): Promise { - if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { - const manifest = await getManifest(vsix.fsPath); - if (isLanguagePackExtension(manifest)) { - // Install on both servers - const [local] = await Promise.all(this.servers.map(server => server.extensionManagementService.install(vsix))); - return local; - } - if (isUIExtension(manifest, this.productService, this.configurationService)) { - // Install only on local server - return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); - } - // Install only on remote server - return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.install(vsix); + protected async installVSIX(vsix: URI, server: IExtensionManagementServer): Promise { + if (vsix.scheme === Schemas.vscodeRemote && server === this.extensionManagementServerService.localExtensionManagementServer) { + const downloadedLocation = URI.file(path.join(tmpdir(), generateUuid())); + await this.downloadService.download(vsix, downloadedLocation); + vsix = downloadedLocation; } - if (this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); - } - return Promise.reject('No Servers to Install'); + return server.extensionManagementService.install(vsix); } } From c8d44e24dd250d416475a10efe64e35c9e2c65b3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 19 Aug 2019 14:11:46 +0200 Subject: [PATCH 349/613] fix scope when falling back to original load function --- src/vs/workbench/api/common/extHostRequireInterceptor.ts | 8 ++++---- src/vs/workbench/api/node/extHostExtensionService.ts | 6 +++++- src/vs/workbench/api/worker/extHostExtensionService.ts | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts index ad16a75c56bde..46866f7db08d0 100644 --- a/src/vs/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -21,12 +21,12 @@ import { platform } from 'vs/base/common/process'; interface LoadFunction { - (request: string, parent: { filename: string; }, isMain: any): any; + (request: string): any; } interface INodeModuleFactory { readonly nodeModuleName: string | string[]; - load(request: string, parent: URI, isMain: any, original: LoadFunction): any; + load(request: string, parent: URI, original: LoadFunction): any; alternativeModuleName?(name: string): string | undefined; } @@ -245,7 +245,7 @@ class OpenNodeModuleFactory implements INodeModuleFactory { }; } - public load(request: string, parent: URI, isMain: any, original: LoadFunction): any { + public load(request: string, parent: URI, original: LoadFunction): any { // get extension id from filename and api for extension const extension = this._extensionPaths.findSubstr(parent.fsPath); if (extension) { @@ -253,7 +253,7 @@ class OpenNodeModuleFactory implements INodeModuleFactory { this.sendShimmingTelemetry(); } - this._original = original(request, { filename: parent.fsPath }, isMain); + this._original = original(request); return this._impl; } diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 10161ac3355ef..8e96e6738ec20 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -31,7 +31,11 @@ class NodeModuleRequireInterceptor extends RequireInterceptor { if (!that._factories.has(request)) { return original.apply(this, arguments); } - return that._factories.get(request)!.load(request, URI.file(parent.filename), isMain, original); + return that._factories.get(request)!.load( + request, + URI.file(parent.filename), + request => original.apply(this, [request, parent, isMain]) + ); }; } } diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index fdc57cbb2ec1e..a40ce65b7bf26 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -89,7 +89,7 @@ class WorkerRequireInterceptor extends RequireInterceptor { } if (this._factories.has(request)) { - return this._factories.get(request)!.load(request, parent, false, () => { throw new Error(); }); + return this._factories.get(request)!.load(request, parent, () => { throw new Error('CANNOT LOAD MODULE from here.'); }); } return undefined; } From 21821141a653378e263448c2954a3384ccce5bee Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 19 Aug 2019 06:40:15 -0700 Subject: [PATCH 350/613] Wording update --- src/vs/workbench/contrib/url/common/url.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index ef5eeb6a96aa3..b8f67e2e5989a 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -65,7 +65,7 @@ const configureTrustedDomainsHandler = ( const specialQuickPickItems: IQuickPickItem[] = [ { type: 'item', - label: localize('allowAllLinks', 'Allow all links to be open without protection'), + label: localize('openAllLinksWithoutPrompt', 'Open all links without prompt'), picked: trustedDomains.indexOf('*') !== -1 } ]; From 34339f92c435a48f49f4e04eee8feeda86925f84 Mon Sep 17 00:00:00 2001 From: Salvador Cabrera Date: Mon, 19 Aug 2019 09:35:06 -0500 Subject: [PATCH 351/613] Fix: Markdown Preview scroll remains same after clicking on some other link #78465 Improves the behavior on how markdown preview behaves when clicking a link --- .../markdown-language-features/media/index.js | 29 +++++++++++++++---- .../preview-src/index.ts | 23 ++++++++++----- .../preview-src/scroll-sync.ts | 9 ++++++ .../src/features/preview.ts | 12 +++++++- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/extensions/markdown-language-features/media/index.js b/extensions/markdown-language-features/media/index.js index 00afdf196af75..9a0fd91b45a61 100644 --- a/extensions/markdown-language-features/media/index.js +++ b/extensions/markdown-language-features/media/index.js @@ -659,10 +659,20 @@ window.onload = () => { events_1.onceDocumentLoaded(() => { if (settings.scrollPreviewWithEditor) { setTimeout(() => { - const initialLine = +settings.line; - if (!isNaN(initialLine)) { - scrollDisabled = true; - scroll_sync_1.scrollToRevealSourceLine(initialLine); + // Try to scroll to fragment if available + if (state.fragment) { + const element = scroll_sync_1.getLineElementForFragment(state.fragment); + if (element) { + scrollDisabled = true; + scroll_sync_1.scrollToRevealSourceLine(element.line); + } + } + else { + const initialLine = +settings.line; + if (!isNaN(initialLine)) { + scrollDisabled = true; + scroll_sync_1.scrollToRevealSourceLine(initialLine); + } } }, 0); } @@ -940,6 +950,15 @@ function getEditorLineNumberForPageOffset(offset) { return null; } exports.getEditorLineNumberForPageOffset = getEditorLineNumberForPageOffset; +/** + * Try to find the html element by using a fragment id + */ +function getLineElementForFragment(fragment) { + return getCodeLineElements().find((element) => { + return element.element.id === fragment; + }); +} +exports.getLineElementForFragment = getLineElementForFragment; /***/ }), @@ -986,4 +1005,4 @@ exports.getSettings = getSettings; /***/ }) /******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsT0FBTztBQUNsQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLDhDQUE4QyxrQkFBa0I7QUFDaEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtREFBbUQsb0JBQW9CO0FBQ3ZFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsRUFBRTtBQUNiLGFBQWEsUUFBUTtBQUNyQjtBQUNBO0FBQ0Esb0JBQW9CO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVyxFQUFFO0FBQ2IsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOzs7Ozs7Ozs7Ozs7O0FDdGJBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNENBQTRDOztBQUU1Qzs7Ozs7Ozs7Ozs7Ozs7O0FDbkJBOzs7Z0dBR2dHO0FBQ2hHLCtGQUF5RDtBQUV6RCxNQUFhLGdCQUFnQjtJQUc1Qiw4QkFBOEIsQ0FBQyxJQUFZO1FBQzFDLE1BQU0sRUFBRSxRQUFRLEVBQUUsR0FBRyxzQ0FBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNwRCxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsSUFBSSxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVELE9BQU8sQ0FBQyxNQUErQjtRQUN0QyxJQUFJLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3pDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNoQyxJQUFJLENBQUMsUUFBUSxHQUFHLE1BQU0sQ0FBQztJQUN4QixDQUFDO0lBRUQsb0JBQW9CLENBQUMsT0FBZ0M7UUFDcEQsSUFBSSxDQUFDLE9BQU8sRUFBRTtZQUNiLE9BQU87U0FDUDtRQUNELE9BQU8sQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsdUJBQXVCLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDNUUsQ0FBQztJQUVELGtCQUFrQixDQUFDLE9BQWdDO1FBQ2xELElBQUksQ0FBQyxPQUFPLEVBQUU7WUFDYixPQUFPO1NBQ1A7UUFDRCxPQUFPLENBQUMsU0FBUyxJQUFJLG1CQUFtQixDQUFDO0lBQzFDLENBQUM7Q0FDRDtBQTNCRCw0Q0EyQkM7Ozs7Ozs7Ozs7Ozs7O0FDakNEOzs7Z0dBR2dHOztBQUVoRyxTQUFnQixrQkFBa0IsQ0FBQyxDQUFhO0lBQy9DLElBQUksUUFBUSxDQUFDLFVBQVUsS0FBSyxTQUFTLElBQUksUUFBUSxDQUFDLFVBQW9CLEtBQUssZUFBZSxFQUFFO1FBQzNGLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxrQkFBa0IsRUFBRSxDQUFDLENBQUMsQ0FBQztLQUNqRDtTQUFNO1FBQ04sQ0FBQyxFQUFFLENBQUM7S0FDSjtBQUNGLENBQUM7QUFORCxnREFNQzs7Ozs7Ozs7Ozs7Ozs7QUNYRDs7O2dHQUdnRzs7QUFFaEcsOEdBQXNEO0FBQ3RELGdGQUE4QztBQUM5Qyx5RkFBb0Q7QUFDcEQsK0ZBQTJGO0FBQzNGLHNGQUFrRDtBQUNsRCx1R0FBNkM7QUFJN0MsSUFBSSxjQUFjLEdBQUcsSUFBSSxDQUFDO0FBQzFCLE1BQU0sTUFBTSxHQUFHLElBQUksbUNBQWdCLEVBQUUsQ0FBQztBQUN0QyxNQUFNLFFBQVEsR0FBRyxzQkFBVyxFQUFFLENBQUM7QUFFL0IsTUFBTSxNQUFNLEdBQUcsZ0JBQWdCLEVBQUUsQ0FBQztBQUVsQyxvQkFBb0I7QUFDcEIsSUFBSSxLQUFLLEdBQUcsa0JBQU8sQ0FBbUIsWUFBWSxDQUFDLENBQUM7QUFDcEQsTUFBTSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUV2QixNQUFNLFNBQVMsR0FBRyxpQ0FBcUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUVoRCxNQUFNLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUN2QyxNQUFNLENBQUMsbUJBQW1CLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0FBRWhELE1BQU0sQ0FBQyxNQUFNLEdBQUcsR0FBRyxFQUFFO0lBQ3BCLGdCQUFnQixFQUFFLENBQUM7QUFDcEIsQ0FBQyxDQUFDO0FBRUYsMkJBQWtCLENBQUMsR0FBRyxFQUFFO0lBQ3ZCLElBQUksUUFBUSxDQUFDLHVCQUF1QixFQUFFO1FBQ3JDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDZixNQUFNLFdBQVcsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7WUFDbkMsSUFBSSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsRUFBRTtnQkFDeEIsY0FBYyxHQUFHLElBQUksQ0FBQztnQkFDdEIsc0NBQXdCLENBQUMsV0FBVyxDQUFDLENBQUM7YUFDdEM7UUFDRixDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7S0FDTjtBQUNGLENBQUMsQ0FBQyxDQUFDO0FBRUgsTUFBTSxZQUFZLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDMUIsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLENBQUMsSUFBWSxFQUFFLEVBQUU7UUFDMUMsY0FBYyxHQUFHLElBQUksQ0FBQztRQUN0QixzQ0FBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNoQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFFUCxPQUFPLENBQUMsSUFBWSxFQUFFLFFBQWEsRUFBRSxFQUFFO1FBQ3RDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUU7WUFDakIsUUFBUSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7WUFDckIsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO1NBQ2Y7SUFDRixDQUFDLENBQUM7QUFDSCxDQUFDLENBQUMsRUFBRSxDQUFDO0FBRUwsSUFBSSxnQkFBZ0IsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFO0lBQ3BDLE1BQU0sU0FBUyxHQUFvRCxFQUFFLENBQUM7SUFDdEUsSUFBSSxNQUFNLEdBQUcsUUFBUSxDQUFDLG9CQUFvQixDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ2xELElBQUksTUFBTSxFQUFFO1FBQ1gsSUFBSSxDQUFDLENBQUM7UUFDTixLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFDbkMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBRXRCLElBQUksR0FBRyxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEVBQUU7Z0JBQ3RDLEdBQUcsQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDO2FBQ2hDO1lBRUQsU0FBUyxDQUFDLElBQUksQ0FBQztnQkFDZCxFQUFFLEVBQUUsR0FBRyxDQUFDLEVBQUU7Z0JBQ1YsTUFBTSxFQUFFLEdBQUcsQ0FBQyxNQUFNO2dCQUNsQixLQUFLLEVBQUUsR0FBRyxDQUFDLEtBQUs7YUFDaEIsQ0FBQyxDQUFDO1NBQ0g7UUFFRCxTQUFTLENBQUMsV0FBVyxDQUFDLGlCQUFpQixFQUFFLFNBQVMsQ0FBQyxDQUFDO0tBQ3BEO0FBQ0YsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBRVAsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxHQUFHLEVBQUU7SUFDdEMsY0FBYyxHQUFHLElBQUksQ0FBQztJQUN0QixnQkFBZ0IsRUFBRSxDQUFDO0FBQ3BCLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUVULE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLEVBQUU7SUFDMUMsSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sS0FBSyxRQUFRLENBQUMsTUFBTSxFQUFFO1FBQzFDLE9BQU87S0FDUDtJQUVELFFBQVEsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUU7UUFDeEIsS0FBSyxnQ0FBZ0M7WUFDcEMsTUFBTSxDQUFDLDhCQUE4QixDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDdkQsTUFBTTtRQUVQLEtBQUssWUFBWTtZQUNoQixZQUFZLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFDeEMsTUFBTTtLQUNQO0FBQ0YsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDO0FBRVYsUUFBUSxDQUFDLGdCQUFnQixDQUFDLFVBQVUsRUFBRSxLQUFLLENBQUMsRUFBRTtJQUM3QyxJQUFJLENBQUMsUUFBUSxDQUFDLDJCQUEyQixFQUFFO1FBQzFDLE9BQU87S0FDUDtJQUVELHlCQUF5QjtJQUN6QixLQUFLLElBQUksSUFBSSxHQUFHLEtBQUssQ0FBQyxNQUFxQixFQUFFLElBQUksRUFBRSxJQUFJLEdBQUcsSUFBSSxDQUFDLFVBQXlCLEVBQUU7UUFDekYsSUFBSSxJQUFJLENBQUMsT0FBTyxLQUFLLEdBQUcsRUFBRTtZQUN6QixPQUFPO1NBQ1A7S0FDRDtJQUVELE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUM7SUFDM0IsTUFBTSxJQUFJLEdBQUcsOENBQWdDLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdEQsSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUU7UUFDN0MsU0FBUyxDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7S0FDOUQ7QUFDRixDQUFDLENBQUMsQ0FBQztBQUVILFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLEVBQUU7SUFDMUMsSUFBSSxDQUFDLEtBQUssRUFBRTtRQUNYLE9BQU87S0FDUDtJQUVELElBQUksSUFBSSxHQUFRLEtBQUssQ0FBQyxNQUFNLENBQUM7SUFDN0IsT0FBTyxJQUFJLEVBQUU7UUFDWixJQUFJLElBQUksQ0FBQyxPQUFPLElBQUksSUFBSSxDQUFDLE9BQU8sS0FBSyxHQUFHLElBQUksSUFBSSxDQUFDLElBQUksRUFBRTtZQUN0RCxJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxFQUFFO2dCQUM5QyxNQUFNO2FBQ047WUFDRCxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLGtCQUFrQixDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLG1CQUFtQixDQUFDLEVBQUU7Z0JBQ3RJLE1BQU0sQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsZ0NBQWdDLEVBQUUsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksTUFBTSxDQUFDLElBQUksWUFBWSxDQUFDLFFBQVEsQ0FBQyxtQkFBbUIsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDbEssU0FBUyxDQUFDLFdBQVcsQ0FBQyxXQUFXLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDdkQsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO2dCQUN2QixLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7Z0JBQ3hCLE1BQU07YUFDTjtZQUNELE1BQU07U0FDTjtRQUNELElBQUksR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDO0tBQ3ZCO0FBQ0YsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBRVQsSUFBSSxRQUFRLENBQUMsdUJBQXVCLEVBQUU7SUFDckMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQy9DLElBQUksY0FBYyxFQUFFO1lBQ25CLGNBQWMsR0FBRyxLQUFLLENBQUM7U0FDdkI7YUFBTTtZQUNOLE1BQU0sSUFBSSxHQUFHLDhDQUFnQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUM5RCxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRTtnQkFDN0MsU0FBUyxDQUFDLFdBQVcsQ0FBQyxZQUFZLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO2dCQUM5QyxLQUFLLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztnQkFDbEIsTUFBTSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQzthQUN2QjtTQUNEO0lBQ0YsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7Q0FDUjtBQUVELFNBQVMsWUFBWSxDQUFDLElBQVk7SUFDakMsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLDBCQUEwQixFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBQ3pELENBQUM7Ozs7Ozs7Ozs7Ozs7O0FDbktEOzs7Z0dBR2dHOztBQUVoRyxzRkFBeUM7QUFTNUIsNkJBQXFCLEdBQUcsQ0FBQyxNQUFXLEVBQUUsRUFBRTtJQUNwRCxPQUFPLElBQUk7UUFDVixXQUFXLENBQUMsSUFBWSxFQUFFLElBQVk7WUFDckMsTUFBTSxDQUFDLFdBQVcsQ0FBQztnQkFDbEIsSUFBSTtnQkFDSixNQUFNLEVBQUUsc0JBQVcsRUFBRSxDQUFDLE1BQU07Z0JBQzVCLElBQUk7YUFDSixDQUFDLENBQUM7UUFDSixDQUFDO0tBQ0QsQ0FBQztBQUNILENBQUMsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7QUN4QkY7OztnR0FHZ0c7O0FBRWhHLHNGQUF5QztBQUd6QyxTQUFTLEtBQUssQ0FBQyxHQUFXLEVBQUUsR0FBVyxFQUFFLEtBQWE7SUFDckQsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDO0FBQzVDLENBQUM7QUFFRCxTQUFTLFNBQVMsQ0FBQyxJQUFZO0lBQzlCLE9BQU8sS0FBSyxDQUFDLENBQUMsRUFBRSxzQkFBVyxFQUFFLENBQUMsU0FBUyxHQUFHLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUNwRCxDQUFDO0FBUUQsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLEdBQUcsRUFBRTtJQUNqQyxJQUFJLFFBQTJCLENBQUM7SUFDaEMsT0FBTyxHQUFHLEVBQUU7UUFDWCxJQUFJLENBQUMsUUFBUSxFQUFFO1lBQ2QsUUFBUSxHQUFHLENBQUMsRUFBRSxPQUFPLEVBQUUsUUFBUSxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUNqRCxLQUFLLE1BQU0sT0FBTyxJQUFJLFFBQVEsQ0FBQyxzQkFBc0IsQ0FBQyxXQUFXLENBQUMsRUFBRTtnQkFDbkUsTUFBTSxJQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLFdBQVcsQ0FBRSxDQUFDO2dCQUNqRCxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFO29CQUNqQixRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLE9BQXNCLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztpQkFDekQ7YUFDRDtTQUNEO1FBQ0QsT0FBTyxRQUFRLENBQUM7SUFDakIsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMOzs7OztHQUtHO0FBQ0gsU0FBZ0Isd0JBQXdCLENBQUMsVUFBa0I7SUFDMUQsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUMxQyxNQUFNLEtBQUssR0FBRyxtQkFBbUIsRUFBRSxDQUFDO0lBQ3BDLElBQUksUUFBUSxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUM7SUFDaEMsS0FBSyxNQUFNLEtBQUssSUFBSSxLQUFLLEVBQUU7UUFDMUIsSUFBSSxLQUFLLENBQUMsSUFBSSxLQUFLLFVBQVUsRUFBRTtZQUM5QixPQUFPLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUM7U0FDNUM7YUFBTSxJQUFJLEtBQUssQ0FBQyxJQUFJLEdBQUcsVUFBVSxFQUFFO1lBQ25DLE9BQU8sRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxDQUFDO1NBQ2pDO1FBQ0QsUUFBUSxHQUFHLEtBQUssQ0FBQztLQUNqQjtJQUNELE9BQU8sRUFBRSxRQUFRLEVBQUUsQ0FBQztBQUNyQixDQUFDO0FBYkQsNERBYUM7QUFFRDs7R0FFRztBQUNILFNBQWdCLDJCQUEyQixDQUFDLE1BQWM7SUFDekQsTUFBTSxLQUFLLEdBQUcsbUJBQW1CLEVBQUUsQ0FBQztJQUNwQyxNQUFNLFFBQVEsR0FBRyxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQztJQUN6QyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNaLElBQUksRUFBRSxHQUFHLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQzFCLE9BQU8sRUFBRSxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUU7UUFDbkIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUN0QyxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFDMUQsSUFBSSxNQUFNLENBQUMsR0FBRyxHQUFHLE1BQU0sQ0FBQyxNQUFNLElBQUksUUFBUSxFQUFFO1lBQzNDLEVBQUUsR0FBRyxHQUFHLENBQUM7U0FDVDthQUNJO1lBQ0osRUFBRSxHQUFHLEdBQUcsQ0FBQztTQUNUO0tBQ0Q7SUFDRCxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDNUIsTUFBTSxRQUFRLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO0lBQzNELElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxRQUFRLENBQUMsR0FBRyxHQUFHLFFBQVEsRUFBRTtRQUN2QyxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDNUIsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO0tBQ2hEO0lBQ0QsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsQ0FBQztBQUNoQyxDQUFDO0FBdEJELGtFQXNCQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0Isd0JBQXdCLENBQUMsSUFBWTtJQUNwRCxJQUFJLENBQUMsc0JBQVcsRUFBRSxDQUFDLHVCQUF1QixFQUFFO1FBQzNDLE9BQU87S0FDUDtJQUVELElBQUksSUFBSSxJQUFJLENBQUMsRUFBRTtRQUNkLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNqQyxPQUFPO0tBQ1A7SUFFRCxNQUFNLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxHQUFHLHdCQUF3QixDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzFELElBQUksQ0FBQyxRQUFRLEVBQUU7UUFDZCxPQUFPO0tBQ1A7SUFDRCxJQUFJLFFBQVEsR0FBRyxDQUFDLENBQUM7SUFDakIsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO0lBQ3RELE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUM7SUFDN0IsSUFBSSxJQUFJLElBQUksSUFBSSxDQUFDLElBQUksS0FBSyxRQUFRLENBQUMsSUFBSSxFQUFFO1FBQ3hDLDhEQUE4RDtRQUM5RCxNQUFNLGVBQWUsR0FBRyxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3RSxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUMsR0FBRyxHQUFHLFdBQVcsQ0FBQztRQUM3RSxRQUFRLEdBQUcsV0FBVyxHQUFHLGVBQWUsR0FBRyxhQUFhLENBQUM7S0FDekQ7U0FBTTtRQUNOLE1BQU0saUJBQWlCLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDbEQsUUFBUSxHQUFHLFdBQVcsR0FBRyxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsaUJBQWlCLENBQUMsQ0FBQztLQUMzRDtJQUNELE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsT0FBTyxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUM7QUFDdkUsQ0FBQztBQTNCRCw0REEyQkM7QUFFRCxTQUFnQixnQ0FBZ0MsQ0FBQyxNQUFjO0lBQzlELE1BQU0sRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEdBQUcsMkJBQTJCLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDL0QsSUFBSSxRQUFRLEVBQUU7UUFDYixNQUFNLGNBQWMsR0FBRyxRQUFRLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFDaEUsTUFBTSxrQkFBa0IsR0FBRyxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUMsT0FBTyxHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMxRSxJQUFJLElBQUksRUFBRTtZQUNULE1BQU0sdUJBQXVCLEdBQUcsa0JBQWtCLEdBQUcsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUMsR0FBRyxHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNySCxNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxHQUFHLHVCQUF1QixHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDbkYsT0FBTyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDdkI7YUFDSTtZQUNKLE1BQU0scUJBQXFCLEdBQUcsa0JBQWtCLEdBQUcsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDM0UsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksR0FBRyxxQkFBcUIsQ0FBQztZQUNuRCxPQUFPLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN2QjtLQUNEO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDYixDQUFDO0FBakJELDRFQWlCQzs7Ozs7Ozs7Ozs7Ozs7QUN2SUQ7OztnR0FHZ0c7O0FBYWhHLElBQUksY0FBYyxHQUFnQyxTQUFTLENBQUM7QUFFNUQsU0FBZ0IsT0FBTyxDQUFTLEdBQVc7SUFDMUMsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0lBQ3hFLElBQUksT0FBTyxFQUFFO1FBQ1osTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QyxJQUFJLElBQUksRUFBRTtZQUNULE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN4QjtLQUNEO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsR0FBRyxFQUFFLENBQUMsQ0FBQztBQUNuRCxDQUFDO0FBVkQsMEJBVUM7QUFFRCxTQUFnQixXQUFXO0lBQzFCLElBQUksY0FBYyxFQUFFO1FBQ25CLE9BQU8sY0FBYyxDQUFDO0tBQ3RCO0lBRUQsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUMxQyxJQUFJLGNBQWMsRUFBRTtRQUNuQixPQUFPLGNBQWMsQ0FBQztLQUN0QjtJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBWEQsa0NBV0MiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHtcbiBcdFx0XHRcdGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuIFx0XHRcdFx0Z2V0OiBnZXR0ZXJcbiBcdFx0XHR9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9wcmV2aWV3LXNyYy9pbmRleC50c1wiKTtcbiIsIi8qKlxuICogbG9kYXNoIChDdXN0b20gQnVpbGQpIDxodHRwczovL2xvZGFzaC5jb20vPlxuICogQnVpbGQ6IGBsb2Rhc2ggbW9kdWxhcml6ZSBleHBvcnRzPVwibnBtXCIgLW8gLi9gXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9ycyA8aHR0cHM6Ly9qcXVlcnkub3JnLz5cbiAqIFJlbGVhc2VkIHVuZGVyIE1JVCBsaWNlbnNlIDxodHRwczovL2xvZGFzaC5jb20vbGljZW5zZT5cbiAqIEJhc2VkIG9uIFVuZGVyc2NvcmUuanMgMS44LjMgPGh0dHA6Ly91bmRlcnNjb3JlanMub3JnL0xJQ0VOU0U+XG4gKiBDb3B5cmlnaHQgSmVyZW15IEFzaGtlbmFzLCBEb2N1bWVudENsb3VkIGFuZCBJbnZlc3RpZ2F0aXZlIFJlcG9ydGVycyAmIEVkaXRvcnNcbiAqL1xuXG4vKiogVXNlZCBhcyB0aGUgYFR5cGVFcnJvcmAgbWVzc2FnZSBmb3IgXCJGdW5jdGlvbnNcIiBtZXRob2RzLiAqL1xudmFyIEZVTkNfRVJST1JfVEVYVCA9ICdFeHBlY3RlZCBhIGZ1bmN0aW9uJztcblxuLyoqIFVzZWQgYXMgcmVmZXJlbmNlcyBmb3IgdmFyaW91cyBgTnVtYmVyYCBjb25zdGFudHMuICovXG52YXIgTkFOID0gMCAvIDA7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBzeW1ib2xUYWcgPSAnW29iamVjdCBTeW1ib2xdJztcblxuLyoqIFVzZWQgdG8gbWF0Y2ggbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZS4gKi9cbnZhciByZVRyaW0gPSAvXlxccyt8XFxzKyQvZztcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJhZCBzaWduZWQgaGV4YWRlY2ltYWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmFkSGV4ID0gL15bLStdMHhbMC05YS1mXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBiaW5hcnkgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmluYXJ5ID0gL14wYlswMV0rJC9pO1xuXG4vKiogVXNlZCB0byBkZXRlY3Qgb2N0YWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzT2N0YWwgPSAvXjBvWzAtN10rJC9pO1xuXG4vKiogQnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMgd2l0aG91dCBhIGRlcGVuZGVuY3kgb24gYHJvb3RgLiAqL1xudmFyIGZyZWVQYXJzZUludCA9IHBhcnNlSW50O1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYGdsb2JhbGAgZnJvbSBOb2RlLmpzLiAqL1xudmFyIGZyZWVHbG9iYWwgPSB0eXBlb2YgZ2xvYmFsID09ICdvYmplY3QnICYmIGdsb2JhbCAmJiBnbG9iYWwuT2JqZWN0ID09PSBPYmplY3QgJiYgZ2xvYmFsO1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYHNlbGZgLiAqL1xudmFyIGZyZWVTZWxmID0gdHlwZW9mIHNlbGYgPT0gJ29iamVjdCcgJiYgc2VsZiAmJiBzZWxmLk9iamVjdCA9PT0gT2JqZWN0ICYmIHNlbGY7XG5cbi8qKiBVc2VkIGFzIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb2JqZWN0LiAqL1xudmFyIHJvb3QgPSBmcmVlR2xvYmFsIHx8IGZyZWVTZWxmIHx8IEZ1bmN0aW9uKCdyZXR1cm4gdGhpcycpKCk7XG5cbi8qKiBVc2VkIGZvciBidWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZVxuICogW2B0b1N0cmluZ1RhZ2BdKGh0dHA6Ly9lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmplY3RUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyBmb3IgdGhvc2Ugd2l0aCB0aGUgc2FtZSBuYW1lIGFzIG90aGVyIGBsb2Rhc2hgIG1ldGhvZHMuICovXG52YXIgbmF0aXZlTWF4ID0gTWF0aC5tYXgsXG4gICAgbmF0aXZlTWluID0gTWF0aC5taW47XG5cbi8qKlxuICogR2V0cyB0aGUgdGltZXN0YW1wIG9mIHRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRoYXQgaGF2ZSBlbGFwc2VkIHNpbmNlXG4gKiB0aGUgVW5peCBlcG9jaCAoMSBKYW51YXJ5IDE5NzAgMDA6MDA6MDAgVVRDKS5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDIuNC4wXG4gKiBAY2F0ZWdvcnkgRGF0ZVxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgdGltZXN0YW1wLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmRlZmVyKGZ1bmN0aW9uKHN0YW1wKSB7XG4gKiAgIGNvbnNvbGUubG9nKF8ubm93KCkgLSBzdGFtcCk7XG4gKiB9LCBfLm5vdygpKTtcbiAqIC8vID0+IExvZ3MgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgaXQgdG9vayBmb3IgdGhlIGRlZmVycmVkIGludm9jYXRpb24uXG4gKi9cbnZhciBub3cgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHJvb3QuRGF0ZS5ub3coKTtcbn07XG5cbi8qKlxuICogQ3JlYXRlcyBhIGRlYm91bmNlZCBmdW5jdGlvbiB0aGF0IGRlbGF5cyBpbnZva2luZyBgZnVuY2AgdW50aWwgYWZ0ZXIgYHdhaXRgXG4gKiBtaWxsaXNlY29uZHMgaGF2ZSBlbGFwc2VkIHNpbmNlIHRoZSBsYXN0IHRpbWUgdGhlIGRlYm91bmNlZCBmdW5jdGlvbiB3YXNcbiAqIGludm9rZWQuIFRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgIG1ldGhvZCB0byBjYW5jZWxcbiAqIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLlxuICogUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2Agc2hvdWxkIGJlIGludm9rZWQgb24gdGhlXG4gKiBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGAgdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkXG4gKiB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50XG4gKiBjYWxscyB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHJldHVybiB0aGUgcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYFxuICogaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIGRlYm91bmNlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy5kZWJvdW5jZWAgYW5kIGBfLnRocm90dGxlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGRlYm91bmNlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIGRlbGF5LlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9ZmFsc2VdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgbGVhZGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHBhcmFtIHtudW1iZXJ9IFtvcHRpb25zLm1heFdhaXRdXG4gKiAgVGhlIG1heGltdW0gdGltZSBgZnVuY2AgaXMgYWxsb3dlZCB0byBiZSBkZWxheWVkIGJlZm9yZSBpdCdzIGludm9rZWQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGRlYm91bmNlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgY29zdGx5IGNhbGN1bGF0aW9ucyB3aGlsZSB0aGUgd2luZG93IHNpemUgaXMgaW4gZmx1eC5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdyZXNpemUnLCBfLmRlYm91bmNlKGNhbGN1bGF0ZUxheW91dCwgMTUwKSk7XG4gKlxuICogLy8gSW52b2tlIGBzZW5kTWFpbGAgd2hlbiBjbGlja2VkLCBkZWJvdW5jaW5nIHN1YnNlcXVlbnQgY2FsbHMuXG4gKiBqUXVlcnkoZWxlbWVudCkub24oJ2NsaWNrJywgXy5kZWJvdW5jZShzZW5kTWFpbCwgMzAwLCB7XG4gKiAgICdsZWFkaW5nJzogdHJ1ZSxcbiAqICAgJ3RyYWlsaW5nJzogZmFsc2VcbiAqIH0pKTtcbiAqXG4gKiAvLyBFbnN1cmUgYGJhdGNoTG9nYCBpcyBpbnZva2VkIG9uY2UgYWZ0ZXIgMSBzZWNvbmQgb2YgZGVib3VuY2VkIGNhbGxzLlxuICogdmFyIGRlYm91bmNlZCA9IF8uZGVib3VuY2UoYmF0Y2hMb2csIDI1MCwgeyAnbWF4V2FpdCc6IDEwMDAgfSk7XG4gKiB2YXIgc291cmNlID0gbmV3IEV2ZW50U291cmNlKCcvc3RyZWFtJyk7XG4gKiBqUXVlcnkoc291cmNlKS5vbignbWVzc2FnZScsIGRlYm91bmNlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyBkZWJvdW5jZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIGRlYm91bmNlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiBkZWJvdW5jZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsYXN0QXJncyxcbiAgICAgIGxhc3RUaGlzLFxuICAgICAgbWF4V2FpdCxcbiAgICAgIHJlc3VsdCxcbiAgICAgIHRpbWVySWQsXG4gICAgICBsYXN0Q2FsbFRpbWUsXG4gICAgICBsYXN0SW52b2tlVGltZSA9IDAsXG4gICAgICBsZWFkaW5nID0gZmFsc2UsXG4gICAgICBtYXhpbmcgPSBmYWxzZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICB3YWl0ID0gdG9OdW1iZXIod2FpdCkgfHwgMDtcbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICEhb3B0aW9ucy5sZWFkaW5nO1xuICAgIG1heGluZyA9ICdtYXhXYWl0JyBpbiBvcHRpb25zO1xuICAgIG1heFdhaXQgPSBtYXhpbmcgPyBuYXRpdmVNYXgodG9OdW1iZXIob3B0aW9ucy5tYXhXYWl0KSB8fCAwLCB3YWl0KSA6IG1heFdhaXQ7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuXG4gIGZ1bmN0aW9uIGludm9rZUZ1bmModGltZSkge1xuICAgIHZhciBhcmdzID0gbGFzdEFyZ3MsXG4gICAgICAgIHRoaXNBcmcgPSBsYXN0VGhpcztcblxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIHJlc3VsdCA9IGZ1bmMuYXBwbHkodGhpc0FyZywgYXJncyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGxlYWRpbmdFZGdlKHRpbWUpIHtcbiAgICAvLyBSZXNldCBhbnkgYG1heFdhaXRgIHRpbWVyLlxuICAgIGxhc3RJbnZva2VUaW1lID0gdGltZTtcbiAgICAvLyBTdGFydCB0aGUgdGltZXIgZm9yIHRoZSB0cmFpbGluZyBlZGdlLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgLy8gSW52b2tlIHRoZSBsZWFkaW5nIGVkZ2UuXG4gICAgcmV0dXJuIGxlYWRpbmcgPyBpbnZva2VGdW5jKHRpbWUpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gcmVtYWluaW5nV2FpdCh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZSxcbiAgICAgICAgcmVzdWx0ID0gd2FpdCAtIHRpbWVTaW5jZUxhc3RDYWxsO1xuXG4gICAgcmV0dXJuIG1heGluZyA/IG5hdGl2ZU1pbihyZXN1bHQsIG1heFdhaXQgLSB0aW1lU2luY2VMYXN0SW52b2tlKSA6IHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHNob3VsZEludm9rZSh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZTtcblxuICAgIC8vIEVpdGhlciB0aGlzIGlzIHRoZSBmaXJzdCBjYWxsLCBhY3Rpdml0eSBoYXMgc3RvcHBlZCBhbmQgd2UncmUgYXQgdGhlXG4gICAgLy8gdHJhaWxpbmcgZWRnZSwgdGhlIHN5c3RlbSB0aW1lIGhhcyBnb25lIGJhY2t3YXJkcyBhbmQgd2UncmUgdHJlYXRpbmdcbiAgICAvLyBpdCBhcyB0aGUgdHJhaWxpbmcgZWRnZSwgb3Igd2UndmUgaGl0IHRoZSBgbWF4V2FpdGAgbGltaXQuXG4gICAgcmV0dXJuIChsYXN0Q2FsbFRpbWUgPT09IHVuZGVmaW5lZCB8fCAodGltZVNpbmNlTGFzdENhbGwgPj0gd2FpdCkgfHxcbiAgICAgICh0aW1lU2luY2VMYXN0Q2FsbCA8IDApIHx8IChtYXhpbmcgJiYgdGltZVNpbmNlTGFzdEludm9rZSA+PSBtYXhXYWl0KSk7XG4gIH1cblxuICBmdW5jdGlvbiB0aW1lckV4cGlyZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKTtcbiAgICBpZiAoc2hvdWxkSW52b2tlKHRpbWUpKSB7XG4gICAgICByZXR1cm4gdHJhaWxpbmdFZGdlKHRpbWUpO1xuICAgIH1cbiAgICAvLyBSZXN0YXJ0IHRoZSB0aW1lci5cbiAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHJlbWFpbmluZ1dhaXQodGltZSkpO1xuICB9XG5cbiAgZnVuY3Rpb24gdHJhaWxpbmdFZGdlKHRpbWUpIHtcbiAgICB0aW1lcklkID0gdW5kZWZpbmVkO1xuXG4gICAgLy8gT25seSBpbnZva2UgaWYgd2UgaGF2ZSBgbGFzdEFyZ3NgIHdoaWNoIG1lYW5zIGBmdW5jYCBoYXMgYmVlblxuICAgIC8vIGRlYm91bmNlZCBhdCBsZWFzdCBvbmNlLlxuICAgIGlmICh0cmFpbGluZyAmJiBsYXN0QXJncykge1xuICAgICAgcmV0dXJuIGludm9rZUZ1bmModGltZSk7XG4gICAgfVxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNhbmNlbCgpIHtcbiAgICBpZiAodGltZXJJZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXJJZCk7XG4gICAgfVxuICAgIGxhc3RJbnZva2VUaW1lID0gMDtcbiAgICBsYXN0QXJncyA9IGxhc3RDYWxsVGltZSA9IGxhc3RUaGlzID0gdGltZXJJZCA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGZsdXNoKCkge1xuICAgIHJldHVybiB0aW1lcklkID09PSB1bmRlZmluZWQgPyByZXN1bHQgOiB0cmFpbGluZ0VkZ2Uobm93KCkpO1xuICB9XG5cbiAgZnVuY3Rpb24gZGVib3VuY2VkKCkge1xuICAgIHZhciB0aW1lID0gbm93KCksXG4gICAgICAgIGlzSW52b2tpbmcgPSBzaG91bGRJbnZva2UodGltZSk7XG5cbiAgICBsYXN0QXJncyA9IGFyZ3VtZW50cztcbiAgICBsYXN0VGhpcyA9IHRoaXM7XG4gICAgbGFzdENhbGxUaW1lID0gdGltZTtcblxuICAgIGlmIChpc0ludm9raW5nKSB7XG4gICAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBsZWFkaW5nRWRnZShsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgICAgaWYgKG1heGluZykge1xuICAgICAgICAvLyBIYW5kbGUgaW52b2NhdGlvbnMgaW4gYSB0aWdodCBsb29wLlxuICAgICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgICAgICByZXR1cm4gaW52b2tlRnVuYyhsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGRlYm91bmNlZC5jYW5jZWwgPSBjYW5jZWw7XG4gIGRlYm91bmNlZC5mbHVzaCA9IGZsdXNoO1xuICByZXR1cm4gZGVib3VuY2VkO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSB0aHJvdHRsZWQgZnVuY3Rpb24gdGhhdCBvbmx5IGludm9rZXMgYGZ1bmNgIGF0IG1vc3Qgb25jZSBwZXJcbiAqIGV2ZXJ5IGB3YWl0YCBtaWxsaXNlY29uZHMuIFRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgXG4gKiBtZXRob2QgdG8gY2FuY2VsIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvXG4gKiBpbW1lZGlhdGVseSBpbnZva2UgdGhlbS4gUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2BcbiAqIHNob3VsZCBiZSBpbnZva2VkIG9uIHRoZSBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGBcbiAqIHRpbWVvdXQuIFRoZSBgZnVuY2AgaXMgaW52b2tlZCB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGVcbiAqIHRocm90dGxlZCBmdW5jdGlvbi4gU3Vic2VxdWVudCBjYWxscyB0byB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uIHJldHVybiB0aGVcbiAqIHJlc3VsdCBvZiB0aGUgbGFzdCBgZnVuY2AgaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIHRocm90dGxlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy50aHJvdHRsZWAgYW5kIGBfLmRlYm91bmNlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIHRocm90dGxlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHRocm90dGxlIGludm9jYXRpb25zIHRvLlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IHRocm90dGxlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgZXhjZXNzaXZlbHkgdXBkYXRpbmcgdGhlIHBvc2l0aW9uIHdoaWxlIHNjcm9sbGluZy5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdzY3JvbGwnLCBfLnRocm90dGxlKHVwZGF0ZVBvc2l0aW9uLCAxMDApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHJlbmV3VG9rZW5gIHdoZW4gdGhlIGNsaWNrIGV2ZW50IGlzIGZpcmVkLCBidXQgbm90IG1vcmUgdGhhbiBvbmNlIGV2ZXJ5IDUgbWludXRlcy5cbiAqIHZhciB0aHJvdHRsZWQgPSBfLnRocm90dGxlKHJlbmV3VG9rZW4sIDMwMDAwMCwgeyAndHJhaWxpbmcnOiBmYWxzZSB9KTtcbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCB0aHJvdHRsZWQpO1xuICpcbiAqIC8vIENhbmNlbCB0aGUgdHJhaWxpbmcgdGhyb3R0bGVkIGludm9jYXRpb24uXG4gKiBqUXVlcnkod2luZG93KS5vbigncG9wc3RhdGUnLCB0aHJvdHRsZWQuY2FuY2VsKTtcbiAqL1xuZnVuY3Rpb24gdGhyb3R0bGUoZnVuYywgd2FpdCwgb3B0aW9ucykge1xuICB2YXIgbGVhZGluZyA9IHRydWUsXG4gICAgICB0cmFpbGluZyA9IHRydWU7XG5cbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICdsZWFkaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLmxlYWRpbmcgOiBsZWFkaW5nO1xuICAgIHRyYWlsaW5nID0gJ3RyYWlsaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLnRyYWlsaW5nIDogdHJhaWxpbmc7XG4gIH1cbiAgcmV0dXJuIGRlYm91bmNlKGZ1bmMsIHdhaXQsIHtcbiAgICAnbGVhZGluZyc6IGxlYWRpbmcsXG4gICAgJ21heFdhaXQnOiB3YWl0LFxuICAgICd0cmFpbGluZyc6IHRyYWlsaW5nXG4gIH0pO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIHRoZVxuICogW2xhbmd1YWdlIHR5cGVdKGh0dHA6Ly93d3cuZWNtYS1pbnRlcm5hdGlvbmFsLm9yZy9lY21hLTI2Mi83LjAvI3NlYy1lY21hc2NyaXB0LWxhbmd1YWdlLXR5cGVzKVxuICogb2YgYE9iamVjdGAuIChlLmcuIGFycmF5cywgZnVuY3Rpb25zLCBvYmplY3RzLCByZWdleGVzLCBgbmV3IE51bWJlcigwKWAsIGFuZCBgbmV3IFN0cmluZygnJylgKVxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGFuIG9iamVjdCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0KHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChfLm5vb3ApO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QobnVsbCk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdCh2YWx1ZSkge1xuICB2YXIgdHlwZSA9IHR5cGVvZiB2YWx1ZTtcbiAgcmV0dXJuICEhdmFsdWUgJiYgKHR5cGUgPT0gJ29iamVjdCcgfHwgdHlwZSA9PSAnZnVuY3Rpb24nKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZS4gQSB2YWx1ZSBpcyBvYmplY3QtbGlrZSBpZiBpdCdzIG5vdCBgbnVsbGBcbiAqIGFuZCBoYXMgYSBgdHlwZW9mYCByZXN1bHQgb2YgXCJvYmplY3RcIi5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZSwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZSh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdExpa2UoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShfLm5vb3ApO1xuICogLy8gPT4gZmFsc2VcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0TGlrZSh2YWx1ZSkge1xuICByZXR1cm4gISF2YWx1ZSAmJiB0eXBlb2YgdmFsdWUgPT0gJ29iamVjdCc7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgY2xhc3NpZmllZCBhcyBhIGBTeW1ib2xgIHByaW1pdGl2ZSBvciBvYmplY3QuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYSBzeW1ib2wsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc1N5bWJvbChTeW1ib2wuaXRlcmF0b3IpO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNTeW1ib2woJ2FiYycpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNTeW1ib2wodmFsdWUpIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PSAnc3ltYm9sJyB8fFxuICAgIChpc09iamVjdExpa2UodmFsdWUpICYmIG9iamVjdFRvU3RyaW5nLmNhbGwodmFsdWUpID09IHN5bWJvbFRhZyk7XG59XG5cbi8qKlxuICogQ29udmVydHMgYHZhbHVlYCB0byBhIG51bWJlci5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gcHJvY2Vzcy5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIG51bWJlci5cbiAqIEBleGFtcGxlXG4gKlxuICogXy50b051bWJlcigzLjIpO1xuICogLy8gPT4gMy4yXG4gKlxuICogXy50b051bWJlcihOdW1iZXIuTUlOX1ZBTFVFKTtcbiAqIC8vID0+IDVlLTMyNFxuICpcbiAqIF8udG9OdW1iZXIoSW5maW5pdHkpO1xuICogLy8gPT4gSW5maW5pdHlcbiAqXG4gKiBfLnRvTnVtYmVyKCczLjInKTtcbiAqIC8vID0+IDMuMlxuICovXG5mdW5jdGlvbiB0b051bWJlcih2YWx1ZSkge1xuICBpZiAodHlwZW9mIHZhbHVlID09ICdudW1iZXInKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIGlmIChpc1N5bWJvbCh2YWx1ZSkpIHtcbiAgICByZXR1cm4gTkFOO1xuICB9XG4gIGlmIChpc09iamVjdCh2YWx1ZSkpIHtcbiAgICB2YXIgb3RoZXIgPSB0eXBlb2YgdmFsdWUudmFsdWVPZiA9PSAnZnVuY3Rpb24nID8gdmFsdWUudmFsdWVPZigpIDogdmFsdWU7XG4gICAgdmFsdWUgPSBpc09iamVjdChvdGhlcikgPyAob3RoZXIgKyAnJykgOiBvdGhlcjtcbiAgfVxuICBpZiAodHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHZhbHVlID09PSAwID8gdmFsdWUgOiArdmFsdWU7XG4gIH1cbiAgdmFsdWUgPSB2YWx1ZS5yZXBsYWNlKHJlVHJpbSwgJycpO1xuICB2YXIgaXNCaW5hcnkgPSByZUlzQmluYXJ5LnRlc3QodmFsdWUpO1xuICByZXR1cm4gKGlzQmluYXJ5IHx8IHJlSXNPY3RhbC50ZXN0KHZhbHVlKSlcbiAgICA/IGZyZWVQYXJzZUludCh2YWx1ZS5zbGljZSgyKSwgaXNCaW5hcnkgPyAyIDogOClcbiAgICA6IChyZUlzQmFkSGV4LnRlc3QodmFsdWUpID8gTkFOIDogK3ZhbHVlKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB0aHJvdHRsZTtcbiIsInZhciBnO1xyXG5cclxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcclxuZyA9IChmdW5jdGlvbigpIHtcclxuXHRyZXR1cm4gdGhpcztcclxufSkoKTtcclxuXHJcbnRyeSB7XHJcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXHJcblx0ZyA9IGcgfHwgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpIHx8ICgxLCBldmFsKShcInRoaXNcIik7XHJcbn0gY2F0Y2ggKGUpIHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxyXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xyXG59XHJcblxyXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXHJcbi8vIFdlIHJldHVybiB1bmRlZmluZWQsIGluc3RlYWQgb2Ygbm90aGluZyBoZXJlLCBzbyBpdCdzXHJcbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cclxuXHJcbm1vZHVsZS5leHBvcnRzID0gZztcclxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5pbXBvcnQgeyBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcblxuZXhwb3J0IGNsYXNzIEFjdGl2ZUxpbmVNYXJrZXIge1xuXHRwcml2YXRlIF9jdXJyZW50OiBhbnk7XG5cblx0b25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmU6IG51bWJlcikge1xuXHRcdGNvbnN0IHsgcHJldmlvdXMgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcblx0XHR0aGlzLl91cGRhdGUocHJldmlvdXMgJiYgcHJldmlvdXMuZWxlbWVudCk7XG5cdH1cblxuXHRfdXBkYXRlKGJlZm9yZTogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHR0aGlzLl91bm1hcmtBY3RpdmVFbGVtZW50KHRoaXMuX2N1cnJlbnQpO1xuXHRcdHRoaXMuX21hcmtBY3RpdmVFbGVtZW50KGJlZm9yZSk7XG5cdFx0dGhpcy5fY3VycmVudCA9IGJlZm9yZTtcblx0fVxuXG5cdF91bm1hcmtBY3RpdmVFbGVtZW50KGVsZW1lbnQ6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkKSB7XG5cdFx0aWYgKCFlbGVtZW50KSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGVsZW1lbnQuY2xhc3NOYW1lID0gZWxlbWVudC5jbGFzc05hbWUucmVwbGFjZSgvXFxiY29kZS1hY3RpdmUtbGluZVxcYi9nLCAnJyk7XG5cdH1cblxuXHRfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudDogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHRpZiAoIWVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0ZWxlbWVudC5jbGFzc05hbWUgKz0gJyBjb2RlLWFjdGl2ZS1saW5lJztcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGY6ICgpID0+IHZvaWQpIHtcblx0aWYgKGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJyB8fCBkb2N1bWVudC5yZWFkeVN0YXRlIGFzIHN0cmluZyA9PT0gJ3VuaW5pdGlhbGl6ZWQnKSB7XG5cdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGYpO1xuXHR9IGVsc2Uge1xuXHRcdGYoKTtcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBBY3RpdmVMaW5lTWFya2VyIH0gZnJvbSAnLi9hY3RpdmVMaW5lTWFya2VyJztcbmltcG9ydCB7IG9uY2VEb2N1bWVudExvYWRlZCB9IGZyb20gJy4vZXZlbnRzJztcbmltcG9ydCB7IGNyZWF0ZVBvc3RlckZvclZzQ29kZSB9IGZyb20gJy4vbWVzc2FnaW5nJztcbmltcG9ydCB7IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0LCBzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcbmltcG9ydCB7IGdldFNldHRpbmdzLCBnZXREYXRhIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5pbXBvcnQgdGhyb3R0bGUgPSByZXF1aXJlKCdsb2Rhc2gudGhyb3R0bGUnKTtcblxuZGVjbGFyZSB2YXIgYWNxdWlyZVZzQ29kZUFwaTogYW55O1xuXG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IEFjdGl2ZUxpbmVNYXJrZXIoKTtcbmNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3MoKTtcblxuY29uc3QgdnNjb2RlID0gYWNxdWlyZVZzQ29kZUFwaSgpO1xuXG4vLyBTZXQgVlMgQ29kZSBzdGF0ZVxubGV0IHN0YXRlID0gZ2V0RGF0YTx7IGxpbmU6IG51bWJlciB9PignZGF0YS1zdGF0ZScpO1xudnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblxuY29uc3QgbWVzc2FnaW5nID0gY3JlYXRlUG9zdGVyRm9yVnNDb2RlKHZzY29kZSk7XG5cbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG5cbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG5cdHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5cbm9uY2VEb2N1bWVudExvYWRlZCgoKSA9PiB7XG5cdGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuXHRcdHNldFRpbWVvdXQoKCkgPT4ge1xuXHRcdFx0Y29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcblx0XHRcdGlmICghaXNOYU4oaW5pdGlhbExpbmUpKSB7XG5cdFx0XHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRcdFx0c2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcblx0XHRcdH1cblx0XHR9LCAwKTtcblx0fVxufSk7XG5cbmNvbnN0IG9uVXBkYXRlVmlldyA9ICgoKSA9PiB7XG5cdGNvbnN0IGRvU2Nyb2xsID0gdGhyb3R0bGUoKGxpbmU6IG51bWJlcikgPT4ge1xuXHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG5cdH0sIDUwKTtcblxuXHRyZXR1cm4gKGxpbmU6IG51bWJlciwgc2V0dGluZ3M6IGFueSkgPT4ge1xuXHRcdGlmICghaXNOYU4obGluZSkpIHtcblx0XHRcdHNldHRpbmdzLmxpbmUgPSBsaW5lO1xuXHRcdFx0ZG9TY3JvbGwobGluZSk7XG5cdFx0fVxuXHR9O1xufSkoKTtcblxubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG5cdGNvbnN0IGltYWdlSW5mbzogeyBpZDogc3RyaW5nLCBoZWlnaHQ6IG51bWJlciwgd2lkdGg6IG51bWJlciB9W10gPSBbXTtcblx0bGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcblx0aWYgKGltYWdlcykge1xuXHRcdGxldCBpO1xuXHRcdGZvciAoaSA9IDA7IGkgPCBpbWFnZXMubGVuZ3RoOyBpKyspIHtcblx0XHRcdGNvbnN0IGltZyA9IGltYWdlc1tpXTtcblxuXHRcdFx0aWYgKGltZy5jbGFzc0xpc3QuY29udGFpbnMoJ2xvYWRpbmcnKSkge1xuXHRcdFx0XHRpbWcuY2xhc3NMaXN0LnJlbW92ZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHRpbWFnZUluZm8ucHVzaCh7XG5cdFx0XHRcdGlkOiBpbWcuaWQsXG5cdFx0XHRcdGhlaWdodDogaW1nLmhlaWdodCxcblx0XHRcdFx0d2lkdGg6IGltZy53aWR0aFxuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjYWNoZUltYWdlU2l6ZXMnLCBpbWFnZUluZm8pO1xuXHR9XG59LCA1MCk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCAoKSA9PiB7XG5cdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0dXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgZXZlbnQgPT4ge1xuXHRpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG5cdFx0Y2FzZSAnb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uJzpcblx0XHRcdG1hcmtlci5vbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24oZXZlbnQuZGF0YS5saW5lKTtcblx0XHRcdGJyZWFrO1xuXG5cdFx0Y2FzZSAndXBkYXRlVmlldyc6XG5cdFx0XHRvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG5cdFx0XHRicmVhaztcblx0fVxufSwgZmFsc2UpO1xuXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcblx0aWYgKCFzZXR0aW5ncy5kb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHQvLyBJZ25vcmUgY2xpY2tzIG9uIGxpbmtzXG5cdGZvciAobGV0IG5vZGUgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7IG5vZGU7IG5vZGUgPSBub2RlLnBhcmVudE5vZGUgYXMgSFRNTEVsZW1lbnQpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lID09PSAnQScpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdH1cblxuXHRjb25zdCBvZmZzZXQgPSBldmVudC5wYWdlWTtcblx0Y29uc3QgbGluZSA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcblx0fVxufSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIWV2ZW50KSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0bGV0IG5vZGU6IGFueSA9IGV2ZW50LnRhcmdldDtcblx0d2hpbGUgKG5vZGUpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lICYmIG5vZGUudGFnTmFtZSA9PT0gJ0EnICYmIG5vZGUuaHJlZikge1xuXHRcdFx0aWYgKG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJykuc3RhcnRzV2l0aCgnIycpKSB7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0aWYgKG5vZGUuaHJlZi5zdGFydHNXaXRoKCdmaWxlOi8vJykgfHwgbm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ3ZzY29kZS1yZXNvdXJjZTonKSB8fCBub2RlLmhyZWYuc3RhcnRzV2l0aChzZXR0aW5ncy53ZWJ2aWV3UmVzb3VyY2VSb290KSkge1xuXHRcdFx0XHRjb25zdCBbcGF0aCwgZnJhZ21lbnRdID0gbm9kZS5ocmVmLnJlcGxhY2UoL14oZmlsZTpcXC9cXC98dnNjb2RlLXJlc291cmNlOikvaSwgJycpLnJlcGxhY2UobmV3IFJlZ0V4cChgXiR7ZXNjYXBlUmVnRXhwKHNldHRpbmdzLndlYnZpZXdSZXNvdXJjZVJvb3QpfWApKS5zcGxpdCgnIycpO1xuXHRcdFx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NsaWNrTGluaycsIHsgcGF0aCwgZnJhZ21lbnQgfSk7XG5cdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRcdGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuXHRcdFx0XHRicmVhaztcblx0XHRcdH1cblx0XHRcdGJyZWFrO1xuXHRcdH1cblx0XHRub2RlID0gbm9kZS5wYXJlbnROb2RlO1xuXHR9XG59LCB0cnVlKTtcblxuaWYgKHNldHRpbmdzLnNjcm9sbEVkaXRvcldpdGhQcmV2aWV3KSB7XG5cdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdzY3JvbGwnLCB0aHJvdHRsZSgoKSA9PiB7XG5cdFx0aWYgKHNjcm9sbERpc2FibGVkKSB7XG5cdFx0XHRzY3JvbGxEaXNhYmxlZCA9IGZhbHNlO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRjb25zdCBsaW5lID0gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQod2luZG93LnNjcm9sbFkpO1xuXHRcdFx0aWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcblx0XHRcdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdyZXZlYWxMaW5lJywgeyBsaW5lIH0pO1xuXHRcdFx0XHRzdGF0ZS5saW5lID0gbGluZTtcblx0XHRcdFx0dnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sIDUwKSk7XG59XG5cbmZ1bmN0aW9uIGVzY2FwZVJlZ0V4cCh0ZXh0OiBzdHJpbmcpIHtcblx0cmV0dXJuIHRleHQucmVwbGFjZSgvWy1bXFxde30oKSorPy4sXFxcXF4kfCNcXHNdL2csICdcXFxcJCYnKTtcbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgZ2V0U2V0dGluZ3MgfSBmcm9tICcuL3NldHRpbmdzJztcblxuZXhwb3J0IGludGVyZmFjZSBNZXNzYWdlUG9zdGVyIHtcblx0LyoqXG5cdCAqIFBvc3QgYSBtZXNzYWdlIHRvIHRoZSBtYXJrZG93biBleHRlbnNpb25cblx0ICovXG5cdHBvc3RNZXNzYWdlKHR5cGU6IHN0cmluZywgYm9keTogb2JqZWN0KTogdm9pZDtcbn1cblxuZXhwb3J0IGNvbnN0IGNyZWF0ZVBvc3RlckZvclZzQ29kZSA9ICh2c2NvZGU6IGFueSkgPT4ge1xuXHRyZXR1cm4gbmV3IGNsYXNzIGltcGxlbWVudHMgTWVzc2FnZVBvc3RlciB7XG5cdFx0cG9zdE1lc3NhZ2UodHlwZTogc3RyaW5nLCBib2R5OiBvYmplY3QpOiB2b2lkIHtcblx0XHRcdHZzY29kZS5wb3N0TWVzc2FnZSh7XG5cdFx0XHRcdHR5cGUsXG5cdFx0XHRcdHNvdXJjZTogZ2V0U2V0dGluZ3MoKS5zb3VyY2UsXG5cdFx0XHRcdGJvZHlcblx0XHRcdH0pO1xuXHRcdH1cblx0fTtcbn07XG5cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuXG5cbmZ1bmN0aW9uIGNsYW1wKG1pbjogbnVtYmVyLCBtYXg6IG51bWJlciwgdmFsdWU6IG51bWJlcikge1xuXHRyZXR1cm4gTWF0aC5taW4obWF4LCBNYXRoLm1heChtaW4sIHZhbHVlKSk7XG59XG5cbmZ1bmN0aW9uIGNsYW1wTGluZShsaW5lOiBudW1iZXIpIHtcblx0cmV0dXJuIGNsYW1wKDAsIGdldFNldHRpbmdzKCkubGluZUNvdW50IC0gMSwgbGluZSk7XG59XG5cblxuZXhwb3J0IGludGVyZmFjZSBDb2RlTGluZUVsZW1lbnQge1xuXHRlbGVtZW50OiBIVE1MRWxlbWVudDtcblx0bGluZTogbnVtYmVyO1xufVxuXG5jb25zdCBnZXRDb2RlTGluZUVsZW1lbnRzID0gKCgpID0+IHtcblx0bGV0IGVsZW1lbnRzOiBDb2RlTGluZUVsZW1lbnRbXTtcblx0cmV0dXJuICgpID0+IHtcblx0XHRpZiAoIWVsZW1lbnRzKSB7XG5cdFx0XHRlbGVtZW50cyA9IFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV07XG5cdFx0XHRmb3IgKGNvbnN0IGVsZW1lbnQgb2YgZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnY29kZS1saW5lJykpIHtcblx0XHRcdFx0Y29uc3QgbGluZSA9ICtlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1saW5lJykhO1xuXHRcdFx0XHRpZiAoIWlzTmFOKGxpbmUpKSB7XG5cdFx0XHRcdFx0ZWxlbWVudHMucHVzaCh7IGVsZW1lbnQ6IGVsZW1lbnQgYXMgSFRNTEVsZW1lbnQsIGxpbmUgfSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIGVsZW1lbnRzO1xuXHR9O1xufSkoKTtcblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgbWFwIHRvIGEgc3BlY2lmaWMgdGFyZ2V0IGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqXG4gKiBJZiBhbiBleGFjdCBtYXRjaCwgcmV0dXJucyBhIHNpbmdsZSBlbGVtZW50LiBJZiB0aGUgbGluZSBpcyBiZXR3ZWVuIGVsZW1lbnRzLFxuICogcmV0dXJucyB0aGUgZWxlbWVudCBwcmlvciB0byBhbmQgdGhlIGVsZW1lbnQgYWZ0ZXIgdGhlIGdpdmVuIGxpbmUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUodGFyZ2V0TGluZTogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZU51bWJlciA9IE1hdGguZmxvb3IodGFyZ2V0TGluZSk7XG5cdGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuXHRsZXQgcHJldmlvdXMgPSBsaW5lc1swXSB8fCBudWxsO1xuXHRmb3IgKGNvbnN0IGVudHJ5IG9mIGxpbmVzKSB7XG5cdFx0aWYgKGVudHJ5LmxpbmUgPT09IGxpbmVOdW1iZXIpIHtcblx0XHRcdHJldHVybiB7IHByZXZpb3VzOiBlbnRyeSwgbmV4dDogdW5kZWZpbmVkIH07XG5cdFx0fSBlbHNlIGlmIChlbnRyeS5saW5lID4gbGluZU51bWJlcikge1xuXHRcdFx0cmV0dXJuIHsgcHJldmlvdXMsIG5leHQ6IGVudHJ5IH07XG5cdFx0fVxuXHRcdHByZXZpb3VzID0gZW50cnk7XG5cdH1cblx0cmV0dXJuIHsgcHJldmlvdXMgfTtcbn1cblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgYXJlIGF0IGEgc3BlY2lmaWMgcGl4ZWwgb2Zmc2V0IG9uIHRoZSBwYWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldDogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG5cdGNvbnN0IHBvc2l0aW9uID0gb2Zmc2V0IC0gd2luZG93LnNjcm9sbFk7XG5cdGxldCBsbyA9IC0xO1xuXHRsZXQgaGkgPSBsaW5lcy5sZW5ndGggLSAxO1xuXHR3aGlsZSAobG8gKyAxIDwgaGkpIHtcblx0XHRjb25zdCBtaWQgPSBNYXRoLmZsb29yKChsbyArIGhpKSAvIDIpO1xuXHRcdGNvbnN0IGJvdW5kcyA9IGxpbmVzW21pZF0uZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcblx0XHRcdGhpID0gbWlkO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGxvID0gbWlkO1xuXHRcdH1cblx0fVxuXHRjb25zdCBoaUVsZW1lbnQgPSBsaW5lc1toaV07XG5cdGNvbnN0IGhpQm91bmRzID0gaGlFbGVtZW50LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG5cdFx0Y29uc3QgbG9FbGVtZW50ID0gbGluZXNbbG9dO1xuXHRcdHJldHVybiB7IHByZXZpb3VzOiBsb0VsZW1lbnQsIG5leHQ6IGhpRWxlbWVudCB9O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzOiBoaUVsZW1lbnQgfTtcbn1cblxuLyoqXG4gKiBBdHRlbXB0IHRvIHJldmVhbCB0aGUgZWxlbWVudCBmb3IgYSBzb3VyY2UgbGluZSBpbiB0aGUgZWRpdG9yLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmU6IG51bWJlcikge1xuXHRpZiAoIWdldFNldHRpbmdzKCkuc2Nyb2xsUHJldmlld1dpdGhFZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHRpZiAobGluZSA8PSAwKSB7XG5cdFx0d2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgMCk7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0Y29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuXHRpZiAoIXByZXZpb3VzKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cdGxldCBzY3JvbGxUbyA9IDA7XG5cdGNvbnN0IHJlY3QgPSBwcmV2aW91cy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuXHRpZiAobmV4dCAmJiBuZXh0LmxpbmUgIT09IHByZXZpb3VzLmxpbmUpIHtcblx0XHQvLyBCZXR3ZWVuIHR3byBlbGVtZW50cy4gR28gdG8gcGVyY2VudGFnZSBvZmZzZXQgYmV0d2VlbiB0aGVtLlxuXHRcdGNvbnN0IGJldHdlZW5Qcm9ncmVzcyA9IChsaW5lIC0gcHJldmlvdXMubGluZSkgLyAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0Y29uc3QgZWxlbWVudE9mZnNldCA9IG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c1RvcDtcblx0XHRzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgYmV0d2VlblByb2dyZXNzICogZWxlbWVudE9mZnNldDtcblx0fSBlbHNlIHtcblx0XHRjb25zdCBwcm9ncmVzc0luRWxlbWVudCA9IGxpbmUgLSBNYXRoLmZsb29yKGxpbmUpO1xuXHRcdHNjcm9sbFRvID0gcHJldmlvdXNUb3AgKyAocmVjdC5oZWlnaHQgKiBwcm9ncmVzc0luRWxlbWVudCk7XG5cdH1cblx0d2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgTWF0aC5tYXgoMSwgd2luZG93LnNjcm9sbFkgKyBzY3JvbGxUbykpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0OiBudW1iZXIpIHtcblx0Y29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmIChwcmV2aW91cykge1xuXHRcdGNvbnN0IHByZXZpb3VzQm91bmRzID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRjb25zdCBvZmZzZXRGcm9tUHJldmlvdXMgPSAob2Zmc2V0IC0gd2luZG93LnNjcm9sbFkgLSBwcmV2aW91c0JvdW5kcy50b3ApO1xuXHRcdGlmIChuZXh0KSB7XG5cdFx0XHRjb25zdCBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyA9IG9mZnNldEZyb21QcmV2aW91cyAvIChuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcblx0XHRcdGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgKiAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0XHRyZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGNvbnN0IHByb2dyZXNzV2l0aGluRWxlbWVudCA9IG9mZnNldEZyb21QcmV2aW91cyAvIChwcmV2aW91c0JvdW5kcy5oZWlnaHQpO1xuXHRcdFx0Y29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc1dpdGhpbkVsZW1lbnQ7XG5cdFx0XHRyZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuXHRcdH1cblx0fVxuXHRyZXR1cm4gbnVsbDtcbn1cbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgaW50ZXJmYWNlIFByZXZpZXdTZXR0aW5ncyB7XG5cdHJlYWRvbmx5IHNvdXJjZTogc3RyaW5nO1xuXHRyZWFkb25seSBsaW5lOiBudW1iZXI7XG5cdHJlYWRvbmx5IGxpbmVDb3VudDogbnVtYmVyO1xuXHRyZWFkb25seSBzY3JvbGxQcmV2aWV3V2l0aEVkaXRvcj86IGJvb2xlYW47XG5cdHJlYWRvbmx5IHNjcm9sbEVkaXRvcldpdGhQcmV2aWV3OiBib29sZWFuO1xuXHRyZWFkb25seSBkaXNhYmxlU2VjdXJpdHlXYXJuaW5nczogYm9vbGVhbjtcblx0cmVhZG9ubHkgZG91YmxlQ2xpY2tUb1N3aXRjaFRvRWRpdG9yOiBib29sZWFuO1xuXHRyZWFkb25seSB3ZWJ2aWV3UmVzb3VyY2VSb290OiBzdHJpbmc7XG59XG5cbmxldCBjYWNoZWRTZXR0aW5nczogUHJldmlld1NldHRpbmdzIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGF0YTxUID0ge30+KGtleTogc3RyaW5nKTogVCB7XG5cdGNvbnN0IGVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuXHRpZiAoZWxlbWVudCkge1xuXHRcdGNvbnN0IGRhdGEgPSBlbGVtZW50LmdldEF0dHJpYnV0ZShrZXkpO1xuXHRcdGlmIChkYXRhKSB7XG5cdFx0XHRyZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcblx0XHR9XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKTogUHJldmlld1NldHRpbmdzIHtcblx0aWYgKGNhY2hlZFNldHRpbmdzKSB7XG5cdFx0cmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuXHR9XG5cblx0Y2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsT0FBTztBQUNsQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLDhDQUE4QyxrQkFBa0I7QUFDaEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtREFBbUQsb0JBQW9CO0FBQ3ZFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsRUFBRTtBQUNiLGFBQWEsUUFBUTtBQUNyQjtBQUNBO0FBQ0Esb0JBQW9CO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVyxFQUFFO0FBQ2IsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOzs7Ozs7Ozs7Ozs7O0FDdGJBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNENBQTRDOztBQUU1Qzs7Ozs7Ozs7Ozs7Ozs7O0FDbkJBOzs7Z0dBR2dHO0FBQ2hHLCtGQUF5RDtBQUV6RCxNQUFhLGdCQUFnQjtJQUc1Qiw4QkFBOEIsQ0FBQyxJQUFZO1FBQzFDLE1BQU0sRUFBRSxRQUFRLEVBQUUsR0FBRyxzQ0FBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNwRCxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsSUFBSSxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVELE9BQU8sQ0FBQyxNQUErQjtRQUN0QyxJQUFJLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3pDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNoQyxJQUFJLENBQUMsUUFBUSxHQUFHLE1BQU0sQ0FBQztJQUN4QixDQUFDO0lBRUQsb0JBQW9CLENBQUMsT0FBZ0M7UUFDcEQsSUFBSSxDQUFDLE9BQU8sRUFBRTtZQUNiLE9BQU87U0FDUDtRQUNELE9BQU8sQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsdUJBQXVCLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDNUUsQ0FBQztJQUVELGtCQUFrQixDQUFDLE9BQWdDO1FBQ2xELElBQUksQ0FBQyxPQUFPLEVBQUU7WUFDYixPQUFPO1NBQ1A7UUFDRCxPQUFPLENBQUMsU0FBUyxJQUFJLG1CQUFtQixDQUFDO0lBQzFDLENBQUM7Q0FDRDtBQTNCRCw0Q0EyQkM7Ozs7Ozs7Ozs7Ozs7O0FDakNEOzs7Z0dBR2dHOztBQUVoRyxTQUFnQixrQkFBa0IsQ0FBQyxDQUFhO0lBQy9DLElBQUksUUFBUSxDQUFDLFVBQVUsS0FBSyxTQUFTLElBQUksUUFBUSxDQUFDLFVBQW9CLEtBQUssZUFBZSxFQUFFO1FBQzNGLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxrQkFBa0IsRUFBRSxDQUFDLENBQUMsQ0FBQztLQUNqRDtTQUFNO1FBQ04sQ0FBQyxFQUFFLENBQUM7S0FDSjtBQUNGLENBQUM7QUFORCxnREFNQzs7Ozs7Ozs7Ozs7Ozs7QUNYRDs7O2dHQUdnRzs7QUFFaEcsOEdBQXNEO0FBQ3RELGdGQUE4QztBQUM5Qyx5RkFBb0Q7QUFDcEQsK0ZBQXNIO0FBQ3RILHNGQUFrRDtBQUNsRCx1R0FBNkM7QUFJN0MsSUFBSSxjQUFjLEdBQUcsSUFBSSxDQUFDO0FBQzFCLE1BQU0sTUFBTSxHQUFHLElBQUksbUNBQWdCLEVBQUUsQ0FBQztBQUN0QyxNQUFNLFFBQVEsR0FBRyxzQkFBVyxFQUFFLENBQUM7QUFFL0IsTUFBTSxNQUFNLEdBQUcsZ0JBQWdCLEVBQUUsQ0FBQztBQUVsQyxvQkFBb0I7QUFDcEIsSUFBSSxLQUFLLEdBQUcsa0JBQU8sQ0FBc0MsWUFBWSxDQUFDLENBQUM7QUFDdkUsTUFBTSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUV2QixNQUFNLFNBQVMsR0FBRyxpQ0FBcUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUVoRCxNQUFNLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUN2QyxNQUFNLENBQUMsbUJBQW1CLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0FBRWhELE1BQU0sQ0FBQyxNQUFNLEdBQUcsR0FBRyxFQUFFO0lBQ3BCLGdCQUFnQixFQUFFLENBQUM7QUFDcEIsQ0FBQyxDQUFDO0FBRUYsMkJBQWtCLENBQUMsR0FBRyxFQUFFO0lBQ3ZCLElBQUksUUFBUSxDQUFDLHVCQUF1QixFQUFFO1FBQ3JDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDZix5Q0FBeUM7WUFDekMsSUFBSSxLQUFLLENBQUMsUUFBUSxFQUFFO2dCQUNuQixNQUFNLE9BQU8sR0FBRyx1Q0FBeUIsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQzFELElBQUksT0FBTyxFQUFFO29CQUNaLGNBQWMsR0FBRyxJQUFJLENBQUM7b0JBQ3RCLHNDQUF3QixDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztpQkFDdkM7YUFDRDtpQkFBTTtnQkFDTixNQUFNLFdBQVcsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7Z0JBQ25DLElBQUksQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLEVBQUU7b0JBQ3hCLGNBQWMsR0FBRyxJQUFJLENBQUM7b0JBQ3RCLHNDQUF3QixDQUFDLFdBQVcsQ0FBQyxDQUFDO2lCQUN0QzthQUNEO1FBQ0YsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0tBQ047QUFDRixDQUFDLENBQUMsQ0FBQztBQUVILE1BQU0sWUFBWSxHQUFHLENBQUMsR0FBRyxFQUFFO0lBQzFCLE1BQU0sUUFBUSxHQUFHLFFBQVEsQ0FBQyxDQUFDLElBQVksRUFBRSxFQUFFO1FBQzFDLGNBQWMsR0FBRyxJQUFJLENBQUM7UUFDdEIsc0NBQXdCLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDaEMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBRVAsT0FBTyxDQUFDLElBQVksRUFBRSxRQUFhLEVBQUUsRUFBRTtRQUN0QyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFO1lBQ2pCLFFBQVEsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1lBQ3JCLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUNmO0lBQ0YsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMLElBQUksZ0JBQWdCLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRTtJQUNwQyxNQUFNLFNBQVMsR0FBb0QsRUFBRSxDQUFDO0lBQ3RFLElBQUksTUFBTSxHQUFHLFFBQVEsQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNsRCxJQUFJLE1BQU0sRUFBRTtRQUNYLElBQUksQ0FBQyxDQUFDO1FBQ04sS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ25DLE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUV0QixJQUFJLEdBQUcsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxFQUFFO2dCQUN0QyxHQUFHLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQzthQUNoQztZQUVELFNBQVMsQ0FBQyxJQUFJLENBQUM7Z0JBQ2QsRUFBRSxFQUFFLEdBQUcsQ0FBQyxFQUFFO2dCQUNWLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTTtnQkFDbEIsS0FBSyxFQUFFLEdBQUcsQ0FBQyxLQUFLO2FBQ2hCLENBQUMsQ0FBQztTQUNIO1FBRUQsU0FBUyxDQUFDLFdBQVcsQ0FBQyxpQkFBaUIsRUFBRSxTQUFTLENBQUMsQ0FBQztLQUNwRDtBQUNGLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUVQLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLEVBQUUsR0FBRyxFQUFFO0lBQ3RDLGNBQWMsR0FBRyxJQUFJLENBQUM7SUFDdEIsZ0JBQWdCLEVBQUUsQ0FBQztBQUNwQixDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFFVCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLEtBQUssQ0FBQyxFQUFFO0lBQzFDLElBQUksS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEtBQUssUUFBUSxDQUFDLE1BQU0sRUFBRTtRQUMxQyxPQUFPO0tBQ1A7SUFFRCxRQUFRLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFO1FBQ3hCLEtBQUssZ0NBQWdDO1lBQ3BDLE1BQU0sQ0FBQyw4QkFBOEIsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3ZELE1BQU07UUFFUCxLQUFLLFlBQVk7WUFDaEIsWUFBWSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQ3hDLE1BQU07S0FDUDtBQUNGLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztBQUVWLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLEVBQUU7SUFDN0MsSUFBSSxDQUFDLFFBQVEsQ0FBQywyQkFBMkIsRUFBRTtRQUMxQyxPQUFPO0tBQ1A7SUFFRCx5QkFBeUI7SUFDekIsS0FBSyxJQUFJLElBQUksR0FBRyxLQUFLLENBQUMsTUFBcUIsRUFBRSxJQUFJLEVBQUUsSUFBSSxHQUFHLElBQUksQ0FBQyxVQUF5QixFQUFFO1FBQ3pGLElBQUksSUFBSSxDQUFDLE9BQU8sS0FBSyxHQUFHLEVBQUU7WUFDekIsT0FBTztTQUNQO0tBQ0Q7SUFFRCxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDO0lBQzNCLE1BQU0sSUFBSSxHQUFHLDhDQUFnQyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3RELElBQUksT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFO1FBQzdDLFNBQVMsQ0FBQyxXQUFXLENBQUMsVUFBVSxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0tBQzlEO0FBQ0YsQ0FBQyxDQUFDLENBQUM7QUFFSCxRQUFRLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxFQUFFO0lBQzFDLElBQUksQ0FBQyxLQUFLLEVBQUU7UUFDWCxPQUFPO0tBQ1A7SUFFRCxJQUFJLElBQUksR0FBUSxLQUFLLENBQUMsTUFBTSxDQUFDO0lBQzdCLE9BQU8sSUFBSSxFQUFFO1FBQ1osSUFBSSxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUU7WUFDdEQsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsRUFBRTtnQkFDOUMsTUFBTTthQUNOO1lBQ0QsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxtQkFBbUIsQ0FBQyxFQUFFO2dCQUN0SSxNQUFNLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGdDQUFnQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLE1BQU0sQ0FBQyxJQUFJLFlBQVksQ0FBQyxRQUFRLENBQUMsbUJBQW1CLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ2xLLFNBQVMsQ0FBQyxXQUFXLENBQUMsV0FBVyxFQUFFLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUM7Z0JBQ3ZELEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFDdkIsS0FBSyxDQUFDLGVBQWUsRUFBRSxDQUFDO2dCQUN4QixNQUFNO2FBQ047WUFDRCxNQUFNO1NBQ047UUFDRCxJQUFJLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQztLQUN2QjtBQUNGLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUVULElBQUksUUFBUSxDQUFDLHVCQUF1QixFQUFFO0lBQ3JDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxRQUFRLEVBQUUsUUFBUSxDQUFDLEdBQUcsRUFBRTtRQUMvQyxJQUFJLGNBQWMsRUFBRTtZQUNuQixjQUFjLEdBQUcsS0FBSyxDQUFDO1NBQ3ZCO2FBQU07WUFDTixNQUFNLElBQUksR0FBRyw4Q0FBZ0MsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDOUQsSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQzdDLFNBQVMsQ0FBQyxXQUFXLENBQUMsWUFBWSxFQUFFLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztnQkFDOUMsS0FBSyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7Z0JBQ2xCLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7YUFDdkI7U0FDRDtJQUNGLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0NBQ1I7QUFFRCxTQUFTLFlBQVksQ0FBQyxJQUFZO0lBQ2pDLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQywwQkFBMEIsRUFBRSxNQUFNLENBQUMsQ0FBQztBQUN6RCxDQUFDOzs7Ozs7Ozs7Ozs7OztBQzVLRDs7O2dHQUdnRzs7QUFFaEcsc0ZBQXlDO0FBUzVCLDZCQUFxQixHQUFHLENBQUMsTUFBVyxFQUFFLEVBQUU7SUFDcEQsT0FBTyxJQUFJO1FBQ1YsV0FBVyxDQUFDLElBQVksRUFBRSxJQUFZO1lBQ3JDLE1BQU0sQ0FBQyxXQUFXLENBQUM7Z0JBQ2xCLElBQUk7Z0JBQ0osTUFBTSxFQUFFLHNCQUFXLEVBQUUsQ0FBQyxNQUFNO2dCQUM1QixJQUFJO2FBQ0osQ0FBQyxDQUFDO1FBQ0osQ0FBQztLQUNELENBQUM7QUFDSCxDQUFDLENBQUM7Ozs7Ozs7Ozs7Ozs7O0FDeEJGOzs7Z0dBR2dHOztBQUVoRyxzRkFBeUM7QUFHekMsU0FBUyxLQUFLLENBQUMsR0FBVyxFQUFFLEdBQVcsRUFBRSxLQUFhO0lBQ3JELE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBRUQsU0FBUyxTQUFTLENBQUMsSUFBWTtJQUM5QixPQUFPLEtBQUssQ0FBQyxDQUFDLEVBQUUsc0JBQVcsRUFBRSxDQUFDLFNBQVMsR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQVFELE1BQU0sbUJBQW1CLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDakMsSUFBSSxRQUEyQixDQUFDO0lBQ2hDLE9BQU8sR0FBRyxFQUFFO1FBQ1gsSUFBSSxDQUFDLFFBQVEsRUFBRTtZQUNkLFFBQVEsR0FBRyxDQUFDLEVBQUUsT0FBTyxFQUFFLFFBQVEsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDakQsS0FBSyxNQUFNLE9BQU8sSUFBSSxRQUFRLENBQUMsc0JBQXNCLENBQUMsV0FBVyxDQUFDLEVBQUU7Z0JBQ25FLE1BQU0sSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUUsQ0FBQztnQkFDakQsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRTtvQkFDakIsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxPQUFzQixFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7aUJBQ3pEO2FBQ0Q7U0FDRDtRQUNELE9BQU8sUUFBUSxDQUFDO0lBQ2pCLENBQUMsQ0FBQztBQUNILENBQUMsQ0FBQyxFQUFFLENBQUM7QUFFTDs7Ozs7R0FLRztBQUNILFNBQWdCLHdCQUF3QixDQUFDLFVBQWtCO0lBQzFELE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDMUMsTUFBTSxLQUFLLEdBQUcsbUJBQW1CLEVBQUUsQ0FBQztJQUNwQyxJQUFJLFFBQVEsR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDO0lBQ2hDLEtBQUssTUFBTSxLQUFLLElBQUksS0FBSyxFQUFFO1FBQzFCLElBQUksS0FBSyxDQUFDLElBQUksS0FBSyxVQUFVLEVBQUU7WUFDOUIsT0FBTyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO1NBQzVDO2FBQU0sSUFBSSxLQUFLLENBQUMsSUFBSSxHQUFHLFVBQVUsRUFBRTtZQUNuQyxPQUFPLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FBQztTQUNqQztRQUNELFFBQVEsR0FBRyxLQUFLLENBQUM7S0FDakI7SUFDRCxPQUFPLEVBQUUsUUFBUSxFQUFFLENBQUM7QUFDckIsQ0FBQztBQWJELDREQWFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFnQiwyQkFBMkIsQ0FBQyxNQUFjO0lBQ3pELE1BQU0sS0FBSyxHQUFHLG1CQUFtQixFQUFFLENBQUM7SUFDcEMsTUFBTSxRQUFRLEdBQUcsTUFBTSxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUM7SUFDekMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDWixJQUFJLEVBQUUsR0FBRyxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUMxQixPQUFPLEVBQUUsR0FBRyxDQUFDLEdBQUcsRUFBRSxFQUFFO1FBQ25CLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDdEMsTUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBQzFELElBQUksTUFBTSxDQUFDLEdBQUcsR0FBRyxNQUFNLENBQUMsTUFBTSxJQUFJLFFBQVEsRUFBRTtZQUMzQyxFQUFFLEdBQUcsR0FBRyxDQUFDO1NBQ1Q7YUFDSTtZQUNKLEVBQUUsR0FBRyxHQUFHLENBQUM7U0FDVDtLQUNEO0lBQ0QsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzVCLE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztJQUMzRCxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksUUFBUSxDQUFDLEdBQUcsR0FBRyxRQUFRLEVBQUU7UUFDdkMsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzVCLE9BQU8sRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQztLQUNoRDtJQUNELE9BQU8sRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLENBQUM7QUFDaEMsQ0FBQztBQXRCRCxrRUFzQkM7QUFFRDs7R0FFRztBQUNILFNBQWdCLHdCQUF3QixDQUFDLElBQVk7SUFDcEQsSUFBSSxDQUFDLHNCQUFXLEVBQUUsQ0FBQyx1QkFBdUIsRUFBRTtRQUMzQyxPQUFPO0tBQ1A7SUFFRCxJQUFJLElBQUksSUFBSSxDQUFDLEVBQUU7UUFDZCxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDakMsT0FBTztLQUNQO0lBRUQsTUFBTSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsR0FBRyx3QkFBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUMxRCxJQUFJLENBQUMsUUFBUSxFQUFFO1FBQ2QsT0FBTztLQUNQO0lBQ0QsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO0lBQ2pCLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztJQUN0RCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO0lBQzdCLElBQUksSUFBSSxJQUFJLElBQUksQ0FBQyxJQUFJLEtBQUssUUFBUSxDQUFDLElBQUksRUFBRTtRQUN4Qyw4REFBOEQ7UUFDOUQsTUFBTSxlQUFlLEdBQUcsQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDN0UsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDLEdBQUcsR0FBRyxXQUFXLENBQUM7UUFDN0UsUUFBUSxHQUFHLFdBQVcsR0FBRyxlQUFlLEdBQUcsYUFBYSxDQUFDO0tBQ3pEO1NBQU07UUFDTixNQUFNLGlCQUFpQixHQUFHLElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2xELFFBQVEsR0FBRyxXQUFXLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxHQUFHLGlCQUFpQixDQUFDLENBQUM7S0FDM0Q7SUFDRCxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFDO0FBQ3ZFLENBQUM7QUEzQkQsNERBMkJDO0FBRUQsU0FBZ0IsZ0NBQWdDLENBQUMsTUFBYztJQUM5RCxNQUFNLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxHQUFHLDJCQUEyQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQy9ELElBQUksUUFBUSxFQUFFO1FBQ2IsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBQ2hFLE1BQU0sa0JBQWtCLEdBQUcsQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDMUUsSUFBSSxJQUFJLEVBQUU7WUFDVCxNQUFNLHVCQUF1QixHQUFHLGtCQUFrQixHQUFHLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDLEdBQUcsR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDckgsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksR0FBRyx1QkFBdUIsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ25GLE9BQU8sU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO1NBQ3ZCO2FBQ0k7WUFDSixNQUFNLHFCQUFxQixHQUFHLGtCQUFrQixHQUFHLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQzNFLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLEdBQUcscUJBQXFCLENBQUM7WUFDbkQsT0FBTyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDdkI7S0FDRDtJQUNELE9BQU8sSUFBSSxDQUFDO0FBQ2IsQ0FBQztBQWpCRCw0RUFpQkM7QUFFRDs7R0FFRztBQUNILFNBQWdCLHlCQUF5QixDQUFDLFFBQWdCO0lBQ3pELE9BQU8sbUJBQW1CLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtRQUM3QyxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxLQUFLLFFBQVEsQ0FBQztJQUN4QyxDQUFDLENBQUMsQ0FBQztBQUNKLENBQUM7QUFKRCw4REFJQzs7Ozs7Ozs7Ozs7Ozs7QUNoSkQ7OztnR0FHZ0c7O0FBYWhHLElBQUksY0FBYyxHQUFnQyxTQUFTLENBQUM7QUFFNUQsU0FBZ0IsT0FBTyxDQUFTLEdBQVc7SUFDMUMsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0lBQ3hFLElBQUksT0FBTyxFQUFFO1FBQ1osTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QyxJQUFJLElBQUksRUFBRTtZQUNULE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN4QjtLQUNEO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsR0FBRyxFQUFFLENBQUMsQ0FBQztBQUNuRCxDQUFDO0FBVkQsMEJBVUM7QUFFRCxTQUFnQixXQUFXO0lBQzFCLElBQUksY0FBYyxFQUFFO1FBQ25CLE9BQU8sY0FBYyxDQUFDO0tBQ3RCO0lBRUQsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUMxQyxJQUFJLGNBQWMsRUFBRTtRQUNuQixPQUFPLGNBQWMsQ0FBQztLQUN0QjtJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBWEQsa0NBV0MiLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHtcbiBcdFx0XHRcdGNvbmZpZ3VyYWJsZTogZmFsc2UsXG4gXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuIFx0XHRcdFx0Z2V0OiBnZXR0ZXJcbiBcdFx0XHR9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9wcmV2aWV3LXNyYy9pbmRleC50c1wiKTtcbiIsIi8qKlxuICogbG9kYXNoIChDdXN0b20gQnVpbGQpIDxodHRwczovL2xvZGFzaC5jb20vPlxuICogQnVpbGQ6IGBsb2Rhc2ggbW9kdWxhcml6ZSBleHBvcnRzPVwibnBtXCIgLW8gLi9gXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9ycyA8aHR0cHM6Ly9qcXVlcnkub3JnLz5cbiAqIFJlbGVhc2VkIHVuZGVyIE1JVCBsaWNlbnNlIDxodHRwczovL2xvZGFzaC5jb20vbGljZW5zZT5cbiAqIEJhc2VkIG9uIFVuZGVyc2NvcmUuanMgMS44LjMgPGh0dHA6Ly91bmRlcnNjb3JlanMub3JnL0xJQ0VOU0U+XG4gKiBDb3B5cmlnaHQgSmVyZW15IEFzaGtlbmFzLCBEb2N1bWVudENsb3VkIGFuZCBJbnZlc3RpZ2F0aXZlIFJlcG9ydGVycyAmIEVkaXRvcnNcbiAqL1xuXG4vKiogVXNlZCBhcyB0aGUgYFR5cGVFcnJvcmAgbWVzc2FnZSBmb3IgXCJGdW5jdGlvbnNcIiBtZXRob2RzLiAqL1xudmFyIEZVTkNfRVJST1JfVEVYVCA9ICdFeHBlY3RlZCBhIGZ1bmN0aW9uJztcblxuLyoqIFVzZWQgYXMgcmVmZXJlbmNlcyBmb3IgdmFyaW91cyBgTnVtYmVyYCBjb25zdGFudHMuICovXG52YXIgTkFOID0gMCAvIDA7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBzeW1ib2xUYWcgPSAnW29iamVjdCBTeW1ib2xdJztcblxuLyoqIFVzZWQgdG8gbWF0Y2ggbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZS4gKi9cbnZhciByZVRyaW0gPSAvXlxccyt8XFxzKyQvZztcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJhZCBzaWduZWQgaGV4YWRlY2ltYWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmFkSGV4ID0gL15bLStdMHhbMC05YS1mXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBiaW5hcnkgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzQmluYXJ5ID0gL14wYlswMV0rJC9pO1xuXG4vKiogVXNlZCB0byBkZXRlY3Qgb2N0YWwgc3RyaW5nIHZhbHVlcy4gKi9cbnZhciByZUlzT2N0YWwgPSAvXjBvWzAtN10rJC9pO1xuXG4vKiogQnVpbHQtaW4gbWV0aG9kIHJlZmVyZW5jZXMgd2l0aG91dCBhIGRlcGVuZGVuY3kgb24gYHJvb3RgLiAqL1xudmFyIGZyZWVQYXJzZUludCA9IHBhcnNlSW50O1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYGdsb2JhbGAgZnJvbSBOb2RlLmpzLiAqL1xudmFyIGZyZWVHbG9iYWwgPSB0eXBlb2YgZ2xvYmFsID09ICdvYmplY3QnICYmIGdsb2JhbCAmJiBnbG9iYWwuT2JqZWN0ID09PSBPYmplY3QgJiYgZ2xvYmFsO1xuXG4vKiogRGV0ZWN0IGZyZWUgdmFyaWFibGUgYHNlbGZgLiAqL1xudmFyIGZyZWVTZWxmID0gdHlwZW9mIHNlbGYgPT0gJ29iamVjdCcgJiYgc2VsZiAmJiBzZWxmLk9iamVjdCA9PT0gT2JqZWN0ICYmIHNlbGY7XG5cbi8qKiBVc2VkIGFzIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb2JqZWN0LiAqL1xudmFyIHJvb3QgPSBmcmVlR2xvYmFsIHx8IGZyZWVTZWxmIHx8IEZ1bmN0aW9uKCdyZXR1cm4gdGhpcycpKCk7XG5cbi8qKiBVc2VkIGZvciBidWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZVxuICogW2B0b1N0cmluZ1RhZ2BdKGh0dHA6Ly9lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmplY3RUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyBmb3IgdGhvc2Ugd2l0aCB0aGUgc2FtZSBuYW1lIGFzIG90aGVyIGBsb2Rhc2hgIG1ldGhvZHMuICovXG52YXIgbmF0aXZlTWF4ID0gTWF0aC5tYXgsXG4gICAgbmF0aXZlTWluID0gTWF0aC5taW47XG5cbi8qKlxuICogR2V0cyB0aGUgdGltZXN0YW1wIG9mIHRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRoYXQgaGF2ZSBlbGFwc2VkIHNpbmNlXG4gKiB0aGUgVW5peCBlcG9jaCAoMSBKYW51YXJ5IDE5NzAgMDA6MDA6MDAgVVRDKS5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDIuNC4wXG4gKiBAY2F0ZWdvcnkgRGF0ZVxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgdGltZXN0YW1wLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmRlZmVyKGZ1bmN0aW9uKHN0YW1wKSB7XG4gKiAgIGNvbnNvbGUubG9nKF8ubm93KCkgLSBzdGFtcCk7XG4gKiB9LCBfLm5vdygpKTtcbiAqIC8vID0+IExvZ3MgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgaXQgdG9vayBmb3IgdGhlIGRlZmVycmVkIGludm9jYXRpb24uXG4gKi9cbnZhciBub3cgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHJvb3QuRGF0ZS5ub3coKTtcbn07XG5cbi8qKlxuICogQ3JlYXRlcyBhIGRlYm91bmNlZCBmdW5jdGlvbiB0aGF0IGRlbGF5cyBpbnZva2luZyBgZnVuY2AgdW50aWwgYWZ0ZXIgYHdhaXRgXG4gKiBtaWxsaXNlY29uZHMgaGF2ZSBlbGFwc2VkIHNpbmNlIHRoZSBsYXN0IHRpbWUgdGhlIGRlYm91bmNlZCBmdW5jdGlvbiB3YXNcbiAqIGludm9rZWQuIFRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgIG1ldGhvZCB0byBjYW5jZWxcbiAqIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLlxuICogUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2Agc2hvdWxkIGJlIGludm9rZWQgb24gdGhlXG4gKiBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGAgdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkXG4gKiB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50XG4gKiBjYWxscyB0byB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHJldHVybiB0aGUgcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYFxuICogaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIGRlYm91bmNlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy5kZWJvdW5jZWAgYW5kIGBfLnRocm90dGxlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGRlYm91bmNlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIGRlbGF5LlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9ZmFsc2VdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgbGVhZGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHBhcmFtIHtudW1iZXJ9IFtvcHRpb25zLm1heFdhaXRdXG4gKiAgVGhlIG1heGltdW0gdGltZSBgZnVuY2AgaXMgYWxsb3dlZCB0byBiZSBkZWxheWVkIGJlZm9yZSBpdCdzIGludm9rZWQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGRlYm91bmNlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgY29zdGx5IGNhbGN1bGF0aW9ucyB3aGlsZSB0aGUgd2luZG93IHNpemUgaXMgaW4gZmx1eC5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdyZXNpemUnLCBfLmRlYm91bmNlKGNhbGN1bGF0ZUxheW91dCwgMTUwKSk7XG4gKlxuICogLy8gSW52b2tlIGBzZW5kTWFpbGAgd2hlbiBjbGlja2VkLCBkZWJvdW5jaW5nIHN1YnNlcXVlbnQgY2FsbHMuXG4gKiBqUXVlcnkoZWxlbWVudCkub24oJ2NsaWNrJywgXy5kZWJvdW5jZShzZW5kTWFpbCwgMzAwLCB7XG4gKiAgICdsZWFkaW5nJzogdHJ1ZSxcbiAqICAgJ3RyYWlsaW5nJzogZmFsc2VcbiAqIH0pKTtcbiAqXG4gKiAvLyBFbnN1cmUgYGJhdGNoTG9nYCBpcyBpbnZva2VkIG9uY2UgYWZ0ZXIgMSBzZWNvbmQgb2YgZGVib3VuY2VkIGNhbGxzLlxuICogdmFyIGRlYm91bmNlZCA9IF8uZGVib3VuY2UoYmF0Y2hMb2csIDI1MCwgeyAnbWF4V2FpdCc6IDEwMDAgfSk7XG4gKiB2YXIgc291cmNlID0gbmV3IEV2ZW50U291cmNlKCcvc3RyZWFtJyk7XG4gKiBqUXVlcnkoc291cmNlKS5vbignbWVzc2FnZScsIGRlYm91bmNlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyBkZWJvdW5jZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIGRlYm91bmNlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiBkZWJvdW5jZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsYXN0QXJncyxcbiAgICAgIGxhc3RUaGlzLFxuICAgICAgbWF4V2FpdCxcbiAgICAgIHJlc3VsdCxcbiAgICAgIHRpbWVySWQsXG4gICAgICBsYXN0Q2FsbFRpbWUsXG4gICAgICBsYXN0SW52b2tlVGltZSA9IDAsXG4gICAgICBsZWFkaW5nID0gZmFsc2UsXG4gICAgICBtYXhpbmcgPSBmYWxzZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICB3YWl0ID0gdG9OdW1iZXIod2FpdCkgfHwgMDtcbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICEhb3B0aW9ucy5sZWFkaW5nO1xuICAgIG1heGluZyA9ICdtYXhXYWl0JyBpbiBvcHRpb25zO1xuICAgIG1heFdhaXQgPSBtYXhpbmcgPyBuYXRpdmVNYXgodG9OdW1iZXIob3B0aW9ucy5tYXhXYWl0KSB8fCAwLCB3YWl0KSA6IG1heFdhaXQ7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuXG4gIGZ1bmN0aW9uIGludm9rZUZ1bmModGltZSkge1xuICAgIHZhciBhcmdzID0gbGFzdEFyZ3MsXG4gICAgICAgIHRoaXNBcmcgPSBsYXN0VGhpcztcblxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIHJlc3VsdCA9IGZ1bmMuYXBwbHkodGhpc0FyZywgYXJncyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGxlYWRpbmdFZGdlKHRpbWUpIHtcbiAgICAvLyBSZXNldCBhbnkgYG1heFdhaXRgIHRpbWVyLlxuICAgIGxhc3RJbnZva2VUaW1lID0gdGltZTtcbiAgICAvLyBTdGFydCB0aGUgdGltZXIgZm9yIHRoZSB0cmFpbGluZyBlZGdlLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgLy8gSW52b2tlIHRoZSBsZWFkaW5nIGVkZ2UuXG4gICAgcmV0dXJuIGxlYWRpbmcgPyBpbnZva2VGdW5jKHRpbWUpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gcmVtYWluaW5nV2FpdCh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZSxcbiAgICAgICAgcmVzdWx0ID0gd2FpdCAtIHRpbWVTaW5jZUxhc3RDYWxsO1xuXG4gICAgcmV0dXJuIG1heGluZyA/IG5hdGl2ZU1pbihyZXN1bHQsIG1heFdhaXQgLSB0aW1lU2luY2VMYXN0SW52b2tlKSA6IHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIHNob3VsZEludm9rZSh0aW1lKSB7XG4gICAgdmFyIHRpbWVTaW5jZUxhc3RDYWxsID0gdGltZSAtIGxhc3RDYWxsVGltZSxcbiAgICAgICAgdGltZVNpbmNlTGFzdEludm9rZSA9IHRpbWUgLSBsYXN0SW52b2tlVGltZTtcblxuICAgIC8vIEVpdGhlciB0aGlzIGlzIHRoZSBmaXJzdCBjYWxsLCBhY3Rpdml0eSBoYXMgc3RvcHBlZCBhbmQgd2UncmUgYXQgdGhlXG4gICAgLy8gdHJhaWxpbmcgZWRnZSwgdGhlIHN5c3RlbSB0aW1lIGhhcyBnb25lIGJhY2t3YXJkcyBhbmQgd2UncmUgdHJlYXRpbmdcbiAgICAvLyBpdCBhcyB0aGUgdHJhaWxpbmcgZWRnZSwgb3Igd2UndmUgaGl0IHRoZSBgbWF4V2FpdGAgbGltaXQuXG4gICAgcmV0dXJuIChsYXN0Q2FsbFRpbWUgPT09IHVuZGVmaW5lZCB8fCAodGltZVNpbmNlTGFzdENhbGwgPj0gd2FpdCkgfHxcbiAgICAgICh0aW1lU2luY2VMYXN0Q2FsbCA8IDApIHx8IChtYXhpbmcgJiYgdGltZVNpbmNlTGFzdEludm9rZSA+PSBtYXhXYWl0KSk7XG4gIH1cblxuICBmdW5jdGlvbiB0aW1lckV4cGlyZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKTtcbiAgICBpZiAoc2hvdWxkSW52b2tlKHRpbWUpKSB7XG4gICAgICByZXR1cm4gdHJhaWxpbmdFZGdlKHRpbWUpO1xuICAgIH1cbiAgICAvLyBSZXN0YXJ0IHRoZSB0aW1lci5cbiAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHJlbWFpbmluZ1dhaXQodGltZSkpO1xuICB9XG5cbiAgZnVuY3Rpb24gdHJhaWxpbmdFZGdlKHRpbWUpIHtcbiAgICB0aW1lcklkID0gdW5kZWZpbmVkO1xuXG4gICAgLy8gT25seSBpbnZva2UgaWYgd2UgaGF2ZSBgbGFzdEFyZ3NgIHdoaWNoIG1lYW5zIGBmdW5jYCBoYXMgYmVlblxuICAgIC8vIGRlYm91bmNlZCBhdCBsZWFzdCBvbmNlLlxuICAgIGlmICh0cmFpbGluZyAmJiBsYXN0QXJncykge1xuICAgICAgcmV0dXJuIGludm9rZUZ1bmModGltZSk7XG4gICAgfVxuICAgIGxhc3RBcmdzID0gbGFzdFRoaXMgPSB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGNhbmNlbCgpIHtcbiAgICBpZiAodGltZXJJZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXJJZCk7XG4gICAgfVxuICAgIGxhc3RJbnZva2VUaW1lID0gMDtcbiAgICBsYXN0QXJncyA9IGxhc3RDYWxsVGltZSA9IGxhc3RUaGlzID0gdGltZXJJZCA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIGZ1bmN0aW9uIGZsdXNoKCkge1xuICAgIHJldHVybiB0aW1lcklkID09PSB1bmRlZmluZWQgPyByZXN1bHQgOiB0cmFpbGluZ0VkZ2Uobm93KCkpO1xuICB9XG5cbiAgZnVuY3Rpb24gZGVib3VuY2VkKCkge1xuICAgIHZhciB0aW1lID0gbm93KCksXG4gICAgICAgIGlzSW52b2tpbmcgPSBzaG91bGRJbnZva2UodGltZSk7XG5cbiAgICBsYXN0QXJncyA9IGFyZ3VtZW50cztcbiAgICBsYXN0VGhpcyA9IHRoaXM7XG4gICAgbGFzdENhbGxUaW1lID0gdGltZTtcblxuICAgIGlmIChpc0ludm9raW5nKSB7XG4gICAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiBsZWFkaW5nRWRnZShsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgICAgaWYgKG1heGluZykge1xuICAgICAgICAvLyBIYW5kbGUgaW52b2NhdGlvbnMgaW4gYSB0aWdodCBsb29wLlxuICAgICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgICAgICByZXR1cm4gaW52b2tlRnVuYyhsYXN0Q2FsbFRpbWUpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAodGltZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aW1lcklkID0gc2V0VGltZW91dCh0aW1lckV4cGlyZWQsIHdhaXQpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGRlYm91bmNlZC5jYW5jZWwgPSBjYW5jZWw7XG4gIGRlYm91bmNlZC5mbHVzaCA9IGZsdXNoO1xuICByZXR1cm4gZGVib3VuY2VkO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSB0aHJvdHRsZWQgZnVuY3Rpb24gdGhhdCBvbmx5IGludm9rZXMgYGZ1bmNgIGF0IG1vc3Qgb25jZSBwZXJcbiAqIGV2ZXJ5IGB3YWl0YCBtaWxsaXNlY29uZHMuIFRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gY29tZXMgd2l0aCBhIGBjYW5jZWxgXG4gKiBtZXRob2QgdG8gY2FuY2VsIGRlbGF5ZWQgYGZ1bmNgIGludm9jYXRpb25zIGFuZCBhIGBmbHVzaGAgbWV0aG9kIHRvXG4gKiBpbW1lZGlhdGVseSBpbnZva2UgdGhlbS4gUHJvdmlkZSBgb3B0aW9uc2AgdG8gaW5kaWNhdGUgd2hldGhlciBgZnVuY2BcbiAqIHNob3VsZCBiZSBpbnZva2VkIG9uIHRoZSBsZWFkaW5nIGFuZC9vciB0cmFpbGluZyBlZGdlIG9mIHRoZSBgd2FpdGBcbiAqIHRpbWVvdXQuIFRoZSBgZnVuY2AgaXMgaW52b2tlZCB3aXRoIHRoZSBsYXN0IGFyZ3VtZW50cyBwcm92aWRlZCB0byB0aGVcbiAqIHRocm90dGxlZCBmdW5jdGlvbi4gU3Vic2VxdWVudCBjYWxscyB0byB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uIHJldHVybiB0aGVcbiAqIHJlc3VsdCBvZiB0aGUgbGFzdCBgZnVuY2AgaW52b2NhdGlvbi5cbiAqXG4gKiAqKk5vdGU6KiogSWYgYGxlYWRpbmdgIGFuZCBgdHJhaWxpbmdgIG9wdGlvbnMgYXJlIGB0cnVlYCwgYGZ1bmNgIGlzXG4gKiBpbnZva2VkIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0IG9ubHkgaWYgdGhlIHRocm90dGxlZCBmdW5jdGlvblxuICogaXMgaW52b2tlZCBtb3JlIHRoYW4gb25jZSBkdXJpbmcgdGhlIGB3YWl0YCB0aW1lb3V0LlxuICpcbiAqIElmIGB3YWl0YCBpcyBgMGAgYW5kIGBsZWFkaW5nYCBpcyBgZmFsc2VgLCBgZnVuY2AgaW52b2NhdGlvbiBpcyBkZWZlcnJlZFxuICogdW50aWwgdG8gdGhlIG5leHQgdGljaywgc2ltaWxhciB0byBgc2V0VGltZW91dGAgd2l0aCBhIHRpbWVvdXQgb2YgYDBgLlxuICpcbiAqIFNlZSBbRGF2aWQgQ29yYmFjaG8ncyBhcnRpY2xlXShodHRwczovL2Nzcy10cmlja3MuY29tL2RlYm91bmNpbmctdGhyb3R0bGluZy1leHBsYWluZWQtZXhhbXBsZXMvKVxuICogZm9yIGRldGFpbHMgb3ZlciB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBgXy50aHJvdHRsZWAgYW5kIGBfLmRlYm91bmNlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDAuMS4wXG4gKiBAY2F0ZWdvcnkgRnVuY3Rpb25cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIHRocm90dGxlLlxuICogQHBhcmFtIHtudW1iZXJ9IFt3YWl0PTBdIFRoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHRocm90dGxlIGludm9jYXRpb25zIHRvLlxuICogQHBhcmFtIHtPYmplY3R9IFtvcHRpb25zPXt9XSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmxlYWRpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLnRyYWlsaW5nPXRydWVdXG4gKiAgU3BlY2lmeSBpbnZva2luZyBvbiB0aGUgdHJhaWxpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IHRocm90dGxlZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogLy8gQXZvaWQgZXhjZXNzaXZlbHkgdXBkYXRpbmcgdGhlIHBvc2l0aW9uIHdoaWxlIHNjcm9sbGluZy5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdzY3JvbGwnLCBfLnRocm90dGxlKHVwZGF0ZVBvc2l0aW9uLCAxMDApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHJlbmV3VG9rZW5gIHdoZW4gdGhlIGNsaWNrIGV2ZW50IGlzIGZpcmVkLCBidXQgbm90IG1vcmUgdGhhbiBvbmNlIGV2ZXJ5IDUgbWludXRlcy5cbiAqIHZhciB0aHJvdHRsZWQgPSBfLnRocm90dGxlKHJlbmV3VG9rZW4sIDMwMDAwMCwgeyAndHJhaWxpbmcnOiBmYWxzZSB9KTtcbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCB0aHJvdHRsZWQpO1xuICpcbiAqIC8vIENhbmNlbCB0aGUgdHJhaWxpbmcgdGhyb3R0bGVkIGludm9jYXRpb24uXG4gKiBqUXVlcnkod2luZG93KS5vbigncG9wc3RhdGUnLCB0aHJvdHRsZWQuY2FuY2VsKTtcbiAqL1xuZnVuY3Rpb24gdGhyb3R0bGUoZnVuYywgd2FpdCwgb3B0aW9ucykge1xuICB2YXIgbGVhZGluZyA9IHRydWUsXG4gICAgICB0cmFpbGluZyA9IHRydWU7XG5cbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgaWYgKGlzT2JqZWN0KG9wdGlvbnMpKSB7XG4gICAgbGVhZGluZyA9ICdsZWFkaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLmxlYWRpbmcgOiBsZWFkaW5nO1xuICAgIHRyYWlsaW5nID0gJ3RyYWlsaW5nJyBpbiBvcHRpb25zID8gISFvcHRpb25zLnRyYWlsaW5nIDogdHJhaWxpbmc7XG4gIH1cbiAgcmV0dXJuIGRlYm91bmNlKGZ1bmMsIHdhaXQsIHtcbiAgICAnbGVhZGluZyc6IGxlYWRpbmcsXG4gICAgJ21heFdhaXQnOiB3YWl0LFxuICAgICd0cmFpbGluZyc6IHRyYWlsaW5nXG4gIH0pO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIHRoZVxuICogW2xhbmd1YWdlIHR5cGVdKGh0dHA6Ly93d3cuZWNtYS1pbnRlcm5hdGlvbmFsLm9yZy9lY21hLTI2Mi83LjAvI3NlYy1lY21hc2NyaXB0LWxhbmd1YWdlLXR5cGVzKVxuICogb2YgYE9iamVjdGAuIChlLmcuIGFycmF5cywgZnVuY3Rpb25zLCBvYmplY3RzLCByZWdleGVzLCBgbmV3IE51bWJlcigwKWAsIGFuZCBgbmV3IFN0cmluZygnJylgKVxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGFuIG9iamVjdCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0KHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChfLm5vb3ApO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QobnVsbCk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdCh2YWx1ZSkge1xuICB2YXIgdHlwZSA9IHR5cGVvZiB2YWx1ZTtcbiAgcmV0dXJuICEhdmFsdWUgJiYgKHR5cGUgPT0gJ29iamVjdCcgfHwgdHlwZSA9PSAnZnVuY3Rpb24nKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZS4gQSB2YWx1ZSBpcyBvYmplY3QtbGlrZSBpZiBpdCdzIG5vdCBgbnVsbGBcbiAqIGFuZCBoYXMgYSBgdHlwZW9mYCByZXN1bHQgb2YgXCJvYmplY3RcIi5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBvYmplY3QtbGlrZSwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZSh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdExpa2UoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShfLm5vb3ApO1xuICogLy8gPT4gZmFsc2VcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0TGlrZSh2YWx1ZSkge1xuICByZXR1cm4gISF2YWx1ZSAmJiB0eXBlb2YgdmFsdWUgPT0gJ29iamVjdCc7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgY2xhc3NpZmllZCBhcyBhIGBTeW1ib2xgIHByaW1pdGl2ZSBvciBvYmplY3QuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSA0LjAuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYSBzeW1ib2wsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc1N5bWJvbChTeW1ib2wuaXRlcmF0b3IpO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNTeW1ib2woJ2FiYycpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNTeW1ib2wodmFsdWUpIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PSAnc3ltYm9sJyB8fFxuICAgIChpc09iamVjdExpa2UodmFsdWUpICYmIG9iamVjdFRvU3RyaW5nLmNhbGwodmFsdWUpID09IHN5bWJvbFRhZyk7XG59XG5cbi8qKlxuICogQ29udmVydHMgYHZhbHVlYCB0byBhIG51bWJlci5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gcHJvY2Vzcy5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIG51bWJlci5cbiAqIEBleGFtcGxlXG4gKlxuICogXy50b051bWJlcigzLjIpO1xuICogLy8gPT4gMy4yXG4gKlxuICogXy50b051bWJlcihOdW1iZXIuTUlOX1ZBTFVFKTtcbiAqIC8vID0+IDVlLTMyNFxuICpcbiAqIF8udG9OdW1iZXIoSW5maW5pdHkpO1xuICogLy8gPT4gSW5maW5pdHlcbiAqXG4gKiBfLnRvTnVtYmVyKCczLjInKTtcbiAqIC8vID0+IDMuMlxuICovXG5mdW5jdGlvbiB0b051bWJlcih2YWx1ZSkge1xuICBpZiAodHlwZW9mIHZhbHVlID09ICdudW1iZXInKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIGlmIChpc1N5bWJvbCh2YWx1ZSkpIHtcbiAgICByZXR1cm4gTkFOO1xuICB9XG4gIGlmIChpc09iamVjdCh2YWx1ZSkpIHtcbiAgICB2YXIgb3RoZXIgPSB0eXBlb2YgdmFsdWUudmFsdWVPZiA9PSAnZnVuY3Rpb24nID8gdmFsdWUudmFsdWVPZigpIDogdmFsdWU7XG4gICAgdmFsdWUgPSBpc09iamVjdChvdGhlcikgPyAob3RoZXIgKyAnJykgOiBvdGhlcjtcbiAgfVxuICBpZiAodHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHZhbHVlID09PSAwID8gdmFsdWUgOiArdmFsdWU7XG4gIH1cbiAgdmFsdWUgPSB2YWx1ZS5yZXBsYWNlKHJlVHJpbSwgJycpO1xuICB2YXIgaXNCaW5hcnkgPSByZUlzQmluYXJ5LnRlc3QodmFsdWUpO1xuICByZXR1cm4gKGlzQmluYXJ5IHx8IHJlSXNPY3RhbC50ZXN0KHZhbHVlKSlcbiAgICA/IGZyZWVQYXJzZUludCh2YWx1ZS5zbGljZSgyKSwgaXNCaW5hcnkgPyAyIDogOClcbiAgICA6IChyZUlzQmFkSGV4LnRlc3QodmFsdWUpID8gTkFOIDogK3ZhbHVlKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB0aHJvdHRsZTtcbiIsInZhciBnO1xyXG5cclxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcclxuZyA9IChmdW5jdGlvbigpIHtcclxuXHRyZXR1cm4gdGhpcztcclxufSkoKTtcclxuXHJcbnRyeSB7XHJcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXHJcblx0ZyA9IGcgfHwgRnVuY3Rpb24oXCJyZXR1cm4gdGhpc1wiKSgpIHx8ICgxLCBldmFsKShcInRoaXNcIik7XHJcbn0gY2F0Y2ggKGUpIHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIHRoZSB3aW5kb3cgcmVmZXJlbmNlIGlzIGF2YWlsYWJsZVxyXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xyXG59XHJcblxyXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXHJcbi8vIFdlIHJldHVybiB1bmRlZmluZWQsIGluc3RlYWQgb2Ygbm90aGluZyBoZXJlLCBzbyBpdCdzXHJcbi8vIGVhc2llciB0byBoYW5kbGUgdGhpcyBjYXNlLiBpZighZ2xvYmFsKSB7IC4uLn1cclxuXHJcbm1vZHVsZS5leHBvcnRzID0gZztcclxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5pbXBvcnQgeyBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcblxuZXhwb3J0IGNsYXNzIEFjdGl2ZUxpbmVNYXJrZXIge1xuXHRwcml2YXRlIF9jdXJyZW50OiBhbnk7XG5cblx0b25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uKGxpbmU6IG51bWJlcikge1xuXHRcdGNvbnN0IHsgcHJldmlvdXMgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcblx0XHR0aGlzLl91cGRhdGUocHJldmlvdXMgJiYgcHJldmlvdXMuZWxlbWVudCk7XG5cdH1cblxuXHRfdXBkYXRlKGJlZm9yZTogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHR0aGlzLl91bm1hcmtBY3RpdmVFbGVtZW50KHRoaXMuX2N1cnJlbnQpO1xuXHRcdHRoaXMuX21hcmtBY3RpdmVFbGVtZW50KGJlZm9yZSk7XG5cdFx0dGhpcy5fY3VycmVudCA9IGJlZm9yZTtcblx0fVxuXG5cdF91bm1hcmtBY3RpdmVFbGVtZW50KGVsZW1lbnQ6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkKSB7XG5cdFx0aWYgKCFlbGVtZW50KSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGVsZW1lbnQuY2xhc3NOYW1lID0gZWxlbWVudC5jbGFzc05hbWUucmVwbGFjZSgvXFxiY29kZS1hY3RpdmUtbGluZVxcYi9nLCAnJyk7XG5cdH1cblxuXHRfbWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudDogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHRpZiAoIWVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0ZWxlbWVudC5jbGFzc05hbWUgKz0gJyBjb2RlLWFjdGl2ZS1saW5lJztcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5leHBvcnQgZnVuY3Rpb24gb25jZURvY3VtZW50TG9hZGVkKGY6ICgpID0+IHZvaWQpIHtcblx0aWYgKGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJyB8fCBkb2N1bWVudC5yZWFkeVN0YXRlIGFzIHN0cmluZyA9PT0gJ3VuaW5pdGlhbGl6ZWQnKSB7XG5cdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGYpO1xuXHR9IGVsc2Uge1xuXHRcdGYoKTtcblx0fVxufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBBY3RpdmVMaW5lTWFya2VyIH0gZnJvbSAnLi9hY3RpdmVMaW5lTWFya2VyJztcbmltcG9ydCB7IG9uY2VEb2N1bWVudExvYWRlZCB9IGZyb20gJy4vZXZlbnRzJztcbmltcG9ydCB7IGNyZWF0ZVBvc3RlckZvclZzQ29kZSB9IGZyb20gJy4vbWVzc2FnaW5nJztcbmltcG9ydCB7IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0LCBzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUsIGdldExpbmVFbGVtZW50Rm9yRnJhZ21lbnQgfSBmcm9tICcuL3Njcm9sbC1zeW5jJztcbmltcG9ydCB7IGdldFNldHRpbmdzLCBnZXREYXRhIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5pbXBvcnQgdGhyb3R0bGUgPSByZXF1aXJlKCdsb2Rhc2gudGhyb3R0bGUnKTtcblxuZGVjbGFyZSB2YXIgYWNxdWlyZVZzQ29kZUFwaTogYW55O1xuXG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IEFjdGl2ZUxpbmVNYXJrZXIoKTtcbmNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3MoKTtcblxuY29uc3QgdnNjb2RlID0gYWNxdWlyZVZzQ29kZUFwaSgpO1xuXG4vLyBTZXQgVlMgQ29kZSBzdGF0ZVxubGV0IHN0YXRlID0gZ2V0RGF0YTx7IGxpbmU6IG51bWJlciwgIGZyYWdtZW50OiBzdHJpbmcgfT4oJ2RhdGEtc3RhdGUnKTtcbnZzY29kZS5zZXRTdGF0ZShzdGF0ZSk7XG5cbmNvbnN0IG1lc3NhZ2luZyA9IGNyZWF0ZVBvc3RlckZvclZzQ29kZSh2c2NvZGUpO1xuXG53aW5kb3cuY3NwQWxlcnRlci5zZXRQb3N0ZXIobWVzc2FnaW5nKTtcbndpbmRvdy5zdHlsZUxvYWRpbmdNb25pdG9yLnNldFBvc3RlcihtZXNzYWdpbmcpO1xuXG53aW5kb3cub25sb2FkID0gKCkgPT4ge1xuXHR1cGRhdGVJbWFnZVNpemVzKCk7XG59O1xuXG5vbmNlRG9jdW1lbnRMb2FkZWQoKCkgPT4ge1xuXHRpZiAoc2V0dGluZ3Muc2Nyb2xsUHJldmlld1dpdGhFZGl0b3IpIHtcblx0XHRzZXRUaW1lb3V0KCgpID0+IHtcblx0XHRcdC8vIFRyeSB0byBzY3JvbGwgdG8gZnJhZ21lbnQgaWYgYXZhaWxhYmxlXG5cdFx0XHRpZiAoc3RhdGUuZnJhZ21lbnQpIHtcblx0XHRcdFx0Y29uc3QgZWxlbWVudCA9IGdldExpbmVFbGVtZW50Rm9yRnJhZ21lbnQoc3RhdGUuZnJhZ21lbnQpO1xuXHRcdFx0XHRpZiAoZWxlbWVudCkge1xuXHRcdFx0XHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRcdFx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUoZWxlbWVudC5saW5lKTtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Y29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcblx0XHRcdFx0aWYgKCFpc05hTihpbml0aWFsTGluZSkpIHtcblx0XHRcdFx0XHRzY3JvbGxEaXNhYmxlZCA9IHRydWU7XG5cdFx0XHRcdFx0c2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0sIDApO1xuXHR9XG59KTtcblxuY29uc3Qgb25VcGRhdGVWaWV3ID0gKCgpID0+IHtcblx0Y29uc3QgZG9TY3JvbGwgPSB0aHJvdHRsZSgobGluZTogbnVtYmVyKSA9PiB7XG5cdFx0c2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuXHRcdHNjcm9sbFRvUmV2ZWFsU291cmNlTGluZShsaW5lKTtcblx0fSwgNTApO1xuXG5cdHJldHVybiAobGluZTogbnVtYmVyLCBzZXR0aW5nczogYW55KSA9PiB7XG5cdFx0aWYgKCFpc05hTihsaW5lKSkge1xuXHRcdFx0c2V0dGluZ3MubGluZSA9IGxpbmU7XG5cdFx0XHRkb1Njcm9sbChsaW5lKTtcblx0XHR9XG5cdH07XG59KSgpO1xuXG5sZXQgdXBkYXRlSW1hZ2VTaXplcyA9IHRocm90dGxlKCgpID0+IHtcblx0Y29uc3QgaW1hZ2VJbmZvOiB7IGlkOiBzdHJpbmcsIGhlaWdodDogbnVtYmVyLCB3aWR0aDogbnVtYmVyIH1bXSA9IFtdO1xuXHRsZXQgaW1hZ2VzID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2ltZycpO1xuXHRpZiAoaW1hZ2VzKSB7XG5cdFx0bGV0IGk7XG5cdFx0Zm9yIChpID0gMDsgaSA8IGltYWdlcy5sZW5ndGg7IGkrKykge1xuXHRcdFx0Y29uc3QgaW1nID0gaW1hZ2VzW2ldO1xuXG5cdFx0XHRpZiAoaW1nLmNsYXNzTGlzdC5jb250YWlucygnbG9hZGluZycpKSB7XG5cdFx0XHRcdGltZy5jbGFzc0xpc3QucmVtb3ZlKCdsb2FkaW5nJyk7XG5cdFx0XHR9XG5cblx0XHRcdGltYWdlSW5mby5wdXNoKHtcblx0XHRcdFx0aWQ6IGltZy5pZCxcblx0XHRcdFx0aGVpZ2h0OiBpbWcuaGVpZ2h0LFxuXHRcdFx0XHR3aWR0aDogaW1nLndpZHRoXG5cdFx0XHR9KTtcblx0XHR9XG5cblx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2NhY2hlSW1hZ2VTaXplcycsIGltYWdlSW5mbyk7XG5cdH1cbn0sIDUwKTtcblxud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsICgpID0+IHtcblx0c2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuXHR1cGRhdGVJbWFnZVNpemVzKCk7XG59LCB0cnVlKTtcblxud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ21lc3NhZ2UnLCBldmVudCA9PiB7XG5cdGlmIChldmVudC5kYXRhLnNvdXJjZSAhPT0gc2V0dGluZ3Muc291cmNlKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0c3dpdGNoIChldmVudC5kYXRhLnR5cGUpIHtcblx0XHRjYXNlICdvbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24nOlxuXHRcdFx0bWFya2VyLm9uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbihldmVudC5kYXRhLmxpbmUpO1xuXHRcdFx0YnJlYWs7XG5cblx0XHRjYXNlICd1cGRhdGVWaWV3Jzpcblx0XHRcdG9uVXBkYXRlVmlldyhldmVudC5kYXRhLmxpbmUsIHNldHRpbmdzKTtcblx0XHRcdGJyZWFrO1xuXHR9XG59LCBmYWxzZSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2RibGNsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIXNldHRpbmdzLmRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcikge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdC8vIElnbm9yZSBjbGlja3Mgb24gbGlua3Ncblx0Zm9yIChsZXQgbm9kZSA9IGV2ZW50LnRhcmdldCBhcyBIVE1MRWxlbWVudDsgbm9kZTsgbm9kZSA9IG5vZGUucGFyZW50Tm9kZSBhcyBIVE1MRWxlbWVudCkge1xuXHRcdGlmIChub2RlLnRhZ05hbWUgPT09ICdBJykge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0fVxuXG5cdGNvbnN0IG9mZnNldCA9IGV2ZW50LnBhZ2VZO1xuXHRjb25zdCBsaW5lID0gZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQob2Zmc2V0KTtcblx0aWYgKHR5cGVvZiBsaW5lID09PSAnbnVtYmVyJyAmJiAhaXNOYU4obGluZSkpIHtcblx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ2RpZENsaWNrJywgeyBsaW5lOiBNYXRoLmZsb29yKGxpbmUpIH0pO1xuXHR9XG59KTtcblxuZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBldmVudCA9PiB7XG5cdGlmICghZXZlbnQpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHRsZXQgbm9kZTogYW55ID0gZXZlbnQudGFyZ2V0O1xuXHR3aGlsZSAobm9kZSkge1xuXHRcdGlmIChub2RlLnRhZ05hbWUgJiYgbm9kZS50YWdOYW1lID09PSAnQScgJiYgbm9kZS5ocmVmKSB7XG5cdFx0XHRpZiAobm9kZS5nZXRBdHRyaWJ1dGUoJ2hyZWYnKS5zdGFydHNXaXRoKCcjJykpIHtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cdFx0XHRpZiAobm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ2ZpbGU6Ly8nKSB8fCBub2RlLmhyZWYuc3RhcnRzV2l0aCgndnNjb2RlLXJlc291cmNlOicpIHx8IG5vZGUuaHJlZi5zdGFydHNXaXRoKHNldHRpbmdzLndlYnZpZXdSZXNvdXJjZVJvb3QpKSB7XG5cdFx0XHRcdGNvbnN0IFtwYXRoLCBmcmFnbWVudF0gPSBub2RlLmhyZWYucmVwbGFjZSgvXihmaWxlOlxcL1xcL3x2c2NvZGUtcmVzb3VyY2U6KS9pLCAnJykucmVwbGFjZShuZXcgUmVnRXhwKGBeJHtlc2NhcGVSZWdFeHAoc2V0dGluZ3Mud2Vidmlld1Jlc291cmNlUm9vdCl9YCkpLnNwbGl0KCcjJyk7XG5cdFx0XHRcdG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnY2xpY2tMaW5rJywgeyBwYXRoLCBmcmFnbWVudCB9KTtcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0ZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0YnJlYWs7XG5cdFx0fVxuXHRcdG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG5cdH1cbn0sIHRydWUpO1xuXG5pZiAoc2V0dGluZ3Muc2Nyb2xsRWRpdG9yV2l0aFByZXZpZXcpIHtcblx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRocm90dGxlKCgpID0+IHtcblx0XHRpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcblx0XHRcdHNjcm9sbERpc2FibGVkID0gZmFsc2U7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbnN0IGxpbmUgPSBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCh3aW5kb3cuc2Nyb2xsWSk7XG5cdFx0XHRpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuXHRcdFx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3JldmVhbExpbmUnLCB7IGxpbmUgfSk7XG5cdFx0XHRcdHN0YXRlLmxpbmUgPSBsaW5lO1xuXHRcdFx0XHR2c2NvZGUuc2V0U3RhdGUoc3RhdGUpO1xuXHRcdFx0fVxuXHRcdH1cblx0fSwgNTApKTtcbn1cblxuZnVuY3Rpb24gZXNjYXBlUmVnRXhwKHRleHQ6IHN0cmluZykge1xuXHRyZXR1cm4gdGV4dC5yZXBsYWNlKC9bLVtcXF17fSgpKis/LixcXFxcXiR8I1xcc10vZywgJ1xcXFwkJicpO1xufVxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmltcG9ydCB7IGdldFNldHRpbmdzIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgTWVzc2FnZVBvc3RlciB7XG5cdC8qKlxuXHQgKiBQb3N0IGEgbWVzc2FnZSB0byB0aGUgbWFya2Rvd24gZXh0ZW5zaW9uXG5cdCAqL1xuXHRwb3N0TWVzc2FnZSh0eXBlOiBzdHJpbmcsIGJvZHk6IG9iamVjdCk6IHZvaWQ7XG59XG5cbmV4cG9ydCBjb25zdCBjcmVhdGVQb3N0ZXJGb3JWc0NvZGUgPSAodnNjb2RlOiBhbnkpID0+IHtcblx0cmV0dXJuIG5ldyBjbGFzcyBpbXBsZW1lbnRzIE1lc3NhZ2VQb3N0ZXIge1xuXHRcdHBvc3RNZXNzYWdlKHR5cGU6IHN0cmluZywgYm9keTogb2JqZWN0KTogdm9pZCB7XG5cdFx0XHR2c2NvZGUucG9zdE1lc3NhZ2Uoe1xuXHRcdFx0XHR0eXBlLFxuXHRcdFx0XHRzb3VyY2U6IGdldFNldHRpbmdzKCkuc291cmNlLFxuXHRcdFx0XHRib2R5XG5cdFx0XHR9KTtcblx0XHR9XG5cdH07XG59O1xuXG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgZ2V0U2V0dGluZ3MgfSBmcm9tICcuL3NldHRpbmdzJztcblxuXG5mdW5jdGlvbiBjbGFtcChtaW46IG51bWJlciwgbWF4OiBudW1iZXIsIHZhbHVlOiBudW1iZXIpIHtcblx0cmV0dXJuIE1hdGgubWluKG1heCwgTWF0aC5tYXgobWluLCB2YWx1ZSkpO1xufVxuXG5mdW5jdGlvbiBjbGFtcExpbmUobGluZTogbnVtYmVyKSB7XG5cdHJldHVybiBjbGFtcCgwLCBnZXRTZXR0aW5ncygpLmxpbmVDb3VudCAtIDEsIGxpbmUpO1xufVxuXG5cbmV4cG9ydCBpbnRlcmZhY2UgQ29kZUxpbmVFbGVtZW50IHtcblx0ZWxlbWVudDogSFRNTEVsZW1lbnQ7XG5cdGxpbmU6IG51bWJlcjtcbn1cblxuY29uc3QgZ2V0Q29kZUxpbmVFbGVtZW50cyA9ICgoKSA9PiB7XG5cdGxldCBlbGVtZW50czogQ29kZUxpbmVFbGVtZW50W107XG5cdHJldHVybiAoKSA9PiB7XG5cdFx0aWYgKCFlbGVtZW50cykge1xuXHRcdFx0ZWxlbWVudHMgPSBbeyBlbGVtZW50OiBkb2N1bWVudC5ib2R5LCBsaW5lOiAwIH1dO1xuXHRcdFx0Zm9yIChjb25zdCBlbGVtZW50IG9mIGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2NvZGUtbGluZScpKSB7XG5cdFx0XHRcdGNvbnN0IGxpbmUgPSArZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtbGluZScpITtcblx0XHRcdFx0aWYgKCFpc05hTihsaW5lKSkge1xuXHRcdFx0XHRcdGVsZW1lbnRzLnB1c2goeyBlbGVtZW50OiBlbGVtZW50IGFzIEhUTUxFbGVtZW50LCBsaW5lIH0pO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiBlbGVtZW50cztcblx0fTtcbn0pKCk7XG5cbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IG1hcCB0byBhIHNwZWNpZmljIHRhcmdldCBsaW5lIGluIHRoZSBlZGl0b3IuXG4gKlxuICogSWYgYW4gZXhhY3QgbWF0Y2gsIHJldHVybnMgYSBzaW5nbGUgZWxlbWVudC4gSWYgdGhlIGxpbmUgaXMgYmV0d2VlbiBlbGVtZW50cyxcbiAqIHJldHVybnMgdGhlIGVsZW1lbnQgcHJpb3IgdG8gYW5kIHRoZSBlbGVtZW50IGFmdGVyIHRoZSBnaXZlbiBsaW5lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKHRhcmdldExpbmU6IG51bWJlcik6IHsgcHJldmlvdXM6IENvZGVMaW5lRWxlbWVudDsgbmV4dD86IENvZGVMaW5lRWxlbWVudDsgfSB7XG5cdGNvbnN0IGxpbmVOdW1iZXIgPSBNYXRoLmZsb29yKHRhcmdldExpbmUpO1xuXHRjb25zdCBsaW5lcyA9IGdldENvZGVMaW5lRWxlbWVudHMoKTtcblx0bGV0IHByZXZpb3VzID0gbGluZXNbMF0gfHwgbnVsbDtcblx0Zm9yIChjb25zdCBlbnRyeSBvZiBsaW5lcykge1xuXHRcdGlmIChlbnRyeS5saW5lID09PSBsaW5lTnVtYmVyKSB7XG5cdFx0XHRyZXR1cm4geyBwcmV2aW91czogZW50cnksIG5leHQ6IHVuZGVmaW5lZCB9O1xuXHRcdH0gZWxzZSBpZiAoZW50cnkubGluZSA+IGxpbmVOdW1iZXIpIHtcblx0XHRcdHJldHVybiB7IHByZXZpb3VzLCBuZXh0OiBlbnRyeSB9O1xuXHRcdH1cblx0XHRwcmV2aW91cyA9IGVudHJ5O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzIH07XG59XG5cbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IGFyZSBhdCBhIHNwZWNpZmljIHBpeGVsIG9mZnNldCBvbiB0aGUgcGFnZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQ6IG51bWJlcik6IHsgcHJldmlvdXM6IENvZGVMaW5lRWxlbWVudDsgbmV4dD86IENvZGVMaW5lRWxlbWVudDsgfSB7XG5cdGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuXHRjb25zdCBwb3NpdGlvbiA9IG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZO1xuXHRsZXQgbG8gPSAtMTtcblx0bGV0IGhpID0gbGluZXMubGVuZ3RoIC0gMTtcblx0d2hpbGUgKGxvICsgMSA8IGhpKSB7XG5cdFx0Y29uc3QgbWlkID0gTWF0aC5mbG9vcigobG8gKyBoaSkgLyAyKTtcblx0XHRjb25zdCBib3VuZHMgPSBsaW5lc1ttaWRdLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdFx0aWYgKGJvdW5kcy50b3AgKyBib3VuZHMuaGVpZ2h0ID49IHBvc2l0aW9uKSB7XG5cdFx0XHRoaSA9IG1pZDtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRsbyA9IG1pZDtcblx0XHR9XG5cdH1cblx0Y29uc3QgaGlFbGVtZW50ID0gbGluZXNbaGldO1xuXHRjb25zdCBoaUJvdW5kcyA9IGhpRWxlbWVudC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRpZiAoaGkgPj0gMSAmJiBoaUJvdW5kcy50b3AgPiBwb3NpdGlvbikge1xuXHRcdGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcblx0XHRyZXR1cm4geyBwcmV2aW91czogbG9FbGVtZW50LCBuZXh0OiBoaUVsZW1lbnQgfTtcblx0fVxuXHRyZXR1cm4geyBwcmV2aW91czogaGlFbGVtZW50IH07XG59XG5cbi8qKlxuICogQXR0ZW1wdCB0byByZXZlYWwgdGhlIGVsZW1lbnQgZm9yIGEgc291cmNlIGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNjcm9sbFRvUmV2ZWFsU291cmNlTGluZShsaW5lOiBudW1iZXIpIHtcblx0aWYgKCFnZXRTZXR0aW5ncygpLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0aWYgKGxpbmUgPD0gMCkge1xuXHRcdHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIDApO1xuXHRcdHJldHVybjtcblx0fVxuXG5cdGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcblx0aWYgKCFwcmV2aW91cykge1xuXHRcdHJldHVybjtcblx0fVxuXHRsZXQgc2Nyb2xsVG8gPSAwO1xuXHRjb25zdCByZWN0ID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0Y29uc3QgcHJldmlvdXNUb3AgPSByZWN0LnRvcDtcblx0aWYgKG5leHQgJiYgbmV4dC5saW5lICE9PSBwcmV2aW91cy5saW5lKSB7XG5cdFx0Ly8gQmV0d2VlbiB0d28gZWxlbWVudHMuIEdvIHRvIHBlcmNlbnRhZ2Ugb2Zmc2V0IGJldHdlZW4gdGhlbS5cblx0XHRjb25zdCBiZXR3ZWVuUHJvZ3Jlc3MgPSAobGluZSAtIHByZXZpb3VzLmxpbmUpIC8gKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuXHRcdGNvbnN0IGVsZW1lbnRPZmZzZXQgPSBuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNUb3A7XG5cdFx0c2Nyb2xsVG8gPSBwcmV2aW91c1RvcCArIGJldHdlZW5Qcm9ncmVzcyAqIGVsZW1lbnRPZmZzZXQ7XG5cdH0gZWxzZSB7XG5cdFx0Y29uc3QgcHJvZ3Jlc3NJbkVsZW1lbnQgPSBsaW5lIC0gTWF0aC5mbG9vcihsaW5lKTtcblx0XHRzY3JvbGxUbyA9IHByZXZpb3VzVG9wICsgKHJlY3QuaGVpZ2h0ICogcHJvZ3Jlc3NJbkVsZW1lbnQpO1xuXHR9XG5cdHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIE1hdGgubWF4KDEsIHdpbmRvdy5zY3JvbGxZICsgc2Nyb2xsVG8pKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldDogbnVtYmVyKSB7XG5cdGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQpO1xuXHRpZiAocHJldmlvdXMpIHtcblx0XHRjb25zdCBwcmV2aW91c0JvdW5kcyA9IHByZXZpb3VzLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdFx0Y29uc3Qgb2Zmc2V0RnJvbVByZXZpb3VzID0gKG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcblx0XHRpZiAobmV4dCkge1xuXHRcdFx0Y29uc3QgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgPSBvZmZzZXRGcm9tUHJldmlvdXMgLyAobmV4dC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG5cdFx0XHRjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzICogKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuXHRcdFx0cmV0dXJuIGNsYW1wTGluZShsaW5lKTtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRjb25zdCBwcm9ncmVzc1dpdGhpbkVsZW1lbnQgPSBvZmZzZXRGcm9tUHJldmlvdXMgLyAocHJldmlvdXNCb3VuZHMuaGVpZ2h0KTtcblx0XHRcdGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NXaXRoaW5FbGVtZW50O1xuXHRcdFx0cmV0dXJuIGNsYW1wTGluZShsaW5lKTtcblx0XHR9XG5cdH1cblx0cmV0dXJuIG51bGw7XG59XG5cbi8qKlxuICogVHJ5IHRvIGZpbmQgdGhlIGh0bWwgZWxlbWVudCBieSB1c2luZyBhIGZyYWdtZW50IGlkXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRMaW5lRWxlbWVudEZvckZyYWdtZW50KGZyYWdtZW50OiBzdHJpbmcpOiBDb2RlTGluZUVsZW1lbnQgfCB1bmRlZmluZWQge1xuXHRyZXR1cm4gZ2V0Q29kZUxpbmVFbGVtZW50cygpLmZpbmQoKGVsZW1lbnQpID0+IHtcblx0XHRyZXR1cm4gZWxlbWVudC5lbGVtZW50LmlkID09PSBmcmFnbWVudDtcblx0fSk7XG59XG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuZXhwb3J0IGludGVyZmFjZSBQcmV2aWV3U2V0dGluZ3Mge1xuXHRyZWFkb25seSBzb3VyY2U6IHN0cmluZztcblx0cmVhZG9ubHkgbGluZTogbnVtYmVyO1xuXHRyZWFkb25seSBsaW5lQ291bnQ6IG51bWJlcjtcblx0cmVhZG9ubHkgc2Nyb2xsUHJldmlld1dpdGhFZGl0b3I/OiBib29sZWFuO1xuXHRyZWFkb25seSBzY3JvbGxFZGl0b3JXaXRoUHJldmlldzogYm9vbGVhbjtcblx0cmVhZG9ubHkgZGlzYWJsZVNlY3VyaXR5V2FybmluZ3M6IGJvb2xlYW47XG5cdHJlYWRvbmx5IGRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcjogYm9vbGVhbjtcblx0cmVhZG9ubHkgd2Vidmlld1Jlc291cmNlUm9vdDogc3RyaW5nO1xufVxuXG5sZXQgY2FjaGVkU2V0dGluZ3M6IFByZXZpZXdTZXR0aW5ncyB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZDtcblxuZXhwb3J0IGZ1bmN0aW9uIGdldERhdGE8VCA9IHt9PihrZXk6IHN0cmluZyk6IFQge1xuXHRjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3ZzY29kZS1tYXJrZG93bi1wcmV2aWV3LWRhdGEnKTtcblx0aWYgKGVsZW1lbnQpIHtcblx0XHRjb25zdCBkYXRhID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoa2V5KTtcblx0XHRpZiAoZGF0YSkge1xuXHRcdFx0cmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG5cdFx0fVxuXHR9XG5cblx0dGhyb3cgbmV3IEVycm9yKGBDb3VsZCBub3QgbG9hZCBkYXRhIGZvciAke2tleX1gKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFNldHRpbmdzKCk6IFByZXZpZXdTZXR0aW5ncyB7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuXHRpZiAoY2FjaGVkU2V0dGluZ3MpIHtcblx0XHRyZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoJ0NvdWxkIG5vdCBsb2FkIHNldHRpbmdzJyk7XG59XG4iXSwic291cmNlUm9vdCI6IiJ9 \ No newline at end of file diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index f78ae2f79724c..b12614fe9ec58 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -6,7 +6,7 @@ import { ActiveLineMarker } from './activeLineMarker'; import { onceDocumentLoaded } from './events'; import { createPosterForVsCode } from './messaging'; -import { getEditorLineNumberForPageOffset, scrollToRevealSourceLine } from './scroll-sync'; +import { getEditorLineNumberForPageOffset, scrollToRevealSourceLine, getLineElementForFragment } from './scroll-sync'; import { getSettings, getData } from './settings'; import throttle = require('lodash.throttle'); @@ -19,7 +19,7 @@ const settings = getSettings(); const vscode = acquireVsCodeApi(); // Set VS Code state -let state = getData<{ line: number }>('data-state'); +let state = getData<{ line: number, fragment: string }>('data-state'); vscode.setState(state); const messaging = createPosterForVsCode(vscode); @@ -34,10 +34,19 @@ window.onload = () => { onceDocumentLoaded(() => { if (settings.scrollPreviewWithEditor) { setTimeout(() => { - const initialLine = +settings.line; - if (!isNaN(initialLine)) { - scrollDisabled = true; - scrollToRevealSourceLine(initialLine); + // Try to scroll to fragment if available + if (state.fragment) { + const element = getLineElementForFragment(state.fragment); + if (element) { + scrollDisabled = true; + scrollToRevealSourceLine(element.line); + } + } else { + const initialLine = +settings.line; + if (!isNaN(initialLine)) { + scrollDisabled = true; + scrollToRevealSourceLine(initialLine); + } } }, 0); } @@ -161,4 +170,4 @@ if (settings.scrollEditorWithPreview) { function escapeRegExp(text: string) { return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); -} \ No newline at end of file +} diff --git a/extensions/markdown-language-features/preview-src/scroll-sync.ts b/extensions/markdown-language-features/preview-src/scroll-sync.ts index 4fe8987f6cddc..eee320ac8e7f6 100644 --- a/extensions/markdown-language-features/preview-src/scroll-sync.ts +++ b/extensions/markdown-language-features/preview-src/scroll-sync.ts @@ -134,3 +134,12 @@ export function getEditorLineNumberForPageOffset(offset: number) { } return null; } + +/** + * Try to find the html element by using a fragment id + */ +export function getLineElementForFragment(fragment: string): CodeLineElement | undefined { + return getCodeLineElements().find((element) => { + return element.element.id === fragment; + }); +} diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index c874f5791edcf..694a4fbb13c7f 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -89,6 +89,7 @@ export class MarkdownPreview extends Disposable { private isScrolling = false; private _disposed: boolean = false; private imageInfo: { id: string, width: number, height: number }[] = []; + private scrollToFragment: string | undefined; public static async revive( webview: vscode.WebviewPanel, @@ -264,7 +265,8 @@ export class MarkdownPreview extends Disposable { locked: this._locked, line: this.line, resourceColumn: this.resourceColumn, - imageInfo: this.imageInfo + imageInfo: this.imageInfo, + fragment: this.scrollToFragment }; } @@ -284,8 +286,12 @@ export class MarkdownPreview extends Disposable { public update(resource: vscode.Uri) { const editor = vscode.window.activeTextEditor; + // Reposition scroll preview, position scroll to the top if active text editor + // doesn't corresponds with preview if (editor && editor.document.uri.fsPath === resource.fsPath) { this.line = getVisibleLine(editor); + } else { + this.line = 0; } // If we have changed resources, cancel any pending updates @@ -520,11 +526,15 @@ export class MarkdownPreview extends Disposable { } private async onDidClickPreviewLink(path: string, fragment: string | undefined) { + this.scrollToFragment = undefined; const config = vscode.workspace.getConfiguration('markdown', this.resource); const openLinks = config.get('preview.openMarkdownLinks', 'inPreview'); if (openLinks === 'inPreview') { const markdownLink = await resolveLinkToMarkdownFile(path); if (markdownLink) { + if (fragment) { + this.scrollToFragment = fragment; + } this.update(markdownLink); return; } From 9fed4cfd3e02eef6510414d793e14226fe2a801f Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 19 Aug 2019 17:10:39 +0200 Subject: [PATCH 352/613] #79429 --- src/vs/workbench/contrib/files/browser/views/emptyView.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/views/emptyView.ts b/src/vs/workbench/contrib/files/browser/views/emptyView.ts index 8f097112f0072..e7e9ecf5f6155 100644 --- a/src/vs/workbench/contrib/files/browser/views/emptyView.ts +++ b/src/vs/workbench/contrib/files/browser/views/emptyView.ts @@ -59,7 +59,6 @@ export class EmptyView extends ViewletPanel { container.appendChild(titleContainer); this.titleElement = document.createElement('span'); - this.titleElement.textContent = name; titleContainer.appendChild(this.titleElement); } From 90a35ecc5d4c6b79b3083bcdb0c6430b8674cc1c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Aug 2019 17:13:18 +0200 Subject: [PATCH 353/613] TSLint: show a warning when accessing node.js globals in common|browser (#79222) * trivial first cut * document where globals are from * improve rule detection * fix "gulp tslint" task * share rules * enable more rules * also add a rule for DOM --- build/gulpfile.hygiene.js | 51 +++++++++++++++++++------ build/lib/tslint/abstractGlobalsRule.js | 40 +++++++++++++++++++ build/lib/tslint/abstractGlobalsRule.ts | 51 +++++++++++++++++++++++++ build/lib/tslint/noDOMGlobalsRule.js | 34 +++++++++++++++++ build/lib/tslint/noDOMGlobalsRule.ts | 45 ++++++++++++++++++++++ build/lib/tslint/noNodeJSGlobalsRule.js | 40 +++++++++++++++++++ build/lib/tslint/noNodeJSGlobalsRule.ts | 51 +++++++++++++++++++++++++ tslint.json | 44 +++++++++++++++++++++ 8 files changed, 344 insertions(+), 12 deletions(-) create mode 100644 build/lib/tslint/abstractGlobalsRule.js create mode 100644 build/lib/tslint/abstractGlobalsRule.ts create mode 100644 build/lib/tslint/noDOMGlobalsRule.js create mode 100644 build/lib/tslint/noDOMGlobalsRule.ts create mode 100644 build/lib/tslint/noNodeJSGlobalsRule.js create mode 100644 build/lib/tslint/noNodeJSGlobalsRule.ts diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index f6594804322e0..a05fff4a1bd6c 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -135,20 +135,39 @@ const eslintFilter = [ '!**/test/**' ]; -const tslintFilter = [ - 'src/**/*.ts', - 'test/**/*.ts', - 'extensions/**/*.ts', +const tslintBaseFilter = [ '!**/fixtures/**', '!**/typings/**', '!**/node_modules/**', - '!extensions/typescript/test/colorize-fixtures/**', + '!extensions/typescript-basics/test/colorize-fixtures/**', '!extensions/vscode-api-tests/testWorkspace/**', '!extensions/vscode-api-tests/testWorkspace2/**', '!extensions/**/*.test.ts', '!extensions/html-language-features/server/lib/jquery.d.ts' ]; +const tslintCoreFilter = [ + 'src/**/*.ts', + 'test/**/*.ts', + '!extensions/**/*.ts', + '!test/smoke/**', + ...tslintBaseFilter +]; + +const tslintExtensionsFilter = [ + 'extensions/**/*.ts', + '!src/**/*.ts', + '!test/**/*.ts', + ...tslintBaseFilter +]; + +const tslintHygieneFilter = [ + 'src/**/*.ts', + 'test/**/*.ts', + 'extensions/**/*.ts', + ...tslintBaseFilter +]; + const copyrightHeaderLines = [ '/*---------------------------------------------------------------------------------------------', ' * Copyright (c) Microsoft Corporation. All rights reserved.', @@ -165,12 +184,20 @@ gulp.task('eslint', () => { }); gulp.task('tslint', () => { - const options = { emitError: true }; - - return vfs.src(all, { base: '.', follow: true, allowEmpty: true }) - .pipe(filter(tslintFilter)) - .pipe(gulptslint.default({ rulesDirectory: 'build/lib/tslint' })) - .pipe(gulptslint.default.report(options)); + return es.merge([ + + // Core: include type information (required by certain rules like no-nodejs-globals) + vfs.src(all, { base: '.', follow: true, allowEmpty: true }) + .pipe(filter(tslintCoreFilter)) + .pipe(gulptslint.default({ rulesDirectory: 'build/lib/tslint', program: tslint.Linter.createProgram('src/tsconfig.json') })) + .pipe(gulptslint.default.report({ emitError: true })), + + // Exenstions: do not include type information + vfs.src(all, { base: '.', follow: true, allowEmpty: true }) + .pipe(filter(tslintExtensionsFilter)) + .pipe(gulptslint.default({ rulesDirectory: 'build/lib/tslint' })) + .pipe(gulptslint.default.report({ emitError: true })) + ]); }); function hygiene(some) { @@ -283,7 +310,7 @@ function hygiene(some) { .pipe(copyrights); const typescript = result - .pipe(filter(tslintFilter)) + .pipe(filter(tslintHygieneFilter)) .pipe(formatting) .pipe(tsl); diff --git a/build/lib/tslint/abstractGlobalsRule.js b/build/lib/tslint/abstractGlobalsRule.js new file mode 100644 index 0000000000000..2aadf2c3bd965 --- /dev/null +++ b/build/lib/tslint/abstractGlobalsRule.js @@ -0,0 +1,40 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const Lint = require("tslint"); +class AbstractGlobalsRuleWalker extends Lint.RuleWalker { + constructor(file, program, opts, _config) { + super(file, opts); + this.program = program; + this._config = _config; + } + visitIdentifier(node) { + if (this.getDisallowedGlobals().some(disallowedGlobal => disallowedGlobal === node.text)) { + if (this._config.allowed && this._config.allowed.some(allowed => allowed === node.text)) { + return; // override + } + const checker = this.program.getTypeChecker(); + const symbol = checker.getSymbolAtLocation(node); + if (symbol) { + const valueDeclaration = symbol.valueDeclaration; + if (valueDeclaration) { + const parent = valueDeclaration.parent; + if (parent) { + const sourceFile = parent.getSourceFile(); + if (sourceFile) { + const fileName = sourceFile.fileName; + if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) { + this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`); + } + } + } + } + } + } + super.visitIdentifier(node); + } +} +exports.AbstractGlobalsRuleWalker = AbstractGlobalsRuleWalker; diff --git a/build/lib/tslint/abstractGlobalsRule.ts b/build/lib/tslint/abstractGlobalsRule.ts new file mode 100644 index 0000000000000..8c80b3e0cfd76 --- /dev/null +++ b/build/lib/tslint/abstractGlobalsRule.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; + +interface AbstractGlobalsRuleConfig { + target: string; + allowed: string[]; +} + +export abstract class AbstractGlobalsRuleWalker extends Lint.RuleWalker { + + constructor(file: ts.SourceFile, private program: ts.Program, opts: Lint.IOptions, private _config: AbstractGlobalsRuleConfig) { + super(file, opts); + } + + protected abstract getDisallowedGlobals(): string[]; + + protected abstract getDefinitionPattern(): string; + + visitIdentifier(node: ts.Identifier) { + if (this.getDisallowedGlobals().some(disallowedGlobal => disallowedGlobal === node.text)) { + if (this._config.allowed && this._config.allowed.some(allowed => allowed === node.text)) { + return; // override + } + + const checker = this.program.getTypeChecker(); + const symbol = checker.getSymbolAtLocation(node); + if (symbol) { + const valueDeclaration = symbol.valueDeclaration; + if (valueDeclaration) { + const parent = valueDeclaration.parent; + if (parent) { + const sourceFile = parent.getSourceFile(); + if (sourceFile) { + const fileName = sourceFile.fileName; + if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) { + this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`); + } + } + } + } + } + } + + super.visitIdentifier(node); + } +} diff --git a/build/lib/tslint/noDOMGlobalsRule.js b/build/lib/tslint/noDOMGlobalsRule.js new file mode 100644 index 0000000000000..a83ac8f7f5958 --- /dev/null +++ b/build/lib/tslint/noDOMGlobalsRule.js @@ -0,0 +1,34 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const Lint = require("tslint"); +const minimatch = require("minimatch"); +const abstractGlobalsRule_1 = require("./abstractGlobalsRule"); +class Rule extends Lint.Rules.TypedRule { + applyWithProgram(sourceFile, program) { + const configs = this.getOptions().ruleArguments; + for (const config of configs) { + if (minimatch(sourceFile.fileName, config.target)) { + return this.applyWithWalker(new NoDOMGlobalsRuleWalker(sourceFile, program, this.getOptions(), config)); + } + } + return []; + } +} +exports.Rule = Rule; +class NoDOMGlobalsRuleWalker extends abstractGlobalsRule_1.AbstractGlobalsRuleWalker { + getDefinitionPattern() { + return 'lib.dom.d.ts'; + } + getDisallowedGlobals() { + // intentionally not complete + return [ + "window", + "document", + "HTMLElement" + ]; + } +} diff --git a/build/lib/tslint/noDOMGlobalsRule.ts b/build/lib/tslint/noDOMGlobalsRule.ts new file mode 100644 index 0000000000000..df9e67bf78b6f --- /dev/null +++ b/build/lib/tslint/noDOMGlobalsRule.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; +import * as minimatch from 'minimatch'; +import { AbstractGlobalsRuleWalker } from './abstractGlobalsRule'; + +interface NoDOMGlobalsRuleConfig { + target: string; + allowed: string[]; +} + +export class Rule extends Lint.Rules.TypedRule { + + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + const configs = this.getOptions().ruleArguments; + + for (const config of configs) { + if (minimatch(sourceFile.fileName, config.target)) { + return this.applyWithWalker(new NoDOMGlobalsRuleWalker(sourceFile, program, this.getOptions(), config)); + } + } + + return []; + } +} + +class NoDOMGlobalsRuleWalker extends AbstractGlobalsRuleWalker { + + getDefinitionPattern(): string { + return 'lib.dom.d.ts'; + } + + getDisallowedGlobals(): string[] { + // intentionally not complete + return [ + "window", + "document", + "HTMLElement" + ]; + } +} diff --git a/build/lib/tslint/noNodeJSGlobalsRule.js b/build/lib/tslint/noNodeJSGlobalsRule.js new file mode 100644 index 0000000000000..1e955a41e1b47 --- /dev/null +++ b/build/lib/tslint/noNodeJSGlobalsRule.js @@ -0,0 +1,40 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const Lint = require("tslint"); +const minimatch = require("minimatch"); +const abstractGlobalsRule_1 = require("./abstractGlobalsRule"); +class Rule extends Lint.Rules.TypedRule { + applyWithProgram(sourceFile, program) { + const configs = this.getOptions().ruleArguments; + for (const config of configs) { + if (minimatch(sourceFile.fileName, config.target)) { + return this.applyWithWalker(new NoNodejsGlobalsRuleWalker(sourceFile, program, this.getOptions(), config)); + } + } + return []; + } +} +exports.Rule = Rule; +class NoNodejsGlobalsRuleWalker extends abstractGlobalsRule_1.AbstractGlobalsRuleWalker { + getDefinitionPattern() { + return '@types/node'; + } + getDisallowedGlobals() { + // https://nodejs.org/api/globals.html#globals_global_objects + return [ + "Buffer", + "__dirname", + "__filename", + "clearImmediate", + "exports", + "global", + "module", + "process", + "setImmediate" + ]; + } +} diff --git a/build/lib/tslint/noNodeJSGlobalsRule.ts b/build/lib/tslint/noNodeJSGlobalsRule.ts new file mode 100644 index 0000000000000..48b229333e9e8 --- /dev/null +++ b/build/lib/tslint/noNodeJSGlobalsRule.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; +import * as minimatch from 'minimatch'; +import { AbstractGlobalsRuleWalker } from './abstractGlobalsRule'; + +interface NoNodejsGlobalsConfig { + target: string; + allowed: string[]; +} + +export class Rule extends Lint.Rules.TypedRule { + + applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { + const configs = this.getOptions().ruleArguments; + + for (const config of configs) { + if (minimatch(sourceFile.fileName, config.target)) { + return this.applyWithWalker(new NoNodejsGlobalsRuleWalker(sourceFile, program, this.getOptions(), config)); + } + } + + return []; + } +} + +class NoNodejsGlobalsRuleWalker extends AbstractGlobalsRuleWalker { + + getDefinitionPattern(): string { + return '@types/node'; + } + + getDisallowedGlobals(): string[] { + // https://nodejs.org/api/globals.html#globals_global_objects + return [ + "Buffer", + "__dirname", + "__filename", + "clearImmediate", + "exports", + "global", + "module", + "process", + "setImmediate" + ]; + } +} diff --git a/tslint.json b/tslint.json index 059d2cea868d2..4cffe3e1c536f 100644 --- a/tslint.json +++ b/tslint.json @@ -9,8 +9,10 @@ "no-duplicate-super": true, "no-duplicate-switch-case": true, "no-duplicate-variable": true, + "no-for-in-array": true, "no-eval": true, "no-redundant-jsdoc": true, + "no-restricted-globals": true, "no-sparse-arrays": true, "no-string-throw": true, "no-unsafe-finally": true, @@ -625,6 +627,48 @@ "restrictions": "**/vs/**" } ], + "no-nodejs-globals": [ + true, + { + "target": "**/vs/base/common/{path,process,platform}.ts", + "allowed": [ + "process" // -> defines safe access to process + ] + }, + { + "target": "**/vs/**/test/{common,browser}/**", + "allowed": [ + "process", + "Buffer", + "__filename", + "__dirname" + ] + }, + { + "target": "**/vs/workbench/api/common/extHostExtensionService.ts", + "allowed": [ + "global" // -> safe access to 'global' + ] + }, + { + "target": "**/vs/**/{common,browser}/**", + "allowed": [ /* none */] + } + ], + "no-dom-globals": [ + true, + { + "target": "**/vs/**/test/{common,node,electron-main}/**", + "allowed": [ + "document", + "HTMLElement" + ] + }, + { + "target": "**/vs/**/{common,node,electron-main}/**", + "allowed": [ /* none */] + } + ], "duplicate-imports": true, "no-new-buffer": true, "translation-remind": true, From af13ba22fbcafb02aa326c67e60ed09470e55179 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 19 Aug 2019 08:32:59 -0700 Subject: [PATCH 354/613] Update trustedDomain default config --- src/vs/platform/request/common/request.ts | 8 -------- src/vs/workbench/contrib/url/common/url.contribution.ts | 8 ++++++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/platform/request/common/request.ts b/src/vs/platform/request/common/request.ts index 27601de0ef05d..31e3c314242c1 100644 --- a/src/vs/platform/request/common/request.ts +++ b/src/vs/platform/request/common/request.ts @@ -117,14 +117,6 @@ Registry.as(Extensions.Configuration) type: 'boolean', default: true, description: localize('systemCertificates', "Controls whether CA certificates should be loaded from the OS. (On Windows and macOS a reload of the window is required after turning this off.)") - }, - 'http.trustedDomains': { - type: 'array', - default: ['https://code.visualstudio.com'], - description: localize('trustedDomains', "Controls whether a http/https link can be opened directly in browser.\n\nAdd `*` to the list to whitelist all domains."), - items: { - type: 'string' - } } } }); diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index b8f67e2e5989a..5fe78f41820e6 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -47,9 +47,13 @@ const configureTrustedDomainsHandler = ( storageService: IStorageService, domainToConfigure?: string ) => { - let trustedDomains: string[] = []; + let trustedDomains: string[] = ['https://code.visualstudio.com']; + try { - trustedDomains = JSON.parse(storageService.get('http.trustedDomains', StorageScope.GLOBAL, '[]')); + const trustedDomainsSrc = storageService.get('http.trustedDomains', StorageScope.GLOBAL); + if (trustedDomainsSrc) { + trustedDomains = JSON.parse(trustedDomainsSrc); + } } catch (err) { } const domainQuickPickItems: IQuickPickItem[] = trustedDomains From dc257921750737813d00123ca83c7c2cda409185 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Aug 2019 17:41:36 +0200 Subject: [PATCH 355/613] tslint - disable some rules with comments (#79454) --- src/vs/base/parts/quickopen/common/quickOpen.ts | 4 +++- src/vs/platform/windows/common/windows.ts | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/base/parts/quickopen/common/quickOpen.ts b/src/vs/base/parts/quickopen/common/quickOpen.ts index dd39a215ef17b..d6039c688028b 100644 --- a/src/vs/base/parts/quickopen/common/quickOpen.ts +++ b/src/vs/base/parts/quickopen/common/quickOpen.ts @@ -68,6 +68,8 @@ export interface IDataSource { export interface IRenderer { getHeight(entry: T): number; getTemplateId(entry: T): string; + // rationale: will be replaced by quickinput later + // tslint:disable-next-line: no-dom-globals renderTemplate(templateId: string, container: HTMLElement, styles: any): any; renderElement(entry: T, templateId: string, templateData: any, styles: any): void; disposeTemplate(templateId: string, templateData: any): void; @@ -92,4 +94,4 @@ export interface IModel { runner: IRunner; filter?: IFilter; accessibilityProvider?: IAccessiblityProvider; -} \ No newline at end of file +} diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 7ac046630817f..bca1181e59197 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -240,6 +240,8 @@ export interface IWindowService { closeWorkspace(): Promise; updateTouchBar(items: ISerializableCommandAction[][]): Promise; enterWorkspace(path: URI): Promise; + // rationale: will eventually move to electron-browser + // tslint:disable-next-line: no-dom-globals toggleFullScreen(target?: HTMLElement): Promise; setRepresentedFilename(fileName: string): Promise; getRecentlyOpened(): Promise; From c0039fa3bda0417228b5a37442470d694301e5b1 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 19 Aug 2019 08:57:19 -0700 Subject: [PATCH 356/613] Drop unneeded action registration --- .../editor/browser/services/openerService.ts | 9 ++-- .../contrib/url/common/url.contribution.ts | 52 +++++++------------ 2 files changed, 24 insertions(+), 37 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index c6aa9891cf5fd..60be6dadd5934 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -67,9 +67,12 @@ export class OpenerService implements IOpenerService { } if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) { - let trustedDomains: string[] = []; + let trustedDomains: string[] = ['https://code.visualstudio.com']; try { - trustedDomains = JSON.parse(this._storageService.get('http.trustedDomains', StorageScope.GLOBAL, '[]')); + const trustedDomainsSrc = this._storageService.get('http.trustedDomains', StorageScope.GLOBAL); + if (trustedDomainsSrc) { + trustedDomains = JSON.parse(trustedDomainsSrc); + } } catch (err) { } const domainToOpen = `${scheme}://${authority}`; @@ -96,7 +99,7 @@ export class OpenerService implements IOpenerService { if (choice === 0) { return this.openExternal(resource); } else if (choice === 2) { - return this._commandService.executeCommand('_workbench.action.configureTrustedDomains', domainToOpen).then((pickedDomains: string[]) => { + return this._commandService.executeCommand('workbench.action.configureTrustedDomains', domainToOpen).then((pickedDomains: string[]) => { if (pickedDomains.indexOf(domainToOpen) !== -1) { return this.openExternal(resource); } diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 5fe78f41820e6..dc917095f0c9e 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { IURLService } from 'vs/platform/url/common/url'; @@ -13,6 +13,7 @@ import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export class OpenUrlAction extends Action { @@ -42,12 +43,14 @@ Registry.as(ActionExtensions.WorkbenchActions).registe localize('developer', 'Developer') ); +const VSCODE_DOMAIN = 'https://code.visualstudio.com'; + const configureTrustedDomainsHandler = ( quickInputService: IQuickInputService, storageService: IStorageService, domainToConfigure?: string ) => { - let trustedDomains: string[] = ['https://code.visualstudio.com']; + let trustedDomains: string[] = [VSCODE_DOMAIN]; try { const trustedDomainsSrc = storageService.get('http.trustedDomains', StorageScope.GLOBAL); @@ -75,7 +78,7 @@ const configureTrustedDomainsHandler = ( ]; let domainToConfigureItem: IQuickPickItem | undefined = undefined; - if (domainToConfigure) { + if (domainToConfigure && trustedDomains.indexOf(domainToConfigure) === -1) { domainToConfigureItem = { type: 'item', label: domainToConfigure, @@ -104,43 +107,24 @@ const configureTrustedDomainsHandler = ( }); }; -export class ConfigureTrustedDomainsAction extends Action { - - static readonly ID = 'workbench.action.configureTrustedDomains'; - static readonly LABEL = localize('configureTrustedDomains', "Configure Trusted Domains"); - - constructor( - id: string, - label: string, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IStorageService private readonly storageService: IStorageService - ) { - super(id, label); - } - - run(): Promise { - return configureTrustedDomainsHandler(this.quickInputService, this.storageService); - } -} - -Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( - new SyncActionDescriptor( - ConfigureTrustedDomainsAction, - ConfigureTrustedDomainsAction.ID, - ConfigureTrustedDomainsAction.LABEL - ), - 'Configure Trusted Domains' -); -CommandsRegistry.registerCommand({ - id: '_workbench.action.configureTrustedDomains', +const configureTrustedDomainCommand = { + id: 'workbench.action.configureTrustedDomains', description: { - description: 'Configure Trusted Domains', + description: localize('configureTrustedDomains', 'Configure Trusted Domains'), args: [{ name: 'domainToConfigure', schema: { type: 'string' } }] }, - handler: (accessor, domainToConfigure?: string) => { + handler: (accessor: ServicesAccessor, domainToConfigure?: string) => { const quickInputService = accessor.get(IQuickInputService); const storageService = accessor.get(IStorageService); return configureTrustedDomainsHandler(quickInputService, storageService, domainToConfigure); } +}; + +CommandsRegistry.registerCommand(configureTrustedDomainCommand); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: configureTrustedDomainCommand.id, + title: configureTrustedDomainCommand.description.description + } }); From cea5cbd07df785ec48056eb0ce22b04a4cfe2df6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Aug 2019 18:14:23 +0200 Subject: [PATCH 357/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e01ec81fc9710..0bcb75c265b3f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "79dcce2b3cd0772344a028cd703b7da03f3ffa83", + "distro": "5b190cd6f374ca91b14712a57cab6be8818f2a4a", "author": { "name": "Microsoft Corporation" }, From b9b5692e279699a5d2d1e1173098004a40408478 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 19 Aug 2019 09:09:08 -0700 Subject: [PATCH 358/613] Support using csharp in markdown preview to identify c# code blocks --- extensions/markdown-language-features/src/markdownEngine.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 0cc75bfe823ea..613d207712b62 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -297,13 +297,13 @@ async function getMarkdownOptions(md: () => MarkdownIt) { html: true, highlight: (str: string, lang?: string) => { // Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155 - if (lang && ['tsx', 'typescriptreact'].indexOf(lang.toLocaleLowerCase()) >= 0) { + if (lang && ['tsx', 'typescriptreact'].includes(lang.toLocaleLowerCase())) { lang = 'jsx'; } if (lang && lang.toLocaleLowerCase() === 'json5') { lang = 'json'; } - if (lang && lang.toLocaleLowerCase() === 'c#') { + if (lang && ['c#', 'csharp'].includes(lang.toLocaleLowerCase())) { lang = 'cs'; } if (lang && hljs.getLanguage(lang)) { From 78d462bd86c0e75b6f7128f68ef450ad6cb53562 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 19 Aug 2019 18:27:22 +0200 Subject: [PATCH 359/613] sync diagnostics, fixes #47292 --- .../api/browser/mainThreadDiagnostics.ts | 21 +++++++++- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostDiagnostics.ts | 38 +++++++++++++++---- .../api/common/extHostTypeConverters.ts | 18 +++++++++ .../api/extHostDiagnostics.test.ts | 36 ++++++++++++++++++ .../api/mainThreadDiagnostics.test.ts | 12 +++++- 6 files changed, 117 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDiagnostics.ts b/src/vs/workbench/api/browser/mainThreadDiagnostics.ts index 801a17d89a703..eb26b62da3aa2 100644 --- a/src/vs/workbench/api/browser/mainThreadDiagnostics.ts +++ b/src/vs/workbench/api/browser/mainThreadDiagnostics.ts @@ -5,24 +5,43 @@ import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { MainThreadDiagnosticsShape, MainContext, IExtHostContext } from '../common/extHost.protocol'; +import { MainThreadDiagnosticsShape, MainContext, IExtHostContext, ExtHostDiagnosticsShape, ExtHostContext } from '../common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { IDisposable } from 'vs/base/common/lifecycle'; @extHostNamedCustomer(MainContext.MainThreadDiagnostics) export class MainThreadDiagnostics implements MainThreadDiagnosticsShape { private readonly _activeOwners = new Set(); + + private readonly _proxy: ExtHostDiagnosticsShape; private readonly _markerService: IMarkerService; + private readonly _markerListener: IDisposable; constructor( extHostContext: IExtHostContext, @IMarkerService markerService: IMarkerService ) { + this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDiagnostics); this._markerService = markerService; + this._markerListener = this._markerService.onMarkerChanged(this._forwardMarkers, this); } dispose(): void { + this._markerListener.dispose(); this._activeOwners.forEach(owner => this._markerService.changeAll(owner, [])); + this._activeOwners.clear(); + } + + private _forwardMarkers(resources: URI[]): void { + const data: [UriComponents, IMarkerData[]][] = []; + for (const resource of resources) { + data.push([ + resource, + this._markerService.read({ resource }).filter(marker => !this._activeOwners.has(marker.owner)) + ]); + } + this._proxy.$acceptMarkersChange(data); } $changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 2b1f57a09d635..055786be74228 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -741,7 +741,7 @@ export interface ExtHostConfigurationShape { } export interface ExtHostDiagnosticsShape { - + $acceptMarkersChange(data: [UriComponents, IMarkerData[]][]): void; } export interface ExtHostDocumentContentProvidersShape { diff --git a/src/vs/workbench/api/common/extHostDiagnostics.ts b/src/vs/workbench/api/common/extHostDiagnostics.ts index 60dae259b05c7..d654408346bac 100644 --- a/src/vs/workbench/api/common/extHostDiagnostics.ts +++ b/src/vs/workbench/api/common/extHostDiagnostics.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import * as vscode from 'vscode'; import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol'; import { DiagnosticSeverity } from './extHostTypes'; @@ -20,12 +20,12 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { private readonly _owner: string; private readonly _maxDiagnosticsPerFile: number; private readonly _onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>; - private readonly _proxy: MainThreadDiagnosticsShape; + private readonly _proxy: MainThreadDiagnosticsShape | undefined; private _isDisposed = false; private _data = new Map(); - constructor(name: string, owner: string, maxDiagnosticsPerFile: number, proxy: MainThreadDiagnosticsShape, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) { + constructor(name: string, owner: string, maxDiagnosticsPerFile: number, proxy: MainThreadDiagnosticsShape | undefined, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) { this._name = name; this._owner = owner; this._maxDiagnosticsPerFile = maxDiagnosticsPerFile; @@ -36,7 +36,9 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { dispose(): void { if (!this._isDisposed) { this._onDidChangeDiagnostics.fire(keys(this._data)); - this._proxy.$clear(this._owner); + if (this._proxy) { + this._proxy.$clear(this._owner); + } this._data = undefined!; this._isDisposed = true; } @@ -112,6 +114,9 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { this._onDidChangeDiagnostics.fire(toSync); // compute change and send to main side + if (!this._proxy) { + return; + } const entries: [URI, IMarkerData[]][] = []; for (let uri of toSync) { let marker: IMarkerData[] = []; @@ -149,7 +154,6 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { entries.push([uri, marker]); } - this._proxy.$changeMany(this._owner, entries); } @@ -157,14 +161,18 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { this._checkDisposed(); this._onDidChangeDiagnostics.fire([uri]); this._data.delete(uri.toString()); - this._proxy.$changeMany(this._owner, [[uri, undefined]]); + if (this._proxy) { + this._proxy.$changeMany(this._owner, [[uri, undefined]]); + } } clear(): void { this._checkDisposed(); this._onDidChangeDiagnostics.fire(keys(this._data)); this._data.clear(); - this._proxy.$clear(this._owner); + if (this._proxy) { + this._proxy.$clear(this._owner); + } } forEach(callback: (uri: URI, diagnostics: ReadonlyArray, collection: DiagnosticCollection) => any, thisArg?: any): void { @@ -311,4 +319,20 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape { }); return res; } + + private _mirrorCollection: vscode.DiagnosticCollection | undefined; + + $acceptMarkersChange(data: [UriComponents, IMarkerData[]][]): void { + + if (!this._mirrorCollection) { + const name = '_generated_mirror'; + const collection = new DiagnosticCollection(name, name, ExtHostDiagnostics._maxDiagnosticsPerFile, undefined, this._onDidChangeDiagnostics); + this._collections.set(name, collection); + this._mirrorCollection = collection; + } + + for (const [uri, markers] of data) { + this._mirrorCollection.set(URI.revive(uri), markers.map(converter.Diagnostic.to)); + } + } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index f7827afe487b9..0fb461e8c977e 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -113,6 +113,15 @@ export namespace DiagnosticTag { } return undefined; } + export function to(value: MarkerTag): vscode.DiagnosticTag | undefined { + switch (value) { + case MarkerTag.Unnecessary: + return types.DiagnosticTag.Unnecessary; + case MarkerTag.Deprecated: + return types.DiagnosticTag.Deprecated; + } + return undefined; + } } export namespace Diagnostic { @@ -127,6 +136,15 @@ export namespace Diagnostic { tags: Array.isArray(value.tags) ? coalesce(value.tags.map(DiagnosticTag.from)) : undefined, }; } + + export function to(value: IMarkerData): vscode.Diagnostic { + const res = new types.Diagnostic(Range.to(value), value.message, DiagnosticSeverity.to(value.severity)); + res.source = value.source; + res.code = value.code; + res.relatedInformation = value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.to); + res.tags = value.tags && coalesce(value.tags.map(DiagnosticTag.to)); + return res; + } } export namespace DiagnosticRelatedInformation { diff --git a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts index 4eca9307e09a9..60181c415f256 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts @@ -424,4 +424,40 @@ suite('ExtHostDiagnostics', () => { collection.set(URI.parse('test:me'), array); assert.equal(callCount, 3); // same but un-equal array }); + + test('Diagnostics created by tasks aren\'t accessible to extensions #47292', async function () { + const diags = new ExtHostDiagnostics(new class implements IMainContext { + getProxy(id: any): any { + return {}; + } + set(): any { + return null; + } + assertRegistered(): void { + + } + }); + + + // + const uri = URI.parse('foo:bar'); + const data: IMarkerData[] = [{ + message: 'message', + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 1, + severity: 3 + }]; + + const p1 = Event.toPromise(diags.onDidChangeDiagnostics); + diags.$acceptMarkersChange([[uri, data]]); + await p1; + assert.equal(diags.getDiagnostics(uri).length, 1); + + const p2 = Event.toPromise(diags.onDidChangeDiagnostics); + diags.$acceptMarkersChange([[uri, []]]); + await p2; + assert.equal(diags.getDiagnostics(uri).length, 0); + }); }); diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts index e01923222da5e..72f53943e0454 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadDiagnostics.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { MarkerService } from 'vs/platform/markers/common/markerService'; import { MainThreadDiagnostics } from 'vs/workbench/api/browser/mainThreadDiagnostics'; import { URI } from 'vs/base/common/uri'; +import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; suite('MainThreadDiagnostics', function () { @@ -19,7 +20,16 @@ suite('MainThreadDiagnostics', function () { test('clear markers on dispose', function () { - let diag = new MainThreadDiagnostics(null!, markerService); + let diag = new MainThreadDiagnostics(new class implements IExtHostContext { + remoteAuthority = ''; + assertRegistered() { } + set(v: any): any { return null; } + getProxy(): any { + return { + $acceptMarkersChange() { } + }; + } + }, markerService); diag.$changeMany('foo', [[URI.file('a'), [{ code: '666', From 997d73516c65afbbb9939ce6bf55a97a82a8bbc7 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 19 Aug 2019 10:09:13 -0700 Subject: [PATCH 360/613] fixes #79280 --- src/vs/base/browser/ui/splitview/splitview.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index b5102c9f9b304..81090f39c2cce 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -117,12 +117,11 @@ abstract class ViewItem { if (typeof size === 'number') { this._size = size; this._cachedVisibleSize = undefined; + dom.addClass(container, 'visible'); } else { this._size = 0; this._cachedVisibleSize = size.cachedVisibleSize; } - - dom.addClass(container, 'visible'); } layout(_orthogonalSize: number | undefined): void { From 949ec731d8dd1b69fb9c84b6ba6b67cb1d714661 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Aug 2019 19:11:31 +0200 Subject: [PATCH 361/613] tests - more diagnostics for getUntitledWorkspaceSync() random failures --- .../test/electron-main/workspacesMainService.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 395e709bd283d..16b3f1d60d75e 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -343,11 +343,12 @@ suite('WorkspacesMainService', () => { 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?`); + const untitledHome = dirname(dirname(untitledTwo.configPath)); + const beforeGettingUntitledWorkspaces = fs.readdirSync(untitledHome.fsPath).map(name => fs.readFileSync(joinPath(untitledHome, name, 'workspace.json').fsPath, 'utf8')); 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.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'))}, before getUntitledWorkspacesSync: ${beforeGettingUntitledWorkspaces}`); } assert.equal(2, untitled.length); From 0361f77d9e5124841b83bd01ecabb4449fcb7b25 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Aug 2019 19:21:25 +0200 Subject: [PATCH 362/613] debt - reduce dependencies diff to E6 branch --- package.json | 2 +- remote/package.json | 2 +- remote/yarn.lock | 12 +++++------- yarn.lock | 8 ++++---- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 0bcb75c265b3f..a9793ee759cd6 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "fast-plist": "0.1.2", "glob": "^5.0.13", "gulp": "^4.0.0", - "gulp-atom-electron": "^1.21.1", + "gulp-atom-electron": "^1.22.0", "gulp-azure-storage": "^0.10.0", "gulp-buffer": "0.0.2", "gulp-concat": "^2.6.1", diff --git a/remote/package.json b/remote/package.json index 9bbc0f44c5c31..2e8ceed08091f 100644 --- a/remote/package.json +++ b/remote/package.json @@ -29,6 +29,6 @@ }, "optionalDependencies": { "vscode-windows-ca-certs": "0.1.0", - "vscode-windows-registry": "1.0.1" + "vscode-windows-registry": "1.0.2" } } diff --git a/remote/yarn.lock b/remote/yarn.lock index 54e0619915e23..dca0e2501f02b 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -743,7 +743,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -nan@^2.0.0, nan@^2.12.1, nan@^2.13.2, nan@^2.14.0: +nan@^2.0.0, nan@^2.13.2, nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -1210,12 +1210,10 @@ vscode-windows-ca-certs@0.1.0: dependencies: node-addon-api "1.6.2" -vscode-windows-registry@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.1.tgz#bc9f765563eb6dc1c9ad9a41f9eaacc84dfadc7c" - integrity sha512-q0aKXi9Py1OBdmXIJJFeJBzpPJMMUxMJNBU9FysWIXEwJyMQGEVevKzM2J3Qz/cHSc5LVqibmoUWzZ7g+97qRg== - dependencies: - nan "^2.12.1" +vscode-windows-registry@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.2.tgz#b863e704a6a69c50b3098a55fbddbe595b0c124a" + integrity sha512-/CLLvuOSM2Vme2z6aNyB+4Omd7hDxpf4Thrt8ImxnXeQtxzel2bClJpFQvQqK/s4oaXlkBKS7LqVLeZM+uSVIA== xterm-addon-search@0.2.0-beta5: version "0.2.0-beta5" diff --git a/yarn.lock b/yarn.lock index 5dfdc062d4f3b..78e871f843b02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3831,10 +3831,10 @@ growl@1.9.2: resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= -gulp-atom-electron@^1.21.1: - version "1.21.1" - resolved "https://registry.yarnpkg.com/gulp-atom-electron/-/gulp-atom-electron-1.21.1.tgz#4017144bf659fbbf7d0644664fcc47c64efac0f0" - integrity sha512-UHEf2pZrJD/u+AAzKCbhdPXaKrReFDa+OEJjBCAdN2SHnD+dfZqSJAz/u2OD6YR/eREuUbQOCw+VWUwex20klQ== +gulp-atom-electron@^1.22.0: + version "1.22.0" + resolved "https://registry.yarnpkg.com/gulp-atom-electron/-/gulp-atom-electron-1.22.0.tgz#0e2f4fe7c7310145c6c81d5660d0277cd8338d27" + integrity sha512-K13Ze2+iRIvC1igl+BIbZdF8M2ZzEIM6LF+/j83oWNGNhe7+en77qT9y9rs6SipYeOyoInlbly+pzbFgvwpSHA== dependencies: event-stream "3.3.4" github-releases-ms "^0.5.0" From 17f0eac6567f1ded08bdffb286bab17fbf406081 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Aug 2019 19:38:09 +0200 Subject: [PATCH 363/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a9793ee759cd6..bfc482a9e1d51 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "5b190cd6f374ca91b14712a57cab6be8818f2a4a", + "distro": "448bb39d6aabd312c4a28393314e7e406c52acb8", "author": { "name": "Microsoft Corporation" }, From b9748c53f041b4b43f5f7828a08531a1d8d69bc3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Aug 2019 20:02:11 +0200 Subject: [PATCH 364/613] debt - menubar service should not live in common (#79181) --- src/vs/code/electron-main/app.ts | 2 +- .../electron-browser/menubarService.ts | 2 +- .../platform/menubar/electron-main/menubar.ts | 2 +- .../menubar/electron-main/menubarService.ts | 2 +- .../menubar/{common => node}/menubar.ts | 0 src/vs/platform/menubar/node/menubarIpc.ts | 2 +- .../browser/parts/titlebar/menubarControl.ts | 193 +--------------- .../browser/parts/titlebar/titlebarPart.ts | 25 +-- src/vs/workbench/electron-browser/window.ts | 212 +++++++++++++++++- src/vs/workbench/workbench.desktop.main.ts | 2 +- 10 files changed, 226 insertions(+), 216 deletions(-) rename src/vs/platform/menubar/{common => node}/menubar.ts (100%) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 1953c269a6b3a..d269616e1b9dd 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -53,7 +53,7 @@ import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc'; import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/errors'; import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; -import { IMenubarService } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService } from 'vs/platform/menubar/node/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService'; import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc'; import { hasArgs } from 'vs/platform/environment/node/argv'; diff --git a/src/vs/platform/menubar/electron-browser/menubarService.ts b/src/vs/platform/menubar/electron-browser/menubarService.ts index 794d65de6a7fd..336330d1e6d7c 100644 --- a/src/vs/platform/menubar/electron-browser/menubarService.ts +++ b/src/vs/platform/menubar/electron-browser/menubarService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService, IMenubarData } from 'vs/platform/menubar/node/menubar'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 01f781dd94490..ff9d06a636b69 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -17,7 +17,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { mnemonicMenuLabel as baseMnemonicLabel } from 'vs/base/common/labels'; import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService } from 'vs/platform/history/common/history'; -import { IMenubarData, IMenubarKeybinding, MenubarMenuItem, isMenubarMenuItemSeparator, isMenubarMenuItemSubmenu, isMenubarMenuItemAction, IMenubarMenu, isMenubarMenuItemUriAction } from 'vs/platform/menubar/common/menubar'; +import { IMenubarData, IMenubarKeybinding, MenubarMenuItem, isMenubarMenuItemSeparator, isMenubarMenuItemSubmenu, isMenubarMenuItemAction, IMenubarMenu, isMenubarMenuItemUriAction } from 'vs/platform/menubar/node/menubar'; import { URI } from 'vs/base/common/uri'; import { IStateService } from 'vs/platform/state/common/state'; import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; diff --git a/src/vs/platform/menubar/electron-main/menubarService.ts b/src/vs/platform/menubar/electron-main/menubarService.ts index b4a8ed44ef489..f7abf2ee9f26d 100644 --- a/src/vs/platform/menubar/electron-main/menubarService.ts +++ b/src/vs/platform/menubar/electron-main/menubarService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService, IMenubarData } from 'vs/platform/menubar/node/menubar'; import { Menubar } from 'vs/platform/menubar/electron-main/menubar'; import { ILogService } from 'vs/platform/log/common/log'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/platform/menubar/common/menubar.ts b/src/vs/platform/menubar/node/menubar.ts similarity index 100% rename from src/vs/platform/menubar/common/menubar.ts rename to src/vs/platform/menubar/node/menubar.ts diff --git a/src/vs/platform/menubar/node/menubarIpc.ts b/src/vs/platform/menubar/node/menubarIpc.ts index 58f22e631b472..850a2542dd05d 100644 --- a/src/vs/platform/menubar/node/menubarIpc.ts +++ b/src/vs/platform/menubar/node/menubarIpc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IMenubarService } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService } from 'vs/platform/menubar/node/menubar'; import { Event } from 'vs/base/common/event'; export class MenubarChannel implements IServerChannel { diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index bb5dda0c50f07..0a45e07062ed0 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IMenubarMenu, IMenubarMenuItemAction, IMenubarMenuItemSubmenu, IMenubarKeybinding, IMenubarService, IMenubarData, MenubarMenuItem } from 'vs/platform/menubar/common/menubar'; import { IMenuService, MenuId, IMenu, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService'; import { IWindowService, MenuBarVisibility, IWindowsService, getTitleBarStyle, IURIToOpen } from 'vs/platform/windows/common/windows'; @@ -33,7 +32,6 @@ import { attachMenuStyler } from 'vs/platform/theme/common/styler'; import { assign } from 'vs/base/common/objects'; import { mnemonicMenuLabel, unmnemonicLabel } from 'vs/base/common/labels'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { isFullscreen } from 'vs/base/browser/browser'; @@ -133,7 +131,7 @@ export abstract class MenubarControl extends Disposable { this.menuUpdater.schedule(); } - protected calculateActionLabel(action: IAction | IMenubarMenuItemAction): string { + protected calculateActionLabel(action: { id: string; label: string; }): string { let label = action.label; switch (action.id) { default: @@ -251,195 +249,6 @@ export abstract class MenubarControl extends Disposable { } } -export class NativeMenubarControl extends MenubarControl { - constructor( - @IMenuService menuService: IMenuService, - @IWindowService windowService: IWindowService, - @IWindowsService windowsService: IWindowsService, - @IContextKeyService contextKeyService: IContextKeyService, - @IKeybindingService keybindingService: IKeybindingService, - @IConfigurationService configurationService: IConfigurationService, - @ILabelService labelService: ILabelService, - @IUpdateService updateService: IUpdateService, - @IStorageService storageService: IStorageService, - @INotificationService notificationService: INotificationService, - @IPreferencesService preferencesService: IPreferencesService, - @IEnvironmentService environmentService: IEnvironmentService, - @IAccessibilityService accessibilityService: IAccessibilityService, - @IMenubarService private readonly menubarService: IMenubarService - ) { - super( - menuService, - windowService, - windowsService, - contextKeyService, - keybindingService, - configurationService, - labelService, - updateService, - storageService, - notificationService, - preferencesService, - environmentService, - accessibilityService); - - if (isMacintosh && !isWeb) { - this.menus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService)); - this.topLevelTitles['Preferences'] = nls.localize('mPreferences', "Preferences"); - } - - for (const topLevelMenuName of Object.keys(this.topLevelTitles)) { - const menu = this.menus[topLevelMenuName]; - if (menu) { - this._register(menu.onDidChange(() => this.updateMenubar())); - } - } - - this.windowService.getRecentlyOpened().then((recentlyOpened) => { - this.recentlyOpened = recentlyOpened; - - this.doUpdateMenubar(true); - }); - - this.registerListeners(); - } - - protected doUpdateMenubar(firstTime: boolean): void { - // Send menus to main process to be rendered by Electron - const menubarData = { menus: {}, keybindings: {} }; - if (this.getMenubarMenus(menubarData)) { - this.menubarService.updateMenubar(this.windowService.windowId, menubarData); - } - } - - private getMenubarMenus(menubarData: IMenubarData): boolean { - if (!menubarData) { - return false; - } - - menubarData.keybindings = this.getAdditionalKeybindings(); - for (const topLevelMenuName of Object.keys(this.topLevelTitles)) { - const menu = this.menus[topLevelMenuName]; - if (menu) { - const menubarMenu: IMenubarMenu = { items: [] }; - this.populateMenuItems(menu, menubarMenu, menubarData.keybindings); - if (menubarMenu.items.length === 0) { - // Menus are incomplete - return false; - } - menubarData.menus[topLevelMenuName] = menubarMenu; - } - } - - return true; - } - - private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding | undefined }) { - let groups = menu.getActions(); - for (let group of groups) { - const [, actions] = group; - - actions.forEach(menuItem => { - - if (menuItem instanceof SubmenuItemAction) { - const submenu = { items: [] }; - - if (!this.menus[menuItem.item.submenu]) { - this.menus[menuItem.item.submenu] = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService); - this._register(this.menus[menuItem.item.submenu]!.onDidChange(() => this.updateMenubar())); - } - - const menuToDispose = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService); - this.populateMenuItems(menuToDispose, submenu, keybindings); - - let menubarSubmenuItem: IMenubarMenuItemSubmenu = { - id: menuItem.id, - label: menuItem.label, - submenu: submenu - }; - - menuToPopulate.items.push(menubarSubmenuItem); - menuToDispose.dispose(); - } else { - if (menuItem.id === 'workbench.action.openRecent') { - const actions = this.getOpenRecentActions().map(this.transformOpenRecentAction); - menuToPopulate.items.push(...actions); - } - - let menubarMenuItem: IMenubarMenuItemAction = { - id: menuItem.id, - label: menuItem.label - }; - - if (menuItem.checked) { - menubarMenuItem.checked = true; - } - - if (!menuItem.enabled) { - menubarMenuItem.enabled = false; - } - - menubarMenuItem.label = this.calculateActionLabel(menubarMenuItem); - keybindings[menuItem.id] = this.getMenubarKeybinding(menuItem.id); - menuToPopulate.items.push(menubarMenuItem); - } - }); - - menuToPopulate.items.push({ id: 'vscode.menubar.separator' }); - } - - if (menuToPopulate.items.length > 0) { - menuToPopulate.items.pop(); - } - } - - private transformOpenRecentAction(action: Separator | (IAction & { uri: URI })): MenubarMenuItem { - if (action instanceof Separator) { - return { id: 'vscode.menubar.separator' }; - } - - return { - id: action.id, - uri: action.uri, - enabled: action.enabled, - label: action.label - }; - } - - private getAdditionalKeybindings(): { [id: string]: IMenubarKeybinding } { - const keybindings: { [id: string]: IMenubarKeybinding } = {}; - if (isMacintosh) { - const keybinding = this.getMenubarKeybinding('workbench.action.quit'); - if (keybinding) { - keybindings['workbench.action.quit'] = keybinding; - } - } - - return keybindings; - } - - private getMenubarKeybinding(id: string): IMenubarKeybinding | undefined { - const binding = this.keybindingService.lookupKeybinding(id); - if (!binding) { - return undefined; - } - - // first try to resolve a native accelerator - const electronAccelerator = binding.getElectronAccelerator(); - if (electronAccelerator) { - return { label: electronAccelerator, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) }; - } - - // we need this fallback to support keybindings that cannot show in electron menus (e.g. chords) - const acceleratorLabel = binding.getLabel(); - if (acceleratorLabel) { - return { label: acceleratorLabel, isNative: false, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) }; - } - - return undefined; - } -} - export class CustomMenubarControl extends MenubarControl { private menubar: MenuBar; private container: HTMLElement; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 66bf29cdd22c3..fbf3423014fdc 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -27,7 +27,7 @@ import { URI } from 'vs/base/common/uri'; import { Color } from 'vs/base/common/color'; import { trim } from 'vs/base/common/strings'; import { EventType, EventHelper, Dimension, isAncestor, hide, show, removeClass, addClass, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; -import { MenubarControl, NativeMenubarControl, CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; +import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { template, getBaseLabel } from 'vs/base/common/labels'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -65,7 +65,7 @@ export class TitlebarPart extends Part implements ITitleService { private windowControls: HTMLElement; private maxRestoreControl: HTMLElement; private appIcon: HTMLElement; - private menubarPart: MenubarControl; + private customMenubar: CustomMenubarControl | undefined; private menubar: HTMLElement; private resizer: HTMLElement; private lastLayoutDimensions: Dimension; @@ -332,19 +332,16 @@ export class TitlebarPart extends Part implements ITitleService { }))); } - // Menubar: the menubar part which is responsible for populating both the custom and native menubars - if ((isMacintosh && !isWeb) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { - this.menubarPart = this.instantiationService.createInstance(NativeMenubarControl); - } else { - const customMenubarControl = this.instantiationService.createInstance(CustomMenubarControl); - this.menubarPart = customMenubarControl; + // Menubar: install a custom menu bar depending on configuration + if (getTitleBarStyle(this.configurationService, this.environmentService) !== 'native' && (!isMacintosh || isWeb)) { + this.customMenubar = this._register(this.instantiationService.createInstance(CustomMenubarControl)); this.menubar = append(this.element, $('div.menubar')); this.menubar.setAttribute('role', 'menubar'); - customMenubarControl.create(this.menubar); + this.customMenubar.create(this.menubar); - this._register(customMenubarControl.onVisibilityChange(e => this.onMenubarVisibilityChanged(e))); - this._register(customMenubarControl.onFocusStateChange(e => this.onMenubarFocusChanged(e))); + this._register(this.customMenubar.onVisibilityChange(e => this.onMenubarVisibilityChanged(e))); + this._register(this.customMenubar.onFocusStateChange(e => this.onMenubarFocusChanged(e))); } // Title @@ -547,7 +544,7 @@ export class TitlebarPart extends Part implements ITitleService { } private adjustTitleMarginToCenter(): void { - if (this.menubarPart instanceof CustomMenubarControl) { + if (this.customMenubar) { const leftMarker = (this.appIcon ? this.appIcon.clientWidth : 0) + this.menubar.clientWidth + 10; const rightMarker = this.element.clientWidth - (this.windowControls ? this.windowControls.clientWidth : 0) - 10; @@ -588,9 +585,9 @@ export class TitlebarPart extends Part implements ITitleService { runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter()); - if (this.menubarPart instanceof CustomMenubarControl) { + if (this.customMenubar) { const menubarDimension = new Dimension(0, dimension.height); - this.menubarPart.layout(menubarDimension); + this.customMenubar.layout(menubarDimension); } } } diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index bfc7916011e5d..57dd6170e9a0d 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -14,7 +14,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { toResource, IUntitledResourceInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IRunKeybindingInWindowRequest } from 'vs/platform/windows/common/windows'; +import { IWindowsService, IWindowService, IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IRunKeybindingInWindowRequest, getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; @@ -24,7 +24,7 @@ import { IResourceInput } from 'vs/platform/editor/common/editor'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; import { ipcRenderer as ipc, webFrame, crashReporter, Event } from 'electron'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; -import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction } from 'vs/platform/actions/common/actions'; +import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -32,7 +32,7 @@ import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecyc import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity'; -import { isRootUser, isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; +import { isRootUser, isWindows, isMacintosh, isLinux, isWeb } from 'vs/base/common/platform'; import product from 'vs/platform/product/node/product'; import pkg from 'vs/platform/product/node/package'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -45,6 +45,15 @@ import { coalesce } from 'vs/base/common/arrays'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { isEqual } from 'vs/base/common/resources'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { MenubarControl } from '../browser/parts/titlebar/menubarControl'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IUpdateService } from 'vs/platform/update/common/update'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IPreferencesService } from '../services/preferences/common/preferences'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IMenubarService, IMenubarData, IMenubarMenu, IMenubarKeybinding, IMenubarMenuItemSubmenu, IMenubarMenuItemAction, MenubarMenuItem } from 'vs/platform/menubar/node/menubar'; +import { withNullAsUndefined } from 'vs/base/common/types'; const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))), @@ -91,7 +100,8 @@ export class ElectronWindow extends Disposable { @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @ITextFileService private readonly textFileService: ITextFileService + @ITextFileService private readonly textFileService: ITextFileService, + @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); @@ -297,6 +307,11 @@ export class ElectronWindow extends Disposable { private create(): void { + // Native menu controller + if (isMacintosh || getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { + this._register(this.instantiationService.createInstance(NativeMenubarControl)); + } + // Handle window.open() calls const $this = this; window.open = function (url: string, target: string, features: string, replace: boolean): Window | null { @@ -538,3 +553,192 @@ export class ElectronWindow extends Disposable { return this.editorService.openEditors(resources); } } + +class NativeMenubarControl extends MenubarControl { + constructor( + @IMenuService menuService: IMenuService, + @IWindowService windowService: IWindowService, + @IWindowsService windowsService: IWindowsService, + @IContextKeyService contextKeyService: IContextKeyService, + @IKeybindingService keybindingService: IKeybindingService, + @IConfigurationService configurationService: IConfigurationService, + @ILabelService labelService: ILabelService, + @IUpdateService updateService: IUpdateService, + @IStorageService storageService: IStorageService, + @INotificationService notificationService: INotificationService, + @IPreferencesService preferencesService: IPreferencesService, + @IEnvironmentService environmentService: IEnvironmentService, + @IAccessibilityService accessibilityService: IAccessibilityService, + @IMenubarService private readonly menubarService: IMenubarService + ) { + super( + menuService, + windowService, + windowsService, + contextKeyService, + keybindingService, + configurationService, + labelService, + updateService, + storageService, + notificationService, + preferencesService, + environmentService, + accessibilityService); + + if (isMacintosh && !isWeb) { + this.menus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService)); + this.topLevelTitles['Preferences'] = nls.localize('mPreferences', "Preferences"); + } + + for (const topLevelMenuName of Object.keys(this.topLevelTitles)) { + const menu = this.menus[topLevelMenuName]; + if (menu) { + this._register(menu.onDidChange(() => this.updateMenubar())); + } + } + + this.windowService.getRecentlyOpened().then((recentlyOpened) => { + this.recentlyOpened = recentlyOpened; + + this.doUpdateMenubar(true); + }); + + this.registerListeners(); + } + + protected doUpdateMenubar(firstTime: boolean): void { + + // Send menus to main process to be rendered by Electron + const menubarData = { menus: {}, keybindings: {} }; + if (this.getMenubarMenus(menubarData)) { + this.menubarService.updateMenubar(this.windowService.windowId, menubarData); + } + } + + private getMenubarMenus(menubarData: IMenubarData): boolean { + if (!menubarData) { + return false; + } + + menubarData.keybindings = this.getAdditionalKeybindings(); + for (const topLevelMenuName of Object.keys(this.topLevelTitles)) { + const menu = this.menus[topLevelMenuName]; + if (menu) { + const menubarMenu: IMenubarMenu = { items: [] }; + this.populateMenuItems(menu, menubarMenu, menubarData.keybindings); + if (menubarMenu.items.length === 0) { + return false; // Menus are incomplete + } + menubarData.menus[topLevelMenuName] = menubarMenu; + } + } + + return true; + } + + private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding | undefined }) { + let groups = menu.getActions(); + for (let group of groups) { + const [, actions] = group; + + actions.forEach(menuItem => { + + if (menuItem instanceof SubmenuItemAction) { + const submenu = { items: [] }; + + if (!this.menus[menuItem.item.submenu]) { + this.menus[menuItem.item.submenu] = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService); + this._register(this.menus[menuItem.item.submenu]!.onDidChange(() => this.updateMenubar())); + } + + const menuToDispose = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService); + this.populateMenuItems(menuToDispose, submenu, keybindings); + + let menubarSubmenuItem: IMenubarMenuItemSubmenu = { + id: menuItem.id, + label: menuItem.label, + submenu: submenu + }; + + menuToPopulate.items.push(menubarSubmenuItem); + menuToDispose.dispose(); + } else { + if (menuItem.id === 'workbench.action.openRecent') { + const actions = this.getOpenRecentActions().map(this.transformOpenRecentAction); + menuToPopulate.items.push(...actions); + } + + let menubarMenuItem: IMenubarMenuItemAction = { + id: menuItem.id, + label: menuItem.label + }; + + if (menuItem.checked) { + menubarMenuItem.checked = true; + } + + if (!menuItem.enabled) { + menubarMenuItem.enabled = false; + } + + menubarMenuItem.label = this.calculateActionLabel(menubarMenuItem); + keybindings[menuItem.id] = this.getMenubarKeybinding(menuItem.id); + menuToPopulate.items.push(menubarMenuItem); + } + }); + + menuToPopulate.items.push({ id: 'vscode.menubar.separator' }); + } + + if (menuToPopulate.items.length > 0) { + menuToPopulate.items.pop(); + } + } + + private transformOpenRecentAction(action: Separator | (IAction & { uri: URI })): MenubarMenuItem { + if (action instanceof Separator) { + return { id: 'vscode.menubar.separator' }; + } + + return { + id: action.id, + uri: action.uri, + enabled: action.enabled, + label: action.label + }; + } + + private getAdditionalKeybindings(): { [id: string]: IMenubarKeybinding } { + const keybindings: { [id: string]: IMenubarKeybinding } = {}; + if (isMacintosh) { + const keybinding = this.getMenubarKeybinding('workbench.action.quit'); + if (keybinding) { + keybindings['workbench.action.quit'] = keybinding; + } + } + + return keybindings; + } + + private getMenubarKeybinding(id: string): IMenubarKeybinding | undefined { + const binding = this.keybindingService.lookupKeybinding(id); + if (!binding) { + return undefined; + } + + // first try to resolve a native accelerator + const electronAccelerator = binding.getElectronAccelerator(); + if (electronAccelerator) { + return { label: electronAccelerator, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) }; + } + + // we need this fallback to support keybindings that cannot show in electron menus (e.g. chords) + const acceleratorLabel = binding.getLabel(); + if (acceleratorLabel) { + return { label: acceleratorLabel, isNative: false, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) }; + } + + return undefined; + } +} \ No newline at end of file diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 04981fd3a82af..7aa1334e1f4ee 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -69,7 +69,7 @@ import { IIssueService } from 'vs/platform/issue/node/issue'; import { IssueService } from 'vs/platform/issue/electron-browser/issueService'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { WorkspacesService } from 'vs/platform/workspaces/electron-browser/workspacesService'; -import { IMenubarService } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService } from 'vs/platform/menubar/node/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; import { IURLService } from 'vs/platform/url/common/url'; import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; From 0fe953811068ffb98736c2f9c0de918ccd518f39 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 19 Aug 2019 20:17:32 +0200 Subject: [PATCH 365/613] distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bfc482a9e1d51..b8ed8e8fea7a8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "448bb39d6aabd312c4a28393314e7e406c52acb8", + "distro": "2f7403275ca8931a7265b58ac35e33096ce0d074", "author": { "name": "Microsoft Corporation" }, From 69f99d3439df22ec5b07358594c7d2bc32e3eb2c Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 19 Aug 2019 11:17:22 -0700 Subject: [PATCH 366/613] Unify trusted domain language and use quick pick item id --- src/vs/editor/browser/services/openerService.ts | 2 +- src/vs/workbench/contrib/url/common/url.contribution.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 60be6dadd5934..c92e5a70c7665 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -91,7 +91,7 @@ export class OpenerService implements IOpenerService { [ localize('openLink', 'Open Link'), localize('cancel', 'Cancel'), - localize('configureLinkPermission', 'Configure Link Permission'), + localize('configureTrustedDomains', 'Configure Trusted Domains') ], { cancelId: 1 diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index dc917095f0c9e..475265542b12f 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -65,6 +65,7 @@ const configureTrustedDomainsHandler = ( return { type: 'item', label: d, + id: d, picked: true, }; }); @@ -73,6 +74,7 @@ const configureTrustedDomainsHandler = ( { type: 'item', label: localize('openAllLinksWithoutPrompt', 'Open all links without prompt'), + id: '*', picked: trustedDomains.indexOf('*') !== -1 } ]; @@ -82,6 +84,7 @@ const configureTrustedDomainsHandler = ( domainToConfigureItem = { type: 'item', label: domainToConfigure, + id: domainToConfigure, picked: true, description: localize('trustDomainAndOpenLink', 'Trust domain and open link') }; @@ -97,7 +100,7 @@ const configureTrustedDomainsHandler = ( activeItem: domainToConfigureItem }).then(result => { if (result) { - const pickedDomains = result.map(r => r.label); + const pickedDomains = result.map(r => r.id); storageService.store('http.trustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL); return pickedDomains; From 94c6d0482f89e04f4aa7903f0ef1947316c7f3f9 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 19 Aug 2019 11:49:30 -0700 Subject: [PATCH 367/613] Update browser telemetry common properties --- src/vs/base/common/platform.ts | 14 ++++++-------- .../telemetry/browser/workbenchCommonProperties.ts | 7 ++++--- src/vs/workbench/browser/web.main.ts | 2 +- .../environment/browser/environmentService.ts | 2 ++ 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index d7371552d30c9..07759dffe5979 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -93,14 +93,12 @@ export function PlatformToString(platform: Platform) { } let _platform: Platform = Platform.Web; -if (_isNative) { - if (_isMacintosh) { - _platform = Platform.Mac; - } else if (_isWindows) { - _platform = Platform.Windows; - } else if (_isLinux) { - _platform = Platform.Linux; - } +if (_isMacintosh) { + _platform = Platform.Mac; +} else if (_isWindows) { + _platform = Platform.Windows; +} else if (_isLinux) { + _platform = Platform.Linux; } export const isWindows = _isWindows; diff --git a/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts b/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts index 8f2d241306144..ac0da4361f762 100644 --- a/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/browser/workbenchCommonProperties.ts @@ -16,18 +16,19 @@ import { cleanRemoteAuthority } from 'vs/platform/telemetry/common/telemetryUtil export async function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string | undefined, version: string | undefined, machineId: string, remoteAuthority?: string): Promise<{ [name: string]: string | undefined }> { const result: { [name: string]: string | undefined; } = Object.create(null); - const instanceId = storageService.get(instanceStorageKey, StorageScope.GLOBAL)!; const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL)!; const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL)!; + /** + * Note: In the web, session date information is fetched from browser storage, so these dates are tied to a specific + * browser and not the machine overall. + */ // __GDPR__COMMON__ "common.firstSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } result['common.firstSessionDate'] = firstSessionDate; // __GDPR__COMMON__ "common.lastSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } result['common.lastSessionDate'] = lastSessionDate || ''; // __GDPR__COMMON__ "common.isNewSession" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } result['common.isNewSession'] = !lastSessionDate ? '1' : '0'; - // __GDPR__COMMON__ "common.instanceId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - result['common.instanceId'] = instanceId; // __GDPR__COMMON__ "common.remoteAuthority" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } result['common.remoteAuthority'] = cleanRemoteAuthority(remoteAuthority); diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index e882484c10b7a..b4a31c9a35603 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -204,7 +204,7 @@ class CodeRendererMain extends Disposable { version: '1.38.0-unknown', nameLong: 'Unknown', extensionAllowedProposedApi: [], - }, ...{ urlProtocol: '', enableTelemetry: false } + }, ...{ urlProtocol: ''} }; return { _serviceBrand: undefined, ...productConfiguration }; } diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 318caeb3d77f2..6cbe59689dbd7 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -15,6 +15,7 @@ import { joinPath } from 'vs/base/common/resources'; import { Schemas } from 'vs/base/common/network'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; +import { generateUuid } from 'vs/base/common/uuid'; export class BrowserWindowConfiguration implements IWindowConfiguration { @@ -80,6 +81,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment this.appNameLong = 'Visual Studio Code - Web'; this.configuration.remoteAuthority = options.remoteAuthority; + this.configuration.machineId = generateUuid(); this.userRoamingDataHome = URI.file('/User').with({ scheme: Schemas.userData }); this.settingsResource = joinPath(this.userRoamingDataHome, 'settings.json'); this.keybindingsResource = joinPath(this.userRoamingDataHome, 'keybindings.json'); From 8d67f9b13a0290c57a9ca13b150f752918cbebab Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 19 Aug 2019 21:05:28 +0200 Subject: [PATCH 368/613] #79454 Fix warnings --- src/vs/workbench/browser/parts/views/customView.ts | 4 +++- src/vs/workbench/common/views.ts | 2 -- .../contrib/preferences/browser/keybindingsEditor.ts | 2 +- src/vs/workbench/contrib/preferences/common/preferences.ts | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 7e98724953dbc..2fcbf196e0d3a 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -69,7 +69,9 @@ export class CustomTreeViewPanel extends ViewletPanel { } renderBody(container: HTMLElement): void { - this.treeView.show(container); + if (this.treeView instanceof CustomTreeView) { + this.treeView.show(container); + } } layoutBody(height: number, width: number): void { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index ab8b188c72dc0..7306d530fcb5c 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -334,8 +334,6 @@ export interface ITreeView extends IDisposable { layout(height: number, width: number): void; - show(container: HTMLElement): void; - getOptimalWidth(): number; reveal(item: ITreeItem): Promise; diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index d83000eaaa41a..a45a2680f325d 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -789,7 +789,7 @@ class KeybindingItemRenderer implements IListRenderer; defineWhenExpression(keybindingEntry: IKeybindingItemEntry): void; From b3827c8b373ac1335e573ad8e6480fcc3a114b69 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 19 Aug 2019 21:08:33 +0200 Subject: [PATCH 369/613] Fix #79429 --- .../extensions/browser/extensionTipsService.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts index ebf0b5ba3366f..3e7884ae1f7ae 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts @@ -520,8 +520,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return false; } - const id = recommendationsToSuggest[0]; - const tip = this._importantExeBasedRecommendations[id]; + const extensionId = recommendationsToSuggest[0]; + const tip = this._importantExeBasedRecommendations[extensionId]; const message = localize('exeRecommended', "The '{0}' extension is recommended as you have {1} installed on your system.", tip.friendlyName!, tip.exeFriendlyName || basename(tip.windowsPath!)); this.notificationService.prompt(Severity.Info, message, @@ -534,8 +534,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } } */ - this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'install', extensionId: name }); - this.instantiationService.createInstance(InstallRecommendedExtensionAction, id).run(); + this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'install', extensionId }); + this.instantiationService.createInstance(InstallRecommendedExtensionAction, extensionId).run(); } }, { label: localize('showRecommendations', "Show Recommendations"), @@ -546,7 +546,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } } */ - this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'show', extensionId: name }); + this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'show', extensionId }); const recommendationsAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); recommendationsAction.run(); @@ -556,14 +556,14 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe label: choiceNever, isSecondary: true, run: () => { - this.addToImportantRecommendationsIgnore(id); + this.addToImportantRecommendationsIgnore(extensionId); /* __GDPR__ "exeExtensionRecommendations:popup" : { "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } } */ - this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: name }); + this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId }); this.notificationService.prompt( Severity.Info, localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), @@ -586,7 +586,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } } */ - this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'cancelled', extensionId: name }); + this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'cancelled', extensionId }); } } ); From 56b7625239e507ed274555495a2f2ca1a6595ffa Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 19 Aug 2019 12:58:51 -0700 Subject: [PATCH 370/613] Fix hygiene check --- src/vs/workbench/browser/web.main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index b4a31c9a35603..dcb44e8c137bb 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -204,7 +204,7 @@ class CodeRendererMain extends Disposable { version: '1.38.0-unknown', nameLong: 'Unknown', extensionAllowedProposedApi: [], - }, ...{ urlProtocol: ''} + }, ...{ urlProtocol: '' } }; return { _serviceBrand: undefined, ...productConfiguration }; } From e9b4a91e4a735343faeaeeaf88aa2c65c6b7943a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 19 Aug 2019 19:40:31 -0700 Subject: [PATCH 371/613] Pass rendered markdown through additional sanitizer Uses insane to process rendered markdown. Adds an additional level of protection for context injections --- build/gulpfile.hygiene.js | 2 + src/vs/base/browser/markdownRenderer.ts | 16 +- src/vs/base/common/insane/cgmanifest.json | 17 + src/vs/base/common/insane/insane.d.ts | 20 + src/vs/base/common/insane/insane.js | 478 +++++++++++++++++++ src/vs/base/common/insane/insane.license.txt | 20 + 6 files changed, 552 insertions(+), 1 deletion(-) create mode 100644 src/vs/base/common/insane/cgmanifest.json create mode 100644 src/vs/base/common/insane/insane.d.ts create mode 100644 src/vs/base/common/insane/insane.js create mode 100644 src/vs/base/common/insane/insane.license.txt diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index a05fff4a1bd6c..c065556dc0920 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -50,6 +50,7 @@ const indentationFilter = [ '!src/vs/css.js', '!src/vs/css.build.js', '!src/vs/loader.js', + '!src/vs/base/common/insane/insane.js', '!src/vs/base/common/marked/marked.js', '!src/vs/base/node/terminateProcess.sh', '!src/vs/base/node/cpuUsage.sh', @@ -131,6 +132,7 @@ const eslintFilter = [ '!src/vs/nls.js', '!src/vs/css.build.js', '!src/vs/nls.build.js', + '!src/**/insane.js', '!src/**/marked.js', '!**/test/**' ]; diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 493f60ef8a1b1..23a30ee93739f 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -9,6 +9,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IMarkdownString, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import * as marked from 'vs/base/common/marked/marked'; +import * as insane from 'vs/base/common/insane/insane'; import { parse } from 'vs/base/common/marshalling'; import { cloneAndChange } from 'vs/base/common/objects'; import { escape } from 'vs/base/common/strings'; @@ -171,7 +172,20 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende renderer }; - element.innerHTML = marked.parse(markdown.value, markedOptions); + const allowedSchemes = ['http', 'https', 'mailto']; + if (markdown.isTrusted) { + allowedSchemes.push('command'); + } + + const renderedMarkdown = marked.parse(markdown.value, markedOptions); + element.innerHTML = insane(renderedMarkdown, { + allowedSchemes, + allowedAttributes: { + 'a': ['href', 'name', 'target', 'data-href'], + 'iframe': ['allowfullscreen', 'frameborder', 'src'], + 'img': ['src'] + } + }); signalInnerHTML!(); return element; diff --git a/src/vs/base/common/insane/cgmanifest.json b/src/vs/base/common/insane/cgmanifest.json new file mode 100644 index 0000000000000..bb94b81708873 --- /dev/null +++ b/src/vs/base/common/insane/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "insane", + "repositoryUrl": "https://github.com/bevacqua/insane", + "commitHash": "7f5a809f44a37e7d11ee5343b2d8bca4be6ba4ae" + } + }, + "license": "MIT", + "version": "2.6.2" + } + ], + "version": 1 +} diff --git a/src/vs/base/common/insane/insane.d.ts b/src/vs/base/common/insane/insane.d.ts new file mode 100644 index 0000000000000..def347529e95f --- /dev/null +++ b/src/vs/base/common/insane/insane.d.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export as namespace insane; + +export = insane; + +declare function insane( + html: string, + options?: { + readonly allowedSchemes?: readonly string[], + readonly allowedTags?: readonly string[], + readonly allowedAttributes?: { readonly [key: string]: string[] }, + }, + strict?: boolean, +): string; + +declare namespace insane { } diff --git a/src/vs/base/common/insane/insane.js b/src/vs/base/common/insane/insane.js new file mode 100644 index 0000000000000..3cfdf5886bb01 --- /dev/null +++ b/src/vs/base/common/insane/insane.js @@ -0,0 +1,478 @@ +/* +The MIT License (MIT) + +Copyright © 2015 Nicolas Bevacqua + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// ESM-comment-begin +let __insane_exports; +// ESM-comment-end + + + +(function () { function r(e, n, t) { function o(i, f) { if (!n[i]) { if (!e[i]) { var c = "function" == typeof require && require; if (!f && c) return c(i, !0); if (u) return u(i, !0); var a = new Error("Cannot find module '" + i + "'"); throw a.code = "MODULE_NOT_FOUND", a } var p = n[i] = { exports: {} }; e[i][0].call(p.exports, function (r) { var n = e[i][1][r]; return o(n || r) }, p, p.exports, r, e, n, t) } return n[i].exports } for (var u = "function" == typeof require && require, i = 0; i < t.length; i++)o(t[i]); return o } return r })()({ + 1: [function (require, module, exports) { + 'use strict'; + + var toMap = require('./toMap'); + var uris = ['background', 'base', 'cite', 'href', 'longdesc', 'src', 'usemap']; + + module.exports = { + uris: toMap(uris) // attributes that have an href and hence need to be sanitized + }; + + }, { "./toMap": 10 }], 2: [function (require, module, exports) { + 'use strict'; + + var defaults = { + allowedAttributes: { + '*': ['title', 'accesskey'], + a: ['href', 'name', 'target', 'aria-label'], + iframe: ['allowfullscreen', 'frameborder', 'src'], + img: ['src', 'alt', 'title', 'aria-label'] + }, + allowedClasses: {}, + allowedSchemes: ['http', 'https', 'mailto'], + allowedTags: [ + 'a', 'abbr', 'article', 'b', 'blockquote', 'br', 'caption', 'code', 'del', 'details', 'div', 'em', + 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'ins', 'kbd', 'li', 'main', 'mark', + 'ol', 'p', 'pre', 'section', 'span', 'strike', 'strong', 'sub', 'summary', 'sup', 'table', + 'tbody', 'td', 'th', 'thead', 'tr', 'u', 'ul' + ], + filter: null + }; + + module.exports = defaults; + + }, {}], 3: [function (require, module, exports) { + 'use strict'; + + var toMap = require('./toMap'); + var voids = ['area', 'br', 'col', 'hr', 'img', 'wbr', 'input', 'base', 'basefont', 'link', 'meta']; + + module.exports = { + voids: toMap(voids) + }; + + }, { "./toMap": 10 }], 4: [function (require, module, exports) { + 'use strict'; + + var he = require('he'); + var assign = require('assignment'); + var parser = require('./parser'); + var sanitizer = require('./sanitizer'); + var defaults = require('./defaults'); + + function insane(html, options, strict) { + var buffer = []; + var configuration = strict === true ? options : assign({}, defaults, options); + var handler = sanitizer(buffer, configuration); + + parser(html, handler); + + return buffer.join(''); + } + + insane.defaults = defaults; + module.exports = insane; + __insane_exports = insane; + + }, { "./defaults": 2, "./parser": 7, "./sanitizer": 8, "assignment": 6, "he": 9 }], 5: [function (require, module, exports) { + 'use strict'; + + module.exports = function lowercase(string) { + return typeof string === 'string' ? string.toLowerCase() : string; + }; + + }, {}], 6: [function (require, module, exports) { + 'use strict'; + + function assignment(result) { + var stack = Array.prototype.slice.call(arguments, 1); + var item; + var key; + while (stack.length) { + item = stack.shift(); + for (key in item) { + if (item.hasOwnProperty(key)) { + if (Object.prototype.toString.call(result[key]) === '[object Object]') { + result[key] = assignment(result[key], item[key]); + } else { + result[key] = item[key]; + } + } + } + } + return result; + } + + module.exports = assignment; + + }, {}], 7: [function (require, module, exports) { + 'use strict'; + + var he = require('he'); + var lowercase = require('./lowercase'); + var attributes = require('./attributes'); + var elements = require('./elements'); + var rstart = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/; + var rend = /^<\s*\/\s*([\w:-]+)[^>]*>/; + var rattrs = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g; + var rtag = /^'); + if (index >= 0) { + if (handler.comment) { + handler.comment(html.substring(4, index)); + } + html = html.substring(index + 3); + chars = false; + } + } + + function parseTagDecode() { + if (!chars) { + return; + } + var text; + var index = html.indexOf('<'); + if (index >= 0) { + text = html.substring(0, index); + html = html.substring(index); + } else { + text = html; + html = ''; + } + if (handler.chars) { + handler.chars(text); + } + } + + function parseStartTag(tag, tagName, rest, unary) { + var attrs = {}; + var low = lowercase(tagName); + var u = elements.voids[low] || !!unary; + + rest.replace(rattrs, attrReplacer); + + if (!u) { + stack.push(low); + } + if (handler.start) { + handler.start(low, attrs, u); + } + + function attrReplacer(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) { + if (doubleQuotedValue === void 0 && singleQuotedValue === void 0 && unquotedValue === void 0) { + attrs[name] = void 0; // attribute is like + } else { + attrs[name] = he.decode(doubleQuotedValue || singleQuotedValue || unquotedValue || ''); + } + } + } + + function parseEndTag(tag, tagName) { + var i; + var pos = 0; + var low = lowercase(tagName); + if (low) { + for (pos = stack.length - 1; pos >= 0; pos--) { + if (stack[pos] === low) { + break; // find the closest opened tag of the same type + } + } + } + if (pos >= 0) { + for (i = stack.length - 1; i >= pos; i--) { + if (handler.end) { // close all the open elements, up the stack + handler.end(stack[i]); + } + } + stack.length = pos; + } + } + } + + module.exports = parser; + + }, { "./attributes": 1, "./elements": 3, "./lowercase": 5, "he": 9 }], 8: [function (require, module, exports) { + 'use strict'; + + var he = require('he'); + var lowercase = require('./lowercase'); + var attributes = require('./attributes'); + var elements = require('./elements'); + + function sanitizer(buffer, options) { + var last; + var context; + var o = options || {}; + + reset(); + + return { + start: start, + end: end, + chars: chars + }; + + function out(value) { + buffer.push(value); + } + + function start(tag, attrs, unary) { + var low = lowercase(tag); + + if (context.ignoring) { + ignore(low); return; + } + if ((o.allowedTags || []).indexOf(low) === -1) { + ignore(low); return; + } + if (o.filter && !o.filter({ tag: low, attrs: attrs })) { + ignore(low); return; + } + + out('<'); + out(low); + Object.keys(attrs).forEach(parse); + out(unary ? '/>' : '>'); + + function parse(key) { + var value = attrs[key]; + var classesOk = (o.allowedClasses || {})[low] || []; + var attrsOk = (o.allowedAttributes || {})[low] || []; + attrsOk = attrsOk.concat((o.allowedAttributes || {})['*'] || []); + var valid; + var lkey = lowercase(key); + if (lkey === 'class' && attrsOk.indexOf(lkey) === -1) { + value = value.split(' ').filter(isValidClass).join(' ').trim(); + valid = value.length; + } else { + valid = attrsOk.indexOf(lkey) !== -1 && (attributes.uris[lkey] !== true || testUrl(value)); + } + if (valid) { + out(' '); + out(key); + if (typeof value === 'string') { + out('="'); + out(he.encode(value)); + out('"'); + } + } + function isValidClass(className) { + return classesOk && classesOk.indexOf(className) !== -1; + } + } + } + + function end(tag) { + var low = lowercase(tag); + var allowed = (o.allowedTags || []).indexOf(low) !== -1; + if (allowed) { + if (context.ignoring === false) { + out(''); + } else { + unignore(low); + } + } else { + unignore(low); + } + } + + function testUrl(text) { + var start = text[0]; + if (start === '#' || start === '/') { + return true; + } + var colon = text.indexOf(':'); + if (colon === -1) { + return true; + } + var questionmark = text.indexOf('?'); + if (questionmark !== -1 && colon > questionmark) { + return true; + } + var hash = text.indexOf('#'); + if (hash !== -1 && colon > hash) { + return true; + } + return o.allowedSchemes.some(matches); + + function matches(scheme) { + return text.indexOf(scheme + ':') === 0; + } + } + + function chars(text) { + if (context.ignoring === false) { + out(o.transformText ? o.transformText(text) : text); + } + } + + function ignore(tag) { + if (elements.voids[tag]) { + return; + } + if (context.ignoring === false) { + context = { ignoring: tag, depth: 1 }; + } else if (context.ignoring === tag) { + context.depth++; + } + } + + function unignore(tag) { + if (context.ignoring === tag) { + if (--context.depth <= 0) { + reset(); + } + } + } + + function reset() { + context = { ignoring: false, depth: 0 }; + } + } + + module.exports = sanitizer; + + }, { "./attributes": 1, "./elements": 3, "./lowercase": 5, "he": 9 }], 9: [function (require, module, exports) { + 'use strict'; + + var escapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + var unescapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'" + }; + var rescaped = /(&|<|>|"|')/g; + var runescaped = /[&<>"']/g; + + function escapeHtmlChar(match) { + return escapes[match]; + } + function unescapeHtmlChar(match) { + return unescapes[match]; + } + + function escapeHtml(text) { + return text == null ? '' : String(text).replace(runescaped, escapeHtmlChar); + } + + function unescapeHtml(html) { + return html == null ? '' : String(html).replace(rescaped, unescapeHtmlChar); + } + + escapeHtml.options = unescapeHtml.options = {}; + + module.exports = { + encode: escapeHtml, + escape: escapeHtml, + decode: unescapeHtml, + unescape: unescapeHtml, + version: '1.0.0-browser' + }; + + }, {}], 10: [function (require, module, exports) { + 'use strict'; + + function toMap(list) { + return list.reduce(asKey, {}); + } + + function asKey(accumulator, item) { + accumulator[item] = true; + return accumulator; + } + + module.exports = toMap; + + }, {}] +}, {}, [4]); + +// BEGIN MONACOCHANGE +// __marked_exports = marked; +// }).call(this); + +// ESM-comment-begin +define(function() { return __insane_exports; }); +// ESM-comment-end diff --git a/src/vs/base/common/insane/insane.license.txt b/src/vs/base/common/insane/insane.license.txt new file mode 100644 index 0000000000000..b980cef0bc7cd --- /dev/null +++ b/src/vs/base/common/insane/insane.license.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright © 2015 Nicolas Bevacqua + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From bfda91410442d32601f2d760fa7e2f612df9f57f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 19 Aug 2019 19:48:50 -0700 Subject: [PATCH 372/613] Update md grammar Fixes #https://github.com/microsoft/vscode/issues/79478 --- .../syntaxes/markdown.tmLanguage.json | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index 6fb25ae6ce24f..28c275a5c3550 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/05ccfa3db6edbd357390431f9e316adb38ba41d8", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/d64818b45a51ad281d0eececa06fccb8c3767fe4", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -122,7 +122,7 @@ "contentName": "meta.embedded.block.html", "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ] } @@ -386,7 +386,7 @@ "contentName": "meta.embedded.block.php", "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" }, { "include": "source.php" @@ -1993,7 +1993,7 @@ "1": { "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ] }, @@ -2015,7 +2015,7 @@ "begin": "(\\s*|$)", "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ], "while": "(?i)^(?!.*)" @@ -2023,10 +2023,10 @@ ] }, { - "begin": "(?i)(^|\\G)\\s*(?=))", + "begin": "(?i)(^|\\G)\\s*(?=]*(\\s|$|/?>))", "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ], "while": "^(?!\\s*$)" @@ -2035,7 +2035,7 @@ "begin": "(^|\\G)\\s*(?=(<[a-zA-Z0-9\\-](/?>|\\s.*?>)|)\\s*$)", "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ], "while": "^(?!\\s*$)" @@ -2095,7 +2095,7 @@ "include": "#inline" }, { - "include": "text.html.basic" + "include": "text.html.derivative" }, { "include": "#heading-setext" @@ -2152,7 +2152,7 @@ "include": "#inline" }, { - "include": "text.html.basic" + "include": "text.html.derivative" }, { "include": "#heading-setext" @@ -2246,7 +2246,7 @@ "end": "(?<=>)", "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ] }, @@ -2391,7 +2391,7 @@ "end": "(?<=>)", "patterns": [ { - "include": "text.html.basic" + "include": "text.html.derivative" } ] }, From abb946ccaa79ba4b17f1d2974ee273797ea0f569 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 19 Aug 2019 20:01:14 -0700 Subject: [PATCH 373/613] =?UTF-8?q?Whitelist=20a=20few=20additional=20attr?= =?UTF-8?q?ibutes=20for=20img=20elements=20in=20md=C3=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/vs/base/browser/markdownRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 23a30ee93739f..5d94200e15acd 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -183,7 +183,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende allowedAttributes: { 'a': ['href', 'name', 'target', 'data-href'], 'iframe': ['allowfullscreen', 'frameborder', 'src'], - 'img': ['src'] + 'img': ['src', 'title', 'alt', 'width', 'height'] } }); signalInnerHTML!(); From 7af2ebb9fca2d021c1571d3c0983e39a1a7af028 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 19 Aug 2019 20:10:35 -0700 Subject: [PATCH 374/613] Update md grammar --- .../syntaxes/markdown.tmLanguage.json | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index 28c275a5c3550..3fe94d8cf7eb5 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/d64818b45a51ad281d0eececa06fccb8c3767fe4", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/eb3898715b50d7911377a650e383a768a3a21f25", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -122,7 +122,7 @@ "contentName": "meta.embedded.block.html", "patterns": [ { - "include": "text.html.derivative" + "include": "text.html.basic" } ] } @@ -386,7 +386,7 @@ "contentName": "meta.embedded.block.php", "patterns": [ { - "include": "text.html.derivative" + "include": "text.html.basic" }, { "include": "source.php" @@ -1855,12 +1855,12 @@ "name": "markup.fenced_code.block.markdown" }, "heading": { - "match": "(?:^|\\G)[ ]{0,3}((#{1,6})\\s*(?=[\\S[^#]]).*?\\s*(#{1,6})?)$\\n?", + "match": "(?:^|\\G)[ ]{0,3}((#{1,6})\\s+(?=[\\S[^#]]).*?\\s*(#{1,6})?)$\\n?", "captures": { "1": { "patterns": [ { - "match": "(#{6})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{6})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", "name": "heading.6.markdown", "captures": { "1": { @@ -1875,7 +1875,7 @@ } }, { - "match": "(#{5})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{5})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", "name": "heading.5.markdown", "captures": { "1": { @@ -1890,7 +1890,7 @@ } }, { - "match": "(#{4})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{4})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", "name": "heading.4.markdown", "captures": { "1": { @@ -1905,7 +1905,7 @@ } }, { - "match": "(#{3})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{3})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", "name": "heading.3.markdown", "captures": { "1": { @@ -1920,7 +1920,7 @@ } }, { - "match": "(#{2})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{2})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", "name": "heading.2.markdown", "captures": { "1": { @@ -1935,7 +1935,7 @@ } }, { - "match": "(#{1})\\s*(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", + "match": "(#{1})\\s+(?=[\\S[^#]])(.*?)\\s*(\\s+#+)?$\\n?", "name": "heading.1.markdown", "captures": { "1": { @@ -2023,7 +2023,7 @@ ] }, { - "begin": "(?i)(^|\\G)\\s*(?=]*(\\s|$|/?>))", + "begin": "(?i)(^|\\G)\\s*(?=))", "patterns": [ { "include": "text.html.derivative" From c5dc9d16be5cbd18b4a1bc44941ef8a1b16cf983 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 19 Aug 2019 20:18:26 -0700 Subject: [PATCH 375/613] Finalize asWebviewUri api Fixes #79242 As discussed, renames `toWebviewResource` to `asWebviewUri` to be consistent with the `asAbsolutePath` api naming --- .../src/features/preview.ts | 4 +-- .../src/features/previewContentProvider.ts | 16 +++++----- .../src/util/resources.ts | 4 +-- .../src/singlefolder-tests/webview.test.ts | 8 ++--- src/vs/vscode.d.ts | 24 ++++++++++++++ src/vs/vscode.proposed.d.ts | 31 ------------------- .../workbench/api/common/extHostCodeInsets.ts | 6 ++-- src/vs/workbench/api/common/extHostWebview.ts | 6 ++-- src/vs/workbench/api/common/shared/webview.ts | 2 +- .../api/extHostWebview.test.ts | 24 +++++++------- 10 files changed, 59 insertions(+), 66 deletions(-) diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index 694a4fbb13c7f..775734d1140a3 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -439,8 +439,8 @@ export class MarkdownPreview extends Disposable { if (this._resource === markdownResource) { const self = this; const resourceProvider: WebviewResourceProvider = { - toWebviewResource: (resource) => { - return this.editor.webview.toWebviewResource(normalizeResource(markdownResource, resource)); + asWebviewUri: (resource) => { + return this.editor.webview.asWebviewUri(normalizeResource(markdownResource, resource)); }, get cspSource() { return self.editor.webview.cspSource; } }; diff --git a/extensions/markdown-language-features/src/features/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts index 9b70fe3beb371..0be66d1d63043 100644 --- a/extensions/markdown-language-features/src/features/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/features/previewContentProvider.ts @@ -65,7 +65,7 @@ export class MarkdownContentProvider { scrollEditorWithPreview: config.scrollEditorWithPreview, doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor, disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings(), - webviewResourceRoot: resourceProvider.toWebviewResource(markdownDocument.uri).toString(), + webviewResourceRoot: resourceProvider.asWebviewUri(markdownDocument.uri).toString(), }; this.logger.log('provideTextDocumentContent', initialData); @@ -86,7 +86,7 @@ export class MarkdownContentProvider { data-state="${escapeAttribute(JSON.stringify(state || {}))}"> ${this.getStyles(resourceProvider, sourceUri, config, state)} - + ${body} @@ -110,7 +110,7 @@ export class MarkdownContentProvider { } private extensionResourcePath(resourceProvider: WebviewResourceProvider, mediaFile: string): string { - const webviewResource = resourceProvider.toWebviewResource( + const webviewResource = resourceProvider.asWebviewUri( vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile)))); return webviewResource.toString(); } @@ -126,17 +126,17 @@ export class MarkdownContentProvider { // Assume it must be a local file if (path.isAbsolute(href)) { - return resourceProvider.toWebviewResource(vscode.Uri.file(href)).toString(); + return resourceProvider.asWebviewUri(vscode.Uri.file(href)).toString(); } // Use a workspace relative path if there is a workspace const root = vscode.workspace.getWorkspaceFolder(resource); if (root) { - return resourceProvider.toWebviewResource(vscode.Uri.file(path.join(root.uri.fsPath, href))).toString(); + return resourceProvider.asWebviewUri(vscode.Uri.file(path.join(root.uri.fsPath, href))).toString(); } // Otherwise look relative to the markdown file - return resourceProvider.toWebviewResource(vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))).toString(); + return resourceProvider.asWebviewUri(vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))).toString(); } private computeCustomStyleSheetIncludes(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration): string { @@ -176,7 +176,7 @@ export class MarkdownContentProvider { private getStyles(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration, state?: any): string { const baseStyles: string[] = []; for (const resource of this.contributionProvider.contributions.previewStyles) { - baseStyles.push(``); + baseStyles.push(``); } return `${baseStyles.join('\n')} @@ -188,7 +188,7 @@ export class MarkdownContentProvider { const out: string[] = []; for (const resource of this.contributionProvider.contributions.previewScripts) { out.push(``); } diff --git a/extensions/markdown-language-features/src/util/resources.ts b/extensions/markdown-language-features/src/util/resources.ts index 1def7adcce005..063c410b39eea 100644 --- a/extensions/markdown-language-features/src/util/resources.ts +++ b/extensions/markdown-language-features/src/util/resources.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; export interface WebviewResourceProvider { - toWebviewResource(resource: vscode.Uri): vscode.Uri; + asWebviewUri(resource: vscode.Uri): vscode.Uri; readonly cspSource: string; } @@ -30,4 +30,4 @@ export function normalizeResource( } } return resource; -} \ No newline at end of file +} diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts index 4be0218ab6435..e785f1d4afbe1 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -251,18 +251,18 @@ suite('Webview tests', () => { }); `); - async function toWebviewResource(path: string) { - const root = await webview.webview.toWebviewResource(vscode.Uri.file(vscode.workspace.rootPath!)); + async function asWebviewUri(path: string) { + const root = await webview.webview.asWebviewUri(vscode.Uri.file(vscode.workspace.rootPath!)); return root.toString() + path; } { - const imagePath = await toWebviewResource('/image.png'); + const imagePath = await asWebviewUri('/image.png'); const response = sendRecieveMessage(webview, { src: imagePath }); assert.strictEqual((await response).value, true); } { - const imagePath = await toWebviewResource('/no-such-image.png'); + const imagePath = await asWebviewUri('/no-such-image.png'); const response = sendRecieveMessage(webview, { src: imagePath }); assert.strictEqual((await response).value, false); } diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index bdef68b2ecade..3611c2746ab5d 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5923,6 +5923,30 @@ declare module 'vscode' { * @param message Body of the message. */ postMessage(message: any): Thenable; + + /** + * Convert a uri for the local file system to one that can be used inside webviews. + * + * Webviews cannot directly load resoruces from the workspace or local file system using `file:` uris. The + * `asWebviewUri` function takes a local `file:` uri and converts it into a uri that can be used inside of + * a webview to load the same resource: + * + * ```ts + * webview.html = `` + * ``` + */ + asWebviewUri(localResource: Uri): Uri; + + /** + * Content security policy source for webview resources. + * + * 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/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 5284711d6e1a9..9dcf41062f4e5 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1141,35 +1141,4 @@ declare module 'vscode' { } //#endregion - - //#region Webview Resource Roots - - export interface Webview { - /** - * Convert a uri for the local file system to one that can be used inside webviews. - * - * Webviews cannot directly load resoruces from the workspace or local file system using `file:` uris. The - * `toWebviewResource` function takes a local `file:` uri and converts it into a uri that can be used inside of - * a webview to load the same resource: - * - * ```ts - * webview.html = `` - * ``` - */ - toWebviewResource(localResource: Uri): Uri; - - /** - * Content security policy source for webview resources. - * - * This is the origin that should be used in a content security policy rule: - * - * ``` - * img-src https: ${webview.cspSource} ...; - * ``` - */ - readonly cspSource: string; - } - - //#endregion - } diff --git a/src/vs/workbench/api/common/extHostCodeInsets.ts b/src/vs/workbench/api/common/extHostCodeInsets.ts index 066b25f2b026d..769e4de2a7a06 100644 --- a/src/vs/workbench/api/common/extHostCodeInsets.ts +++ b/src/vs/workbench/api/common/extHostCodeInsets.ts @@ -10,7 +10,7 @@ import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor'; import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors'; import * as vscode from 'vscode'; import { ExtHostEditorInsetsShape, MainThreadEditorInsetsShape } from './extHost.protocol'; -import { toWebviewResource, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; +import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; import { generateUuid } from 'vs/base/common/uuid'; export class ExtHostEditorInsets implements ExtHostEditorInsetsShape { @@ -65,8 +65,8 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape { private _html: string = ''; private _options: vscode.WebviewOptions = Object.create(null); - toWebviewResource(resource: vscode.Uri): vscode.Uri { - return toWebviewResource(that._initData, this._uuid, resource); + asWebviewUri(resource: vscode.Uri): vscode.Uri { + return asWebviewUri(that._initData, this._uuid, resource); } get cspSource(): string { diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index aaa6a49734cea..44b99bcbd4c59 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -12,7 +12,7 @@ import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShap import { Disposable } from './extHostTypes'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import * as modes from 'vs/editor/common/modes'; -import { WebviewInitData, toWebviewResource } from 'vs/workbench/api/common/shared/webview'; +import { WebviewInitData, asWebviewUri } from 'vs/workbench/api/common/shared/webview'; import { generateUuid } from 'vs/base/common/uuid'; type IconPath = URI | { light: URI, dark: URI }; @@ -35,8 +35,8 @@ export class ExtHostWebview implements vscode.Webview { this._onMessageEmitter.dispose(); } - public toWebviewResource(resource: vscode.Uri): vscode.Uri { - return toWebviewResource(this._initData, this._handle, resource); + public asWebviewUri(resource: vscode.Uri): vscode.Uri { + return asWebviewUri(this._initData, this._handle, resource); } public get cspSource(): string { diff --git a/src/vs/workbench/api/common/shared/webview.ts b/src/vs/workbench/api/common/shared/webview.ts index 3969d74dbe1bd..9740cd21a6fd1 100644 --- a/src/vs/workbench/api/common/shared/webview.ts +++ b/src/vs/workbench/api/common/shared/webview.ts @@ -11,7 +11,7 @@ export interface WebviewInitData { readonly webviewCspSource: string; } -export function toWebviewResource( +export function asWebviewUri( initData: WebviewInitData, uuid: string, resource: vscode.Uri diff --git a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts index 8a12127904123..fb75ee14a20a3 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts @@ -48,7 +48,7 @@ suite('ExtHostWebview', () => { assert.strictEqual(lastInvokedDeserializer, serializerB); }); - test('toWebviewResource for desktop vscode-resource scheme', () => { + test('asWebviewUri for desktop vscode-resource scheme', () => { const shape = createNoopMainThreadWebviews(); const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { webviewCspSource: '', @@ -57,37 +57,37 @@ suite('ExtHostWebview', () => { const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {}); assert.strictEqual( - webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html')).toString(), + webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html')).toString(), 'vscode-resource:/Users/codey/file.html', 'Unix basic' ); assert.strictEqual( - webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html#frag')).toString(), + webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html#frag')).toString(), 'vscode-resource:/Users/codey/file.html#frag', 'Unix should preserve fragment' ); assert.strictEqual( - webview.webview.toWebviewResource(URI.parse('file:///Users/codey/f%20ile.html')).toString(), + webview.webview.asWebviewUri(URI.parse('file:///Users/codey/f%20ile.html')).toString(), 'vscode-resource:/Users/codey/f%20ile.html', 'Unix with encoding' ); assert.strictEqual( - webview.webview.toWebviewResource(URI.parse('file://localhost/Users/codey/file.html')).toString(), + webview.webview.asWebviewUri(URI.parse('file://localhost/Users/codey/file.html')).toString(), 'vscode-resource://localhost/Users/codey/file.html', 'Unix should preserve authority' ); assert.strictEqual( - webview.webview.toWebviewResource(URI.parse('file:///c:/codey/file.txt')).toString(), + webview.webview.asWebviewUri(URI.parse('file:///c:/codey/file.txt')).toString(), 'vscode-resource:/c%3A/codey/file.txt', 'Windows C drive' ); }); - test('toWebviewResource for web endpoint', () => { + test('asWebviewUri for web endpoint', () => { const shape = createNoopMainThreadWebviews(); const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), { @@ -101,31 +101,31 @@ suite('ExtHostWebview', () => { } assert.strictEqual( - stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html')).toString()), + stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html')).toString()), 'webview.contoso.com/commit///Users/codey/file.html', 'Unix basic' ); assert.strictEqual( - stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html#frag')).toString()), + stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html#frag')).toString()), 'webview.contoso.com/commit///Users/codey/file.html#frag', 'Unix should preserve fragment' ); assert.strictEqual( - stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///Users/codey/f%20ile.html')).toString()), + stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/f%20ile.html')).toString()), 'webview.contoso.com/commit///Users/codey/f%20ile.html', 'Unix with encoding' ); assert.strictEqual( - stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file://localhost/Users/codey/file.html')).toString()), + stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file://localhost/Users/codey/file.html')).toString()), 'webview.contoso.com/commit//localhost/Users/codey/file.html', 'Unix should preserve authority' ); assert.strictEqual( - stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///c:/codey/file.txt')).toString()), + stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///c:/codey/file.txt')).toString()), 'webview.contoso.com/commit///c%3A/codey/file.txt', 'Windows C drive' ); From df0dd2edc279af12fdf6140bb4362231fa8dc8a3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 19 Aug 2019 21:06:47 -0700 Subject: [PATCH 376/613] Move webview into browser Fixes #79424 This file depends on dom api so it should live in browser instead of common --- src/vs/workbench/api/browser/mainThreadCodeInsets.ts | 2 +- src/vs/workbench/api/browser/mainThreadWebview.ts | 2 +- .../workbench/contrib/extensions/browser/extensionEditor.ts | 2 +- .../contrib/webview/browser/dynamicWebviewEditorOverlay.ts | 2 +- .../workbench/contrib/webview/browser/webview.contribution.ts | 2 +- .../workbench/contrib/webview/{common => browser}/webview.ts | 0 src/vs/workbench/contrib/webview/browser/webviewEditor.ts | 2 +- .../workbench/contrib/webview/browser/webviewEditorInput.ts | 2 +- .../workbench/contrib/webview/browser/webviewEditorService.ts | 2 +- src/vs/workbench/contrib/webview/browser/webviewElement.ts | 2 +- src/vs/workbench/contrib/webview/browser/webviewService.ts | 2 +- .../contrib/webview/electron-browser/webview.contribution.ts | 4 ++-- .../contrib/webview/electron-browser/webviewCommands.ts | 4 ++-- .../contrib/webview/electron-browser/webviewElement.ts | 2 +- .../contrib/webview/electron-browser/webviewService.ts | 4 ++-- 15 files changed, 17 insertions(+), 17 deletions(-) rename src/vs/workbench/contrib/webview/{common => browser}/webview.ts (100%) diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index bb42627961df1..053217cda5205 100644 --- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -8,7 +8,7 @@ import * as modes from 'vs/editor/common/modes'; import { MainContext, MainThreadEditorInsetsShape, IExtHostContext, ExtHostEditorInsetsShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from '../common/extHostCustomers'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 90bdeb5ea748a..d4beebbd5ffd6 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -22,7 +22,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { extHostNamedCustomer } from '../common/extHostCustomers'; import { IProductService } from 'vs/platform/product/common/product'; import { startsWith } from 'vs/base/common/strings'; -import { Webview } from 'vs/workbench/contrib/webview/common/webview'; +import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; interface OldMainThreadWebviewState { readonly viewType: string; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index d4a6e6f60acbc..dc9e6489cd7a7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -50,7 +50,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry'; import { isUndefined } from 'vs/base/common/types'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { generateUuid } from 'vs/base/common/uuid'; import { platform } from 'vs/base/common/process'; diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index c86d25f7724f3..94d8bfa1f36fe 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -6,7 +6,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { IWebviewService, Webview, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, Webview, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { memoize } from 'vs/base/common/decorators'; diff --git a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts index 40dac14b615dd..56017d4348f19 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts @@ -15,7 +15,7 @@ import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } fro import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory'; -import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/common/webview'; +import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview'; import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand } from '../browser/webviewCommands'; import { WebviewEditor } from '../browser/webviewEditor'; import { WebviewEditorInput } from '../browser/webviewEditorInput'; diff --git a/src/vs/workbench/contrib/webview/common/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts similarity index 100% rename from src/vs/workbench/contrib/webview/common/webview.ts rename to src/vs/workbench/contrib/webview/browser/webview.ts diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index d01dc751d00b4..b8d7c6dd48e26 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -15,7 +15,7 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorOptions } from 'vs/workbench/common/editor'; import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; -import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewEditorOverlay } from 'vs/workbench/contrib/webview/common/webview'; +import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts index 614097c5ba93b..316c112a8bd1c 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { EditorInput, EditorModel, GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor'; -import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/common/webview'; +import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { UnownedDisposable as Unowned } from 'vs/base/common/lifecycle'; class WebviewIconsManager { diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts index c17d203aed333..6fdda6cfb1cbf 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { GroupIdentifier } from 'vs/workbench/common/editor'; -import { IWebviewService, WebviewOptions, WebviewContentOptions } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, WebviewOptions, WebviewContentOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { RevivedWebviewEditorInput, WebviewEditorInput } from './webviewEditorInput'; diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 6eb2d99a7b0c3..65b09a14ddedd 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -5,7 +5,7 @@ import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; +import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; diff --git a/src/vs/workbench/contrib/webview/browser/webviewService.ts b/src/vs/workbench/contrib/webview/browser/webviewService.ts index 44ef4a15f1e99..eed1445c49211 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewService.ts @@ -5,7 +5,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; -import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { DynamicWebviewEditorOverlay } from './dynamicWebviewEditorOverlay'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; diff --git a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts index 084163f045916..627d57bd3fd24 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts @@ -13,7 +13,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; -import { IWebviewService, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview'; import * as webviewCommands from 'vs/workbench/contrib/webview/electron-browser/webviewCommands'; import { ElectronWebviewService } from 'vs/workbench/contrib/webview/electron-browser/webviewService'; @@ -89,4 +89,4 @@ function registerWebViewCommands(editorId: string): void { } } -registerWebViewCommands(WebviewEditor.ID); \ No newline at end of file +registerWebViewCommands(WebviewEditor.ID); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts index a1d8ecda01a83..066dbbecf5e36 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts @@ -9,7 +9,7 @@ import { Command, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; -import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/common/webview'; +import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; export class OpenWebviewDeveloperToolsAction extends Action { static readonly ID = 'workbench.action.webview.openDeveloperTools'; @@ -101,4 +101,4 @@ function withActiveWebviewBasedWebview(accessor: ServicesAccessor, f: (webview: } }); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 4002461cae284..68ad466ee19ed 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -20,7 +20,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing'; -import { Webview, WebviewContentOptions, WebviewOptions, WebviewResourceScheme } from 'vs/workbench/contrib/webview/common/webview'; +import { Webview, WebviewContentOptions, WebviewOptions, WebviewResourceScheme } from 'vs/workbench/contrib/webview/browser/webview'; import { registerFileProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols'; import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService'; import { WebviewFindWidget } from '../browser/webviewFindWidget'; diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts index ae8eb72d06fd3..dedb445ab5c0a 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts @@ -7,7 +7,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DynamicWebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay'; import { IFrameWebview } from 'vs/workbench/contrib/webview/browser/webviewElement'; -import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; +import { IWebviewService, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { ElectronWebviewBasedWebview } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; export class ElectronWebviewService implements IWebviewService { @@ -38,4 +38,4 @@ export class ElectronWebviewService implements IWebviewService { ): WebviewEditorOverlay { return this._instantiationService.createInstance(DynamicWebviewEditorOverlay, id, options, contentOptions); } -} \ No newline at end of file +} From d5026ed3fbf84acec67bb8be933060bedd46a97e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Aug 2019 11:01:07 +0200 Subject: [PATCH 377/613] fix #79453 --- .../editor/browser/services/openerService.ts | 19 ++++---- src/vs/platform/opener/common/opener.ts | 10 +--- .../url/electron-browser/urlService.ts | 10 ++-- .../workbench/api/browser/mainThreadWindow.ts | 2 +- .../files/browser/editors/binaryFileEditor.ts | 2 +- src/vs/workbench/electron-browser/window.ts | 45 ++++++++++++++---- .../services/files/common/workspaceWatcher.ts | 4 +- .../integrity/node/integrityService.ts | 2 +- .../opener/electron-browser/openerService.ts | 46 ------------------- src/vs/workbench/workbench.common.main.ts | 3 ++ src/vs/workbench/workbench.desktop.main.ts | 1 - src/vs/workbench/workbench.web.main.ts | 3 -- 12 files changed, 63 insertions(+), 84 deletions(-) delete mode 100644 src/vs/workbench/services/opener/electron-browser/openerService.ts diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index c92e5a70c7665..7632af1b45cab 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -19,10 +19,11 @@ import { localize } from 'vs/nls'; import { IProductService } from 'vs/platform/product/common/product'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import Severity from 'vs/base/common/severity'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; export class OpenerService implements IOpenerService { - _serviceBrand: any; + _serviceBrand!: ServiceIdentifier; private readonly _opener = new LinkedList(); @@ -41,7 +42,7 @@ export class OpenerService implements IOpenerService { return { dispose: remove }; } - async open(resource: URI, options?: { openToSide?: boolean }): Promise { + async open(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise { // no scheme ?!? if (!resource.scheme) { return Promise.resolve(false); @@ -57,13 +58,13 @@ export class OpenerService implements IOpenerService { return this._doOpen(resource, options); } - private _doOpen(resource: URI, options?: { openToSide?: boolean }): Promise { + private _doOpen(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise { const { scheme, authority, path, query, fragment } = resource; - if (equalsIgnoreCase(scheme, Schemas.mailto)) { + if (equalsIgnoreCase(scheme, Schemas.mailto) || (options && options.openExternal)) { // open default mail application - return this.openExternal(resource); + return this._doOpenExternal(resource); } if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) { @@ -78,7 +79,7 @@ export class OpenerService implements IOpenerService { const domainToOpen = `${scheme}://${authority}`; if (isDomainTrusted(domainToOpen, trustedDomains)) { - return this.openExternal(resource); + return this._doOpenExternal(resource); } else { return this._dialogService.show( Severity.Info, @@ -97,11 +98,11 @@ export class OpenerService implements IOpenerService { cancelId: 1 }).then((choice) => { if (choice === 0) { - return this.openExternal(resource); + return this._doOpenExternal(resource); } else if (choice === 2) { return this._commandService.executeCommand('workbench.action.configureTrustedDomains', domainToOpen).then((pickedDomains: string[]) => { if (pickedDomains.indexOf(domainToOpen) !== -1) { - return this.openExternal(resource); + return this._doOpenExternal(resource); } return Promise.resolve(false); }); @@ -152,7 +153,7 @@ export class OpenerService implements IOpenerService { } } - openExternal(resource: URI): Promise { + private _doOpenExternal(resource: URI): Promise { dom.windowOpenNoOpener(encodeURI(resource.toString(true))); return Promise.resolve(true); diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index c8336fc712cf7..17cea7f5b3abe 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -11,6 +11,7 @@ export const IOpenerService = createDecorator('openerService'); export interface IOpener { open(resource: URI, options?: { openToSide?: boolean }): Promise; + open(resource: URI, options?: { openExternal?: boolean }): Promise; } export interface IOpenerService { @@ -29,18 +30,11 @@ export interface IOpenerService { * @return A promise that resolves when the opening is done. */ open(resource: URI, options?: { openToSide?: boolean }): Promise; - - /** - * Opens a URL externally. - * - * @param url A resource to open externally. - */ - openExternal(resource: URI): Promise; + open(resource: URI, options?: { openExternal?: boolean }): Promise; } export const NullOpenerService: IOpenerService = Object.freeze({ _serviceBrand: undefined, registerOpener() { return { dispose() { } }; }, open() { return Promise.resolve(false); }, - openExternal() { return Promise.resolve(false); } }); diff --git a/src/vs/platform/url/electron-browser/urlService.ts b/src/vs/platform/url/electron-browser/urlService.ts index 06b5d7c31f085..1b1c88721b75e 100644 --- a/src/vs/platform/url/electron-browser/urlService.ts +++ b/src/vs/platform/url/electron-browser/urlService.ts @@ -27,12 +27,16 @@ export class RelayURLService extends URLService implements IURLHandler { openerService.registerOpener(this); } - async open(uri: URI): Promise { - if (uri.scheme !== product.urlProtocol) { + async open(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise { + if (options && options.openExternal) { return false; } - return await this.urlService.open(uri); + if (resource.scheme !== product.urlProtocol) { + return false; + } + + return await this.urlService.open(resource); } handleURL(uri: URI): Promise { diff --git a/src/vs/workbench/api/browser/mainThreadWindow.ts b/src/vs/workbench/api/browser/mainThreadWindow.ts index acbb5c6f30688..df059758f65e9 100644 --- a/src/vs/workbench/api/browser/mainThreadWindow.ts +++ b/src/vs/workbench/api/browser/mainThreadWindow.ts @@ -59,7 +59,7 @@ export class MainThreadWindow implements MainThreadWindowShape { } } - return this.openerService.openExternal(uri); + return this.openerService.open(uri, { openExternal: true }); } private getOrCreateTunnel(remotePort: number): Promise | undefined { diff --git a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts index d6ce1130e866e..9c0808d9ad1ae 100644 --- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts @@ -38,7 +38,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { BinaryFileEditor.ID, { openInternal: (input, options) => this.openInternal(input, options), - openExternal: resource => this.openerService.openExternal(resource) + openExternal: resource => this.openerService.open(resource, { openExternal: true }) }, telemetryService, themeService, diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 57dd6170e9a0d..a2ee3629a4683 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -54,6 +54,8 @@ import { IPreferencesService } from '../services/preferences/common/preferences' import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IMenubarService, IMenubarData, IMenubarMenu, IMenubarKeybinding, IMenubarMenuItemSubmenu, IMenubarMenuItemAction, MenubarMenuItem } from 'vs/platform/menubar/node/menubar'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { Schemas } from 'vs/base/common/network'; const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))), @@ -101,7 +103,8 @@ export class ElectronWindow extends Disposable { @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @ITextFileService private readonly textFileService: ITextFileService, - @IInstantiationService private readonly instantiationService: IInstantiationService + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IOpenerService private readonly openerService: IOpenerService ) { super(); @@ -312,13 +315,8 @@ export class ElectronWindow extends Disposable { this._register(this.instantiationService.createInstance(NativeMenubarControl)); } - // Handle window.open() calls - const $this = this; - window.open = function (url: string, target: string, features: string, replace: boolean): Window | null { - $this.windowsService.openExternal(url); - - return null; - }; + // Handle open calls + this.setupOpenHandlers(); // Emit event when vscode is ready this.lifecycleService.when(LifecyclePhase.Ready).then(() => ipc.send('vscode:workbenchReady', this.windowService.windowId)); @@ -356,6 +354,35 @@ export class ElectronWindow extends Disposable { } } + private setupOpenHandlers(): void { + + // Handle window.open() calls + const $this = this; + window.open = function (url: string, target: string, features: string, replace: boolean): Window | null { + $this.windowsService.openExternal(url); + + return null; + }; + + // Handle external open calls + this.openerService.registerOpener({ + async open(resource: URI, options?: { openToSide?: boolean; openExternal?: boolean; } | undefined): Promise { + if (!options || !options.openExternal) { + return false; // only override behaviour for external open() + } + + const success = await $this.windowsService.openExternal(encodeURI(resource.toString(true))); + if (!success && resource.scheme === Schemas.file) { + await $this.windowsService.showItemInFolder(resource); + + return true; + } + + return success; + } + }); + } + private updateTouchbarMenu(): void { if (!isMacintosh) { return; // macOS only @@ -741,4 +768,4 @@ class NativeMenubarControl extends MenubarControl { return undefined; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/files/common/workspaceWatcher.ts b/src/vs/workbench/services/files/common/workspaceWatcher.ts index 1f9049858d3a7..da8c120643af5 100644 --- a/src/vs/workbench/services/files/common/workspaceWatcher.ts +++ b/src/vs/workbench/services/files/common/workspaceWatcher.ts @@ -79,7 +79,7 @@ export class WorkspaceWatcher extends Disposable { localize('netVersionError', "The Microsoft .NET Framework 4.5 is required. Please follow the link to install it."), [{ label: localize('installNet', "Download .NET Framework 4.5"), - run: () => this.openerService.openExternal(URI.parse('https://go.microsoft.com/fwlink/?LinkId=786533')) + run: () => this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?LinkId=786533')) }], { sticky: true, @@ -95,7 +95,7 @@ export class WorkspaceWatcher extends Disposable { localize('enospcError', "Unable to watch for file changes in this large workspace. Please follow the instructions link to resolve this issue."), [{ label: localize('learnMore', "Instructions"), - run: () => this.openerService.openExternal(URI.parse('https://go.microsoft.com/fwlink/?linkid=867693')) + run: () => this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?linkid=867693')) }], { sticky: true, diff --git a/src/vs/workbench/services/integrity/node/integrityService.ts b/src/vs/workbench/services/integrity/node/integrityService.ts index 81f5317fe99fa..36c80d8c1485f 100644 --- a/src/vs/workbench/services/integrity/node/integrityService.ts +++ b/src/vs/workbench/services/integrity/node/integrityService.ts @@ -93,7 +93,7 @@ export class IntegrityServiceImpl implements IIntegrityService { [ { label: nls.localize('integrity.moreInformation', "More Information"), - run: () => this.openerService.openExternal(URI.parse(product.checksumFailMoreInfoUrl)) + run: () => this.openerService.open(URI.parse(product.checksumFailMoreInfoUrl)) }, { label: nls.localize('integrity.dontShowAgain', "Don't Show Again"), diff --git a/src/vs/workbench/services/opener/electron-browser/openerService.ts b/src/vs/workbench/services/opener/electron-browser/openerService.ts deleted file mode 100644 index d6cc8bb5bec10..0000000000000 --- a/src/vs/workbench/services/opener/electron-browser/openerService.ts +++ /dev/null @@ -1,46 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { OpenerService as BaseOpenerService } from 'vs/editor/browser/services/openerService'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { Schemas } from 'vs/base/common/network'; -import { URI } from 'vs/base/common/uri'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IProductService } from 'vs/platform/product/common/product'; -import { IStorageService } from 'vs/platform/storage/common/storage'; - -export class OpenerService extends BaseOpenerService { - - _serviceBrand!: ServiceIdentifier; - - constructor( - @ICodeEditorService codeEditorService: ICodeEditorService, - @ICommandService commandService: ICommandService, - @IWindowsService private readonly windowsService: IWindowsService, - @IStorageService readonly storageService: IStorageService, - @IDialogService readonly dialogService: IDialogService, - @IProductService readonly productService: IProductService - ) { - super(codeEditorService, commandService, storageService, dialogService, productService); - } - - async openExternal(resource: URI): Promise { - const success = this.windowsService.openExternal(encodeURI(resource.toString(true))); - if (!success && resource.scheme === Schemas.file) { - await this.windowsService.showItemInFolder(resource); - - return true; - } - - return success; - } -} - -registerSingleton(IOpenerService, OpenerService, true); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index e58f7058329e6..198e68e65bb2e 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -96,6 +96,8 @@ import { IMenuService } from 'vs/platform/actions/common/actions'; import { MenuService } from 'vs/platform/actions/common/menuService'; import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadService } from 'vs/platform/download/common/downloadService'; +import { OpenerService } from 'vs/editor/browser/services/openerService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true); registerSingleton(IContextViewService, ContextViewService, true); @@ -108,6 +110,7 @@ registerSingleton(IModelService, ModelServiceImpl, true); registerSingleton(ITextResourceConfigurationService, TextResourceConfigurationService); registerSingleton(IMenuService, MenuService, true); registerSingleton(IDownloadService, DownloadService, true); +registerSingleton(IOpenerService, OpenerService, true); //#endregion diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 7aa1334e1f4ee..b44e341f9c1c6 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -48,7 +48,6 @@ import 'vs/workbench/services/extensionManagement/node/extensionManagementServic import 'vs/workbench/services/accessibility/node/accessibilityService'; import 'vs/workbench/services/remote/node/tunnelService'; import 'vs/workbench/services/backup/node/backupFileService'; -import 'vs/workbench/services/opener/electron-browser/openerService'; import 'vs/workbench/services/credentials/node/credentialsService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 40384b249b094..20db7d0737836 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -56,8 +56,6 @@ import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuS import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; import { ExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; -import { OpenerService } from 'vs/editor/browser/services/openerService'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; registerSingleton(IRequestService, RequestService, true); registerSingleton(IExtensionManagementService, ExtensionManagementService); @@ -67,7 +65,6 @@ registerSingleton(IClipboardService, BrowserClipboardService, true); registerSingleton(IAccessibilityService, BrowserAccessibilityService, true); registerSingleton(ILifecycleService, BrowserLifecycleService); registerSingleton(IContextMenuService, ContextMenuService); -registerSingleton(IOpenerService, OpenerService, true); //#endregion From 711faedf570d735f32b580a1ad3adf5ff37318d9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 20 Aug 2019 11:17:59 +0200 Subject: [PATCH 378/613] allow https: as 'script-src' --- src/vs/code/browser/workbench/workbench.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 9b8e42d3a3bd5..45a9d6d13fe12 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -10,7 +10,7 @@ + content="default-src 'none'; img-src 'self' https: data: blob: vscode-remote:; media-src 'none'; frame-src 'self' {{WEBVIEW_ENDPOINT}} https://*.vscode-webview-test.com; script-src 'self' https://az416426.vo.msecnd.net 'unsafe-eval' https:; style-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss: https:; font-src 'self' blob: vscode-remote:;"> From 0f338d93b5b384bb575d195c1b3119eadaf8e170 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 14 Aug 2019 12:02:07 +0200 Subject: [PATCH 379/613] node-debug@1.38.3 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 27467db5743ba..664ef4bba1823 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,7 +1,7 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.38.2", + "version": "1.38.3", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", From 6aa2a756b04ffbffd3632c0896cb37fbf9875d49 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Tue, 20 Aug 2019 11:21:41 +0200 Subject: [PATCH 380/613] use latest DAP --- .../contrib/debug/common/debugProtocol.d.ts | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts b/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts index 39fbff2964f99..8854146a48484 100644 --- a/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts +++ b/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts @@ -168,7 +168,7 @@ declare module DebugProtocol { category?: string; /** The output to report. */ output: string; - /** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. */ + /** If an attribute 'variablesReference' exists and its value is > 0, the output contains objects which can be retrieved by passing 'variablesReference' to the 'variables' request. The value should be less than or equal to 2147483647 (2^31 - 1). */ variablesReference?: number; /** An optional source location where the output was produced. */ source?: Source; @@ -284,9 +284,9 @@ declare module DebugProtocol { /** Response to 'runInTerminal' request. */ export interface RunInTerminalResponse extends Response { body: { - /** The process ID. */ + /** The process ID. The value should be less than or equal to 2147483647 (2^31 - 1). */ processId?: number; - /** The process ID of the terminal shell. */ + /** The process ID of the terminal shell. The value should be less than or equal to 2147483647 (2^31 - 1). */ shellProcessId?: number; }; } @@ -757,7 +757,7 @@ declare module DebugProtocol { } /** Pause request; value of command field is 'pause'. - The request suspenses the debuggee. + The request suspends the debuggee. The debug adapter first sends the response and then a 'stopped' event (with reason 'pause') after the thread has been paused successfully. */ export interface PauseRequest extends Request { @@ -842,7 +842,7 @@ declare module DebugProtocol { export interface VariablesArguments { /** The Variable reference. */ variablesReference: number; - /** Optional filter to limit the child variables to either named or indexed. If ommited, both types are fetched. */ + /** Optional filter to limit the child variables to either named or indexed. If omitted, both types are fetched. */ filter?: 'indexed' | 'named'; /** The index of the first variable to return; if omitted children start at 0. */ start?: number; @@ -887,14 +887,14 @@ declare module DebugProtocol { value: string; /** The type of the new value. Typically shown in the UI when hovering over the value. */ type?: string; - /** If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */ + /** If variablesReference is > 0, the new value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */ variablesReference?: number; /** The number of named child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). */ namedVariables?: number; /** The number of indexed child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). */ indexedVariables?: number; }; @@ -1041,14 +1041,14 @@ declare module DebugProtocol { type?: string; /** Properties of a evaluate result that can be used to determine how to render the result in the UI. */ presentationHint?: VariablePresentationHint; - /** If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */ + /** If variablesReference is > 0, the evaluate result is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */ variablesReference: number; /** The number of named child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). */ namedVariables?: number; /** The number of indexed child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). */ indexedVariables?: number; /** Memory reference to a location appropriate for this result. For pointer type eval results, this is generally a reference to the memory address contained in the pointer. */ @@ -1086,14 +1086,14 @@ declare module DebugProtocol { type?: string; /** Properties of a value that can be used to determine how to render the result in the UI. */ presentationHint?: VariablePresentationHint; - /** If variablesReference is > 0, the value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. */ + /** If variablesReference is > 0, the value is structured and its children can be retrieved by passing variablesReference to the VariablesRequest. The value should be less than or equal to 2147483647 (2^31 - 1). */ variablesReference?: number; /** The number of named child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). */ namedVariables?: number; /** The number of indexed child variables. - The client can use this optional information to present the variables in a paged UI and fetch them in chunks. + The client can use this optional information to present the variables in a paged UI and fetch them in chunks. The value should be less than or equal to 2147483647 (2^31 - 1). */ indexedVariables?: number; }; @@ -1294,6 +1294,8 @@ declare module DebugProtocol { supportsStepInTargetsRequest?: boolean; /** The debug adapter supports the 'completions' request. */ supportsCompletionsRequest?: boolean; + /** The set of characters that should trigger completion in a REPL. If not specified, the UI should assume the '.' character. */ + completionTriggerCharacters?: string[]; /** The debug adapter supports the 'modules' request. */ supportsModulesRequest?: boolean; /** The set of additional module information exposed by the debug adapter. */ @@ -1433,7 +1435,7 @@ declare module DebugProtocol { name?: string; /** The path of the source to be shown in the UI. It is only used to locate and load the content of the source if no sourceReference is specified (or its value is 0). */ path?: string; - /** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. */ + /** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. The value should be less than or equal to 2147483647 (2^31 - 1). */ sourceReference?: number; /** An optional hint for how to present the source in the UI. A value of 'deemphasize' can be used to indicate that the source is not available or that it is skipped on stepping. */ presentationHint?: 'normal' | 'emphasize' | 'deemphasize'; @@ -1668,6 +1670,8 @@ declare module DebugProtocol { label: string; /** If text is not falsy then it is inserted instead of the label. */ text?: string; + /** A string that should be used when comparing this item with other items. When `falsy` the label is used. */ + sortText?: string; /** The item's type. Typically the client uses this information to render the item in the UI with an icon. */ type?: CompletionItemType; /** This value determines the location (in the CompletionsRequest's 'text' attribute) where the completion text is added. @@ -1729,7 +1733,7 @@ declare module DebugProtocol { /** This enumeration defines all possible conditions when a thrown exception should result in a break. never: never breaks, always: always breaks, - unhandled: breaks when excpetion unhandled, + unhandled: breaks when exception unhandled, userUnhandled: breaks if the exception is not handled by user code. */ export type ExceptionBreakMode = 'never' | 'always' | 'unhandled' | 'userUnhandled'; @@ -1766,7 +1770,7 @@ declare module DebugProtocol { instructionBytes?: string; /** Text representing the instruction and its operands, in an implementation-defined format. */ instruction: string; - /** Name of the symbol that correponds with the location of this instruction, if any. */ + /** Name of the symbol that corresponds with the location of this instruction, if any. */ symbol?: string; /** Source location that corresponds to this instruction, if any. Should always be set (if available) on the first instruction returned, but can be omitted afterwards if this instruction maps to the same source file as the previous instruction. */ location?: Source; From b0d58e4fe6327250ef912a2ff8ea7e07251a54c5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Aug 2019 11:28:55 +0200 Subject: [PATCH 381/613] debt - prefer opener service over window.open(). Block window.open() --- .../contrib/debug/browser/debugSession.ts | 6 ++- .../contrib/debug/browser/rawDebugSession.ts | 6 ++- .../debug/test/browser/debugModel.test.ts | 5 +- .../experiments/browser/experimentalPrompt.ts | 7 ++- .../extensions/browser/extensionEditor.ts | 11 +++-- .../electron-browser/extensionsSlowActions.ts | 7 ++- .../runtimeExtensionsEditor.ts | 13 +++-- .../contrib/feedback/browser/feedback.ts | 8 +-- .../feedback/browser/feedbackStatusbarItem.ts | 6 ++- .../electron-browser/startupProfiler.ts | 4 +- .../contrib/search/browser/searchView.ts | 4 +- .../languageSurveys.contribution.ts | 12 +++-- .../electron-browser/nps.contribution.ts | 9 ++-- .../tasks/browser/abstractTaskService.ts | 2 +- .../electron-browser/gettingStarted.ts | 7 ++- .../electron-browser/actions/helpActions.ts | 49 ++++++++++++------- src/vs/workbench/electron-browser/window.ts | 28 ++++++----- 17 files changed, 117 insertions(+), 67 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index b0f361a487216..24b9b18c788ab 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -31,6 +31,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel'; import { onUnexpectedError } from 'vs/base/common/errors'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; export class DebugSession implements IDebugSession { @@ -66,7 +67,8 @@ export class DebugSession implements IDebugSession { @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @INotificationService private readonly notificationService: INotificationService, @IProductService private readonly productService: IProductService, - @IWindowsService private readonly windowsService: IWindowsService + @IWindowsService private readonly windowsService: IWindowsService, + @IOpenerService private readonly openerService: IOpenerService ) { this.id = generateUuid(); this.repl = new ReplModel(this); @@ -167,7 +169,7 @@ export class DebugSession implements IDebugSession { return dbgr.createDebugAdapter(this).then(debugAdapter => { - this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.windowsService); + this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.windowsService, this.openerService); return this.raw.start().then(() => { diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index fe5e98b83dd8a..d8ad2212ace83 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -17,6 +17,7 @@ import { IWindowsService } from 'vs/platform/windows/common/windows'; import { URI } from 'vs/base/common/uri'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { env as processEnv } from 'vs/base/common/process'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; /** * This interface represents a single command line argument split into a "prefix" and a "path" half. @@ -74,7 +75,8 @@ export class RawDebugSession { dbgr: IDebugger, private readonly telemetryService: ITelemetryService, public readonly customTelemetryService: ITelemetryService | undefined, - private readonly windowsService: IWindowsService + private readonly windowsService: IWindowsService, + private readonly openerService: IOpenerService ) { this.debugAdapter = debugAdapter; @@ -652,7 +654,7 @@ export class RawDebugSession { const label = error.urlLabel ? error.urlLabel : nls.localize('moreInfo', "More Info"); return createErrorWithActions(userMessage, { actions: [new Action('debug.moreInfo', label, undefined, true, () => { - window.open(error.url); + this.openerService.open(URI.parse(error.url)); return Promise.resolve(null); })] }); diff --git a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts index d6ebbfd721be2..d967dce1868f0 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugModel.test.ts @@ -13,9 +13,10 @@ import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession'; import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel'; import { IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug'; +import { NullOpenerService } from 'vs/platform/opener/common/opener'; function createMockSession(model: DebugModel, name = 'mockSession', parentSession?: DebugSession | undefined): DebugSession { - return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, parentSession, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!); + return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, parentSession, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService); } suite('Debug - Model', () => { @@ -427,7 +428,7 @@ suite('Debug - Model', () => { // Repl output test('repl output', () => { - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService); const repl = new ReplModel(session); repl.appendToRepl('first line\n', severity.Error); repl.appendToRepl('second line ', severity.Error); diff --git a/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts b/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts index 97f55700a404a..b8ded4b910a56 100644 --- a/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts +++ b/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts @@ -11,6 +11,8 @@ import { IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/exten import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Disposable } from 'vs/base/common/lifecycle'; import { language } from 'vs/base/common/platform'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; export class ExperimentalPrompts extends Disposable implements IWorkbenchContribution { @@ -18,7 +20,8 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib @IExperimentService private readonly experimentService: IExperimentService, @IViewletService private readonly viewletService: IViewletService, @INotificationService private readonly notificationService: INotificationService, - @ITelemetryService private readonly telemetryService: ITelemetryService + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IOpenerService private readonly openerService: IOpenerService ) { super(); @@ -65,7 +68,7 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib run: () => { logTelemetry(commandText); if (command.externalLink) { - window.open(command.externalLink); + this.openerService.open(URI.parse(command.externalLink)); } else if (command.curatedExtensionsKey && Array.isArray(command.curatedExtensionsList)) { this.viewletService.openViewlet('workbench.view.extensions', true) .then(viewlet => viewlet as IExtensionsViewlet) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index dc9e6489cd7a7..272fa9d0591b4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -54,6 +54,7 @@ import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/browser/w import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { generateUuid } from 'vs/base/common/uuid'; import { platform } from 'vs/base/common/process'; +import { URI } from 'vs/base/common/uri'; function removeEmbeddedSVGs(documentContent: string): string { const newDocument = new DOMParser().parseFromString(documentContent, 'text/html'); @@ -185,7 +186,7 @@ export class ExtensionEditor extends BaseEditor { @IStorageService storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, - @IWebviewService private readonly webviewService: IWebviewService, + @IWebviewService private readonly webviewService: IWebviewService ) { super(ExtensionEditor.ID, telemetryService, themeService, storageService); this.extensionReadme = null; @@ -354,8 +355,8 @@ export class ExtensionEditor extends BaseEditor { toggleClass(template.publisher, 'clickable', !!extension.url); toggleClass(template.rating, 'clickable', !!extension.url); if (extension.url) { - this.transientDisposables.add(this.onClick(template.name, () => window.open(extension.url))); - this.transientDisposables.add(this.onClick(template.rating, () => window.open(`${extension.url}#review-details`))); + this.transientDisposables.add(this.onClick(template.name, () => this.openerService.open(URI.parse(extension.url!)))); + this.transientDisposables.add(this.onClick(template.rating, () => this.openerService.open(URI.parse(`${extension.url}#review-details`)))); this.transientDisposables.add(this.onClick(template.publisher, () => { this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) @@ -363,7 +364,7 @@ export class ExtensionEditor extends BaseEditor { })); if (extension.licenseUrl) { - this.transientDisposables.add(this.onClick(template.license, () => window.open(extension.licenseUrl))); + this.transientDisposables.add(this.onClick(template.license, () => this.openerService.open(URI.parse(extension.licenseUrl!)))); template.license.style.display = 'initial'; } else { template.license.style.display = 'none'; @@ -373,7 +374,7 @@ export class ExtensionEditor extends BaseEditor { } if (extension.repository) { - this.transientDisposables.add(this.onClick(template.repository, () => window.open(extension.repository))); + this.transientDisposables.add(this.onClick(template.repository, () => this.openerService.open(URI.parse(extension.repository!)))); template.repository.style.display = 'initial'; } else { diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts index 11aacd0695543..e7e9842b027fa 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts @@ -17,6 +17,7 @@ import { join } from 'vs/base/common/path'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; abstract class RepoInfo { abstract get base(): string; @@ -117,6 +118,7 @@ class ReportExtensionSlowAction extends Action { readonly repoInfo: RepoInfo, readonly profile: IExtensionHostProfile, @IDialogService private readonly _dialogService: IDialogService, + @IOpenerService private readonly _openerService: IOpenerService ) { super('report.slow', localize('cmd.report', "Report Issue")); } @@ -140,7 +142,7 @@ class ReportExtensionSlowAction extends Action { - VSCode version: \`${pkg.version}\`\n\n${message}`); const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues/new/?body=${body}&title=${title}`; - window.open(url); + this._openerService.open(URI.parse(url)); this._dialogService.show( Severity.Info, @@ -158,6 +160,7 @@ class ShowExtensionSlowAction extends Action { readonly repoInfo: RepoInfo, readonly profile: IExtensionHostProfile, @IDialogService private readonly _dialogService: IDialogService, + @IOpenerService private readonly _openerService: IOpenerService ) { super('show.slow', localize('cmd.show', "Show Issues")); } @@ -172,7 +175,7 @@ class ShowExtensionSlowAction extends Action { // show issues const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues?utf8=✓&q=is%3Aissue+state%3Aopen+%22Extension+causes+high+cpu+load%22`; - window.open(url); + this._openerService.open(URI.parse(url)); this._dialogService.show( Severity.Info, diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 1cc01b85802ea..b6ac536b0898d 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -44,6 +44,8 @@ import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/pl import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { SlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; export const IExtensionHostProfileService = createDecorator('extensionHostProfileService'); export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey('profileSessionState', 'none'); @@ -120,7 +122,8 @@ export class RuntimeExtensionsEditor extends BaseEditor { @IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService, @IStorageService storageService: IStorageService, @ILabelService private readonly _labelService: ILabelService, - @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + @IOpenerService private readonly _openerService: IOpenerService ) { super(RuntimeExtensionsEditor.ID, telemetryService, themeService, storageService); @@ -312,7 +315,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { data.actionbar.push(this._instantiationService.createInstance(SlowExtensionAction, element.description, element.unresponsiveProfile), { icon: true, label: true }); } if (isNonEmptyArray(element.status.runtimeErrors)) { - data.actionbar.push(new ReportExtensionIssueAction(element), { icon: true, label: true }); + data.actionbar.push(new ReportExtensionIssueAction(element, this._openerService), { icon: true, label: true }); } let title: string; @@ -416,7 +419,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { const actions: IAction[] = []; - actions.push(new ReportExtensionIssueAction(e.element)); + actions.push(new ReportExtensionIssueAction(e.element, this._openerService)); actions.push(new Separator()); if (e.element.marketplaceInfo) { @@ -480,7 +483,7 @@ export class ReportExtensionIssueAction extends Action { marketplaceInfo: IExtension; status?: IExtensionsStatus; unresponsiveProfile?: IExtensionHostProfile - }) { + }, @IOpenerService private readonly openerService: IOpenerService) { super(ReportExtensionIssueAction._id, ReportExtensionIssueAction._label, 'extension-action report-issue'); this.enabled = extension.marketplaceInfo && extension.marketplaceInfo.type === ExtensionType.User @@ -490,7 +493,7 @@ export class ReportExtensionIssueAction extends Action { } async run(): Promise { - window.open(this._url); + this.openerService.open(URI.parse(this._url)); } private static _generateNewIssueUrl(extension: { diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.ts b/src/vs/workbench/contrib/feedback/browser/feedback.ts index 618dc7edaa9d5..ef308647b8df1 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.ts @@ -20,6 +20,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { IProductService } from 'vs/platform/product/common/product'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; export interface IFeedback { feedback: string; @@ -27,7 +28,7 @@ export interface IFeedback { } export interface IFeedbackDelegate { - submitFeedback(feedback: IFeedback): void; + submitFeedback(feedback: IFeedback, openerService: IOpenerService): void; getCharacterLimit(sentiment: number): number; } @@ -66,7 +67,8 @@ export class FeedbackDropdown extends Dropdown { @IIntegrityService private readonly integrityService: IIntegrityService, @IThemeService private readonly themeService: IThemeService, @IStatusbarService private readonly statusbarService: IStatusbarService, - @IProductService productService: IProductService + @IProductService productService: IProductService, + @IOpenerService private readonly openerService: IOpenerService ) { super(container, options); @@ -415,7 +417,7 @@ export class FeedbackDropdown extends Dropdown { this.feedbackDelegate.submitFeedback({ feedback: this.feedbackDescriptionInput.value, sentiment: this.sentiment - }); + }, this.openerService); this.hide(); } diff --git a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts index c609aa4132fa3..9eda590df6fcb 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts @@ -12,6 +12,8 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStatusbarService, StatusbarAlignment, IStatusbarEntry, IStatusbarEntryAccessor } from 'vs/platform/statusbar/common/statusbar'; import { localize } from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; class TwitterFeedbackService implements IFeedbackDelegate { @@ -23,11 +25,11 @@ class TwitterFeedbackService implements IFeedbackDelegate { return TwitterFeedbackService.HASHTAGS.join(','); } - submitFeedback(feedback: IFeedback): void { + submitFeedback(feedback: IFeedback, openerService: IOpenerService): void { const queryString = `?${feedback.sentiment === 1 ? `hashtags=${this.combineHashTagsAsString()}&` : null}ref_src=twsrc%5Etfw&related=twitterapi%2Ctwitter&text=${encodeURIComponent(feedback.feedback)}&tw_p=tweetbutton&via=${TwitterFeedbackService.VIA_NAME}`; const url = TwitterFeedbackService.TWITTER_URL + queryString; - window.open(url); + openerService.open(URI.parse(url)); } getCharacterLimit(sentiment: number): number { diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts index ecfdd8e2ea0cf..9c8fe678aede6 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts @@ -17,6 +17,7 @@ import { PerfviewInput } from 'vs/workbench/contrib/performance/electron-browser import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { URI } from 'vs/base/common/uri'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; export class StartupProfiler implements IWorkbenchContribution { @@ -28,6 +29,7 @@ export class StartupProfiler implements IWorkbenchContribution { @IClipboardService private readonly _clipboardService: IClipboardService, @ILifecycleService lifecycleService: ILifecycleService, @IExtensionService extensionService: IExtensionService, + @IOpenerService private readonly _openerService: IOpenerService ) { // wait for everything to be ready Promise.all([ @@ -116,6 +118,6 @@ export class StartupProfiler implements IWorkbenchContribution { const baseUrl = product.reportIssueUrl; const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&'; - window.open(`${baseUrl}${queryStringPrefix}body=${encodeURIComponent(body)}`); + this._openerService.open(URI.parse(`${baseUrl}${queryStringPrefix}body=${encodeURIComponent(body)}`)); } } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 4174d8137f637..33e16e9f22d0f 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -61,6 +61,7 @@ import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/v import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; const $ = dom.$; @@ -153,6 +154,7 @@ export class SearchView extends ViewletPanel { @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @IKeybindingService keybindingService: IKeybindingService, @IStorageService storageService: IStorageService, + @IOpenerService private readonly openerService: IOpenerService ) { super({ ...(options as IViewletPanelOptions), id: VIEW_ID, ariaHeaderLabel: nls.localize('searchView', "Search") }, keybindingService, contextMenuService, configurationService, contextKeyService); @@ -1473,7 +1475,7 @@ export class SearchView extends ViewletPanel { private onLearnMore = (e: MouseEvent): void => { dom.EventHelper.stop(e, false); - window.open('https://go.microsoft.com/fwlink/?linkid=853977'); + this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?linkid=853977')); } private updateSearchResultCount(disregardExcludesAndIgnores?: boolean): void { diff --git a/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts index ec9ac3fb2b770..44cf53887ff37 100644 --- a/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts @@ -16,6 +16,8 @@ import { ISurveyData } from 'vs/platform/product/common/product'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; class LanguageSurvey { @@ -25,7 +27,8 @@ class LanguageSurvey { notificationService: INotificationService, telemetryService: ITelemetryService, modelService: IModelService, - textFileService: ITextFileService + textFileService: ITextFileService, + openerService: IOpenerService ) { const SESSION_COUNT_KEY = `${data.surveyId}.sessionCount`; const LAST_SESSION_DATE_KEY = `${data.surveyId}.lastSessionDate`; @@ -94,7 +97,7 @@ class LanguageSurvey { run: () => { telemetryService.publicLog(`${data.surveyId}.survey/takeShortSurvey`); telemetryService.getTelemetryInfo().then(info => { - window.open(`${data.surveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`); + openerService.open(URI.parse(`${data.surveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`)); storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); }); @@ -126,11 +129,12 @@ class LanguageSurveysContribution implements IWorkbenchContribution { @INotificationService notificationService: INotificationService, @ITelemetryService telemetryService: ITelemetryService, @IModelService modelService: IModelService, - @ITextFileService textFileService: ITextFileService + @ITextFileService textFileService: ITextFileService, + @IOpenerService openerService: IOpenerService ) { product.surveys .filter(surveyData => surveyData.surveyId && surveyData.editCount && surveyData.languageId && surveyData.surveyUrl && surveyData.userProbability) - .map(surveyData => new LanguageSurvey(surveyData, storageService, notificationService, telemetryService, modelService, textFileService)); + .map(surveyData => new LanguageSurvey(surveyData, storageService, notificationService, telemetryService, modelService, textFileService, openerService)); } } diff --git a/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts b/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts index c11fd38db201c..5e4433ce2ad9c 100644 --- a/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts +++ b/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts @@ -13,6 +13,8 @@ import pkg from 'vs/platform/product/node/package'; import product from 'vs/platform/product/node/product'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; const PROBABILITY = 0.15; const SESSION_COUNT_KEY = 'nps/sessionCount'; @@ -25,7 +27,8 @@ class NPSContribution implements IWorkbenchContribution { constructor( @IStorageService storageService: IStorageService, @INotificationService notificationService: INotificationService, - @ITelemetryService telemetryService: ITelemetryService + @ITelemetryService telemetryService: ITelemetryService, + @IOpenerService openerService: IOpenerService ) { const skipVersion = storageService.get(SKIP_VERSION_KEY, StorageScope.GLOBAL, ''); if (skipVersion) { @@ -64,7 +67,7 @@ class NPSContribution implements IWorkbenchContribution { label: nls.localize('takeSurvey', "Take Survey"), run: () => { telemetryService.getTelemetryInfo().then(info => { - window.open(`${product.npsSurveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`); + openerService.open(URI.parse(`${product.npsSurveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`)); storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); }); @@ -87,4 +90,4 @@ class NPSContribution implements IWorkbenchContribution { if (language === 'en' && product.npsSurveyUrl) { const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(NPSContribution, LifecyclePhase.Restored); -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 17e627687400b..461797d862c1b 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -1620,7 +1620,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer nls.localize('TaskService.noWorkspace', "Tasks are only available on a workspace folder."), [{ label: nls.localize('TaskService.learnMore', "Learn More"), - run: () => window.open('https://code.visualstudio.com/docs/editor/tasks') + run: () => this.openerService.open(URI.parse('https://code.visualstudio.com/docs/editor/tasks')) }] ); return false; diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts index 2c0d6778d39c7..200de4fa5c9b7 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts @@ -9,6 +9,8 @@ import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/ import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import * as platform from 'vs/base/common/platform'; import product from 'vs/platform/product/node/product'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; export class GettingStarted implements IWorkbenchContribution { @@ -20,7 +22,8 @@ export class GettingStarted implements IWorkbenchContribution { constructor( @IStorageService private readonly storageService: IStorageService, @IEnvironmentService environmentService: IEnvironmentService, - @ITelemetryService private readonly telemetryService: ITelemetryService + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IOpenerService private readonly openerService: IOpenerService ) { this.appName = product.nameLong; @@ -50,7 +53,7 @@ export class GettingStarted implements IWorkbenchContribution { if (platform.isLinux && platform.isRootUser()) { return; } - window.open(url); + this.openerService.open(URI.parse(url)); } private handleWelcome(): void { diff --git a/src/vs/workbench/electron-browser/actions/helpActions.ts b/src/vs/workbench/electron-browser/actions/helpActions.ts index 3c5d242f92f16..ce5e237ef08c9 100644 --- a/src/vs/workbench/electron-browser/actions/helpActions.ts +++ b/src/vs/workbench/electron-browser/actions/helpActions.ts @@ -8,6 +8,8 @@ import * as nls from 'vs/nls'; import product from 'vs/platform/product/node/product'; import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; export class KeybindingsReferenceAction extends Action { @@ -19,13 +21,14 @@ export class KeybindingsReferenceAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } run(): Promise { - window.open(KeybindingsReferenceAction.URL); + this.openerService.open(URI.parse(KeybindingsReferenceAction.URL)); return Promise.resolve(); } @@ -41,13 +44,14 @@ export class OpenDocumentationUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } run(): Promise { - window.open(OpenDocumentationUrlAction.URL); + this.openerService.open(URI.parse(OpenDocumentationUrlAction.URL)); return Promise.resolve(); } @@ -63,13 +67,14 @@ export class OpenIntroductoryVideosUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } run(): Promise { - window.open(OpenIntroductoryVideosUrlAction.URL); + this.openerService.open(URI.parse(OpenIntroductoryVideosUrlAction.URL)); return Promise.resolve(); } @@ -85,13 +90,14 @@ export class OpenTipsAndTricksUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } run(): Promise { - window.open(OpenTipsAndTricksUrlAction.URL); + this.openerService.open(URI.parse(OpenTipsAndTricksUrlAction.URL)); return Promise.resolve(); } } @@ -107,6 +113,7 @@ export class OpenNewsletterSignupUrlAction extends Action { constructor( id: string, label: string, + @IOpenerService private readonly openerService: IOpenerService, @ITelemetryService telemetryService: ITelemetryService ) { super(id, label); @@ -116,7 +123,7 @@ export class OpenNewsletterSignupUrlAction extends Action { async run(): Promise { const info = await this.telemetryService.getTelemetryInfo(); - window.open(`${OpenNewsletterSignupUrlAction.URL}?machineId=${encodeURIComponent(info.machineId)}`); + this.openerService.open(URI.parse(`${OpenNewsletterSignupUrlAction.URL}?machineId=${encodeURIComponent(info.machineId)}`)); } } @@ -127,14 +134,15 @@ export class OpenTwitterUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } run(): Promise { if (product.twitterUrl) { - window.open(product.twitterUrl); + this.openerService.open(URI.parse(product.twitterUrl)); } return Promise.resolve(); @@ -148,14 +156,15 @@ export class OpenRequestFeatureUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } run(): Promise { if (product.requestFeatureUrl) { - window.open(product.requestFeatureUrl); + this.openerService.open(URI.parse(product.requestFeatureUrl)); } return Promise.resolve(); @@ -169,7 +178,8 @@ export class OpenLicenseUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } @@ -178,9 +188,9 @@ export class OpenLicenseUrlAction extends Action { if (product.licenseUrl) { if (language) { const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?'; - window.open(`${product.licenseUrl}${queryArgChar}lang=${language}`); + this.openerService.open(URI.parse(`${product.licenseUrl}${queryArgChar}lang=${language}`)); } else { - window.open(product.licenseUrl); + this.openerService.open(URI.parse(product.licenseUrl)); } } @@ -195,7 +205,8 @@ export class OpenPrivacyStatementUrlAction extends Action { constructor( id: string, - label: string + label: string, + @IOpenerService private readonly openerService: IOpenerService ) { super(id, label); } @@ -204,9 +215,9 @@ export class OpenPrivacyStatementUrlAction extends Action { if (product.privacyStatementUrl) { if (language) { const queryArgChar = product.privacyStatementUrl.indexOf('?') > 0 ? '&' : '?'; - window.open(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`); + this.openerService.open(URI.parse(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`)); } else { - window.open(product.privacyStatementUrl); + this.openerService.open(URI.parse(product.privacyStatementUrl)); } } diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index a2ee3629a4683..a9c2344af12a5 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -356,29 +356,35 @@ export class ElectronWindow extends Disposable { private setupOpenHandlers(): void { - // Handle window.open() calls + // Block window.open() calls const $this = this; - window.open = function (url: string, target: string, features: string, replace: boolean): Window | null { - $this.windowsService.openExternal(url); + window.open = function (): Window | null { + console.error(new Error('Prevented call to window.open(). Use IOpenerService instead!')); return null; }; - // Handle external open calls + // Handle internal open() calls this.openerService.registerOpener({ async open(resource: URI, options?: { openToSide?: boolean; openExternal?: boolean; } | undefined): Promise { - if (!options || !options.openExternal) { - return false; // only override behaviour for external open() - } - const success = await $this.windowsService.openExternal(encodeURI(resource.toString(true))); - if (!success && resource.scheme === Schemas.file) { - await $this.windowsService.showItemInFolder(resource); + // If either the caller wants to open externally or the + // scheme is one where we prefer to open externally + // we handle this resource by delegating the opening to + // the main process to prevent window focus issues. + const scheme = resource.scheme.toLowerCase(); + const preferOpenExternal = (scheme === Schemas.mailto || scheme === Schemas.http || scheme === Schemas.https); + if ((options && options.openExternal) || preferOpenExternal) { + const success = await $this.windowsService.openExternal(encodeURI(resource.toString(true))); + if (!success && resource.scheme === Schemas.file) { + // if opening failed, and this is a file, we can still try to reveal it + await $this.windowsService.showItemInFolder(resource); + } return true; } - return success; + return false; // not handled by us } }); } From 97e60d9f1ccb40afcd7244387018dfb2ba3afd33 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 20 Aug 2019 12:19:02 +0200 Subject: [PATCH 382/613] History Main Service: bad reference to 'window'? Fixes #79426 --- src/vs/platform/history/common/historyStorage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/history/common/historyStorage.ts b/src/vs/platform/history/common/historyStorage.ts index 089d73ec3139f..b026460963d66 100644 --- a/src/vs/platform/history/common/historyStorage.ts +++ b/src/vs/platform/history/common/historyStorage.ts @@ -71,7 +71,7 @@ export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefine result.workspaces.push({ folderUri: URI.file(workspace) }); } else if (isLegacySerializedWorkspace(workspace)) { result.workspaces.push({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } }); - } else if (isUriComponents(window)) { + } else if (isUriComponents(workspace)) { // added by 1.26-insiders result.workspaces.push({ folderUri: URI.revive(workspace) }); } From 3a6099cab3b9b17a47c2a045ceb3914930f9a75a Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 20 Aug 2019 12:19:22 +0200 Subject: [PATCH 383/613] repl: support completionTriggerCharacters #79195 --- .../workbench/contrib/debug/browser/repl.ts | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 938d2c6477c5d..2328778efd0b0 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -104,6 +104,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati private scopedInstantiationService!: IInstantiationService; private replElementsChangeListener: IDisposable | undefined; private styleElement: HTMLStyleElement | undefined; + private completionItemProvider: IDisposable | undefined; constructor( @IDebugService private readonly debugService: IDebugService, @@ -131,7 +132,33 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati this._register(this.debugService.getViewModel().onDidFocusSession(session => { if (session) { sessionsToIgnore.delete(session); + if (this.completionItemProvider) { + this.completionItemProvider.dispose(); + } + if (session.capabilities.supportsCompletionsRequest) { + this.completionItemProvider = CompletionProviderRegistry.register({ scheme: DEBUG_SCHEME, pattern: '**/replinput', hasAccessToAllModels: true }, { + triggerCharacters: session.capabilities.completionTriggerCharacters || ['.'], + provideCompletionItems: async (_: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken): Promise => { + // Disable history navigation because up and down are used to navigate through the suggest widget + this.historyNavigationEnablement.set(false); + + const model = this.replInput.getModel(); + if (model) { + const word = model.getWordAtPosition(position); + const overwriteBefore = word ? word.word.length : 0; + const text = model.getLineContent(position.lineNumber); + const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; + const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined; + const suggestions = await session.completions(frameId, text, position, overwriteBefore); + return { suggestions }; + } + + return Promise.resolve({ suggestions: [] }); + } + }); + } } + this.selectSession(); })); this._register(this.debugService.onWillNewSession(newSession => { @@ -430,34 +457,6 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati options.readOnly = true; this.replInput = this.scopedInstantiationService.createInstance(CodeEditorWidget, this.replInputContainer, options, getSimpleCodeEditorWidgetOptions()); - CompletionProviderRegistry.register({ scheme: DEBUG_SCHEME, pattern: '**/replinput', hasAccessToAllModels: true }, { - triggerCharacters: ['.'], - provideCompletionItems: (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken): Promise => { - // Disable history navigation because up and down are used to navigate through the suggest widget - this.historyNavigationEnablement.set(false); - - const focusedSession = this.debugService.getViewModel().focusedSession; - if (focusedSession && focusedSession.capabilities.supportsCompletionsRequest) { - - const model = this.replInput.getModel(); - if (model) { - const word = model.getWordAtPosition(position); - const overwriteBefore = word ? word.word.length : 0; - const text = model.getLineContent(position.lineNumber); - const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; - const frameId = focusedStackFrame ? focusedStackFrame.frameId : undefined; - - return focusedSession.completions(frameId, text, position, overwriteBefore).then(suggestions => { - return { suggestions }; - }, err => { - return { suggestions: [] }; - }); - } - } - return Promise.resolve({ suggestions: [] }); - } - }); - this._register(this.replInput.onDidScrollChange(e => { if (!e.scrollHeightChanged) { return; From 1271e25f4fceb332c6e4dd2d56f3f30f7bae9f99 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Tue, 20 Aug 2019 12:28:26 +0200 Subject: [PATCH 384/613] update DAP to 1.36.0-pre.0 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index b8ed8e8fea7a8..559c11a1763d9 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", "vsce": "1.48.0", - "vscode-debugprotocol": "1.35.0", + "vscode-debugprotocol": "1.36.0-pre.0", "vscode-nls-dev": "^3.3.1", "webpack": "^4.16.5", "webpack-cli": "^3.1.0", diff --git a/yarn.lock b/yarn.lock index 78e871f843b02..90211b1e51536 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9631,10 +9631,10 @@ vscode-chokidar@2.1.7: optionalDependencies: vscode-fsevents "1.2.12" -vscode-debugprotocol@1.35.0: - version "1.35.0" - resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.35.0.tgz#565140cd42945e30c6c85cafb38c631457d4a46c" - integrity sha512-+OMm11R1bGYbpIJ5eQIkwoDGFF4GvBz3Ztl6/VM+/RNNb2Gjk2c0Ku+oMmfhlTmTlPCpgHBsH4JqVCbUYhu5bA== +vscode-debugprotocol@1.36.0-pre.0: + version "1.36.0-pre.0" + resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.36.0-pre.0.tgz#4f998e143acae9e3ce13c308d4ad322f96841926" + integrity sha512-nQhImfsUJFfr73JqA2Uc1bjTvpgZRabqkkKQWUbmrrnTLCPKRCr9AgE44Ehb/vkLXNdo+vPnWjJXWnPGjT10mA== vscode-fsevents@1.2.12: version "1.2.12" From 37981d8fd5d4ba1619f69dc8a0f4670543d39650 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 20 Aug 2019 12:33:58 +0200 Subject: [PATCH 385/613] Fixes #79430: Bring all auto-closing logic in one place which can now also handle multi-character auto-closing pairs --- src/vs/editor/common/controller/cursor.ts | 26 +- .../editor/common/controller/cursorCommon.ts | 29 +- .../controller/cursorDeleteOperations.ts | 12 +- .../common/controller/cursorTypeOperations.ts | 250 ++++++++---------- .../common/modes/languageConfiguration.ts | 8 +- .../modes/languageConfigurationRegistry.ts | 25 +- .../common/modes/supports/characterPair.ts | 27 +- .../modes/supports/electricCharacter.ts | 68 +---- .../test/browser/controller/cursor.test.ts | 47 ++-- .../modes/supports/characterPair.test.ts | 16 +- .../modes/supports/electricCharacter.test.ts | 77 +----- src/vs/monaco.d.ts | 8 +- 12 files changed, 235 insertions(+), 358 deletions(-) diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index fefca3cd9b55f..1b448239065ef 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -86,6 +86,14 @@ export class CursorModelState { class AutoClosedAction { + public static getAllAutoClosedCharacters(autoClosedActions: AutoClosedAction[]): Range[] { + let autoClosedCharacters: Range[] = []; + for (const autoClosedAction of autoClosedActions) { + autoClosedCharacters = autoClosedCharacters.concat(autoClosedAction.getAutoClosedCharactersRanges()); + } + return autoClosedCharacters; + } + private readonly _model: ITextModel; private _autoClosedCharactersDecorations: string[]; @@ -593,11 +601,12 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { } const closeChar = m[1]; - const openChar = this.context.config.autoClosingPairsClose[closeChar]; - if (!openChar) { + const autoClosingPairsCandidates = this.context.config.autoClosingPairsClose2.get(closeChar); + if (!autoClosingPairsCandidates || autoClosingPairsCandidates.length !== 1) { return null; } + const openChar = autoClosingPairsCandidates[0].open; const closeCharIndex = edit.text.length - m[2].length - 1; const openCharIndex = edit.text.lastIndexOf(openChar, closeCharIndex - 1); if (openCharIndex === -1) { @@ -738,7 +747,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { private _interpretCompositionEnd(source: string) { if (!this._isDoingComposition && source === 'keyboard') { // composition finishes, let's check if we need to auto complete if necessary. - this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections())); + const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); + this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), autoClosedCharacters)); } } @@ -756,14 +766,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { chr = text.charAt(i); } - let autoClosedCharacters: Range[] = []; - if (this._autoClosedActions.length > 0) { - for (let i = 0, len = this._autoClosedActions.length; i < len; i++) { - autoClosedCharacters = autoClosedCharacters.concat(this._autoClosedActions[i].getAutoClosedCharactersRanges()); - } - } - - // Here we must interpret each typed character individually, that's why we create a new context + // Here we must interpret each typed character individually + const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); this._executeEditOperation(TypeOperations.typeWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), autoClosedCharacters, chr)); } diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index 844387516d67d..f459e61735fe2 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -15,7 +15,7 @@ import { ICommand, IConfiguration, ScrollType } from 'vs/editor/common/editorCom import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageIdentifier } from 'vs/editor/common/modes'; -import { IAutoClosingPair } from 'vs/editor/common/modes/languageConfiguration'; +import { IAutoClosingPair, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; @@ -67,11 +67,22 @@ export interface ICursors { export interface CharacterMap { [char: string]: string; } +export interface MultipleCharacterMap { + [char: string]: string[]; +} const autoCloseAlways = () => true; const autoCloseNever = () => false; const autoCloseBeforeWhitespace = (chr: string) => (chr === ' ' || chr === '\t'); +function appendEntry(target: Map, key: K, value: V): void { + if (target.has(key)) { + target.get(key)!.push(value); + } else { + target.set(key, [value]); + } +} + export class CursorConfiguration { _cursorMoveConfigurationBrand: void; @@ -90,8 +101,8 @@ export class CursorConfiguration { public readonly autoClosingQuotes: EditorAutoClosingStrategy; public readonly autoSurround: EditorAutoSurroundStrategy; public readonly autoIndent: boolean; - public readonly autoClosingPairsOpen: CharacterMap; - public readonly autoClosingPairsClose: CharacterMap; + public readonly autoClosingPairsOpen2: Map; + public readonly autoClosingPairsClose2: Map; public readonly surroundingPairs: CharacterMap; public readonly shouldAutoCloseBefore: { quote: (ch: string) => boolean, bracket: (ch: string) => boolean }; @@ -138,8 +149,8 @@ export class CursorConfiguration { this.autoSurround = c.autoSurround; this.autoIndent = c.autoIndent; - this.autoClosingPairsOpen = {}; - this.autoClosingPairsClose = {}; + this.autoClosingPairsOpen2 = new Map(); + this.autoClosingPairsClose2 = new Map(); this.surroundingPairs = {}; this._electricChars = null; @@ -151,8 +162,10 @@ export class CursorConfiguration { let autoClosingPairs = CursorConfiguration._getAutoClosingPairs(languageIdentifier); if (autoClosingPairs) { for (const pair of autoClosingPairs) { - this.autoClosingPairsOpen[pair.open] = pair.close; - this.autoClosingPairsClose[pair.close] = pair.open; + appendEntry(this.autoClosingPairsOpen2, pair.open.charAt(pair.open.length - 1), pair); + if (pair.close.length === 1) { + appendEntry(this.autoClosingPairsClose2, pair.close, pair); + } } } @@ -190,7 +203,7 @@ export class CursorConfiguration { } } - private static _getAutoClosingPairs(languageIdentifier: LanguageIdentifier): IAutoClosingPair[] | null { + private static _getAutoClosingPairs(languageIdentifier: LanguageIdentifier): StandardAutoClosingPairConditional[] | null { try { return LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id); } catch (e) { diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts index 5a7830e26b3b3..3f5e80a3ee96b 100644 --- a/src/vs/editor/common/controller/cursorDeleteOperations.ts +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -63,7 +63,8 @@ export class DeleteOperations { const lineText = model.getLineContent(position.lineNumber); const character = lineText[position.column - 2]; - if (!config.autoClosingPairsOpen.hasOwnProperty(character)) { + const autoClosingPairCandidates = config.autoClosingPairsOpen2.get(character); + if (!autoClosingPairCandidates) { return false; } @@ -78,9 +79,14 @@ export class DeleteOperations { } const afterCharacter = lineText[position.column - 1]; - const closeCharacter = config.autoClosingPairsOpen[character]; - if (afterCharacter !== closeCharacter) { + let foundAutoClosingPair = false; + for (const autoClosingPairCandidate of autoClosingPairCandidates) { + if (autoClosingPairCandidate.open === character && autoClosingPairCandidate.close === afterCharacter) { + foundAutoClosingPair = true; + } + } + if (!foundAutoClosingPair) { return false; } } diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 7ea1d26dffdfa..aa6b574742040 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -13,9 +13,10 @@ import { CursorColumns, CursorConfiguration, EditOperationResult, EditOperationT import { WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; +import { Position } from 'vs/editor/common/core/position'; import { ICommand, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { EnterAction, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import { EnterAction, IndentAction, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; @@ -433,7 +434,11 @@ export class TypeOperations { private static _isAutoClosingCloseCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], autoClosedCharacters: Range[], ch: string): boolean { const autoCloseConfig = isQuote(ch) ? config.autoClosingQuotes : config.autoClosingBrackets; - if (autoCloseConfig === 'never' || !config.autoClosingPairsClose.hasOwnProperty(ch)) { + if (autoCloseConfig === 'never') { + return false; + } + + if (!config.autoClosingPairsClose2.has(ch)) { return false; } @@ -469,15 +474,6 @@ export class TypeOperations { return true; } - private static _countNeedlesInHaystack(haystack: string, needle: string): number { - let cnt = 0; - let lastIndex = -1; - while ((lastIndex = haystack.indexOf(needle, lastIndex + 1)) !== -1) { - cnt++; - } - return cnt; - } - private static _runAutoClosingCloseCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): EditOperationResult { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { @@ -492,65 +488,98 @@ export class TypeOperations { }); } - private static _isBeforeClosingBrace(config: CursorConfiguration, ch: string, characterAfter: string) { - const thisBraceIsSymmetric = (config.autoClosingPairsOpen[ch] === ch); - let isBeforeCloseBrace = false; - for (let otherCloseBrace in config.autoClosingPairsClose) { - const otherBraceIsSymmetric = (config.autoClosingPairsOpen[otherCloseBrace] === otherCloseBrace); + private static _isBeforeClosingBrace(config: CursorConfiguration, autoClosingPair: StandardAutoClosingPairConditional, characterAfter: string) { + const otherAutoClosingPairs = config.autoClosingPairsClose2.get(characterAfter); + if (!otherAutoClosingPairs) { + return false; + } + + const thisBraceIsSymmetric = (autoClosingPair.open === autoClosingPair.close); + for (const otherAutoClosingPair of otherAutoClosingPairs) { + const otherBraceIsSymmetric = (otherAutoClosingPair.open === otherAutoClosingPair.close); if (!thisBraceIsSymmetric && otherBraceIsSymmetric) { continue; } - if (characterAfter === otherCloseBrace) { - isBeforeCloseBrace = true; - break; - } + return true; } - return isBeforeCloseBrace; + return false; } - private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): boolean { + private static _findAutoClosingPairOpen(config: CursorConfiguration, model: ITextModel, positions: Position[], ch: string): StandardAutoClosingPairConditional | null { + const autoClosingPairCandidates = config.autoClosingPairsOpen2.get(ch); + if (!autoClosingPairCandidates) { + return null; + } + + // Determine which auto-closing pair it is + let autoClosingPair: StandardAutoClosingPairConditional | null = null; + for (const autoClosingPairCandidate of autoClosingPairCandidates) { + if (autoClosingPair === null || autoClosingPairCandidate.open.length > autoClosingPair.open.length) { + let candidateIsMatch = true; + for (const position of positions) { + const relevantText = model.getValueInRange(new Range(position.lineNumber, position.column - autoClosingPairCandidate.open.length + 1, position.lineNumber, position.column)); + if (relevantText + ch !== autoClosingPairCandidate.open) { + candidateIsMatch = false; + break; + } + } + + if (candidateIsMatch) { + autoClosingPair = autoClosingPairCandidate; + } + } + } + return autoClosingPair; + } + + private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean): StandardAutoClosingPairConditional | null { const chIsQuote = isQuote(ch); const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets; + if (autoCloseConfig === 'never') { + return null; + } - if (autoCloseConfig === 'never' || !config.autoClosingPairsOpen.hasOwnProperty(ch)) { - return false; + const autoClosingPair = this._findAutoClosingPairOpen(config, model, selections.map(s => s.getPosition()), ch); + if (!autoClosingPair) { + return null; } - let shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; + const shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; if (!selection.isEmpty()) { - return false; + return null; } const position = selection.getPosition(); const lineText = model.getLineContent(position.lineNumber); - // Do not auto-close ' or " after a word character - if ((chIsQuote && position.column > 1) && autoCloseConfig !== 'always') { - const wordSeparators = getMapForWordSeparators(config.wordSeparators); - const characterBeforeCode = lineText.charCodeAt(position.column - 2); - const characterBeforeType = wordSeparators.get(characterBeforeCode); - if (characterBeforeType === WordCharacterClass.Regular) { - return false; - } - } - // Only consider auto closing the pair if a space follows or if another autoclosed pair follows - const characterAfter = lineText.charAt(position.column - 1); - if (characterAfter) { - let isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, ch, characterAfter); + if (lineText.length > position.column - 1) { + const characterAfter = lineText.charAt(position.column - 1); + const isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, autoClosingPair, characterAfter); if (!isBeforeCloseBrace && !shouldAutoCloseBefore(characterAfter)) { - return false; + return null; } } if (!model.isCheapToTokenize(position.lineNumber)) { // Do not force tokenization - return false; + return null; + } + + // Do not auto-close ' or " after a word character + if (autoClosingPair.open.length === 1 && chIsQuote && autoCloseConfig !== 'always') { + const wordSeparators = getMapForWordSeparators(config.wordSeparators); + if (insertOpenCharacter && position.column > 1 && wordSeparators.get(lineText.charCodeAt(position.column - 2)) === WordCharacterClass.Regular) { + return null; + } + if (!insertOpenCharacter && position.column > 2 && wordSeparators.get(lineText.charCodeAt(position.column - 3)) === WordCharacterClass.Regular) { + return null; + } } model.forceTokenization(position.lineNumber); @@ -558,25 +587,24 @@ export class TypeOperations { let shouldAutoClosePair = false; try { - shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column); + shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(autoClosingPair, lineTokens, insertOpenCharacter ? position.column : position.column - 1); } catch (e) { onUnexpectedError(e); } if (!shouldAutoClosePair) { - return false; + return null; } } - return true; + return autoClosingPair; } - private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): EditOperationResult { + private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean, autoClosingPair: StandardAutoClosingPairConditional): EditOperationResult { let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; - const closeCharacter = config.autoClosingPairsOpen[ch]; - commands[i] = new TypeWithAutoClosingCommand(selection, ch, closeCharacter); + commands[i] = new TypeWithAutoClosingCommand(selection, ch, insertOpenCharacter, autoClosingPair.close); } return new EditOperationResult(EditOperationType.Typing, commands, { shouldPushStackElementBefore: true, @@ -679,14 +707,6 @@ export class TypeOperations { return null; } - if (electricAction.appendText) { - const command = new ReplaceCommandWithOffsetCursorState(selection, ch + electricAction.appendText, 0, -electricAction.appendText.length); - return new EditOperationResult(EditOperationType.Typing, [command], { - shouldPushStackElementBefore: false, - shouldPushStackElementAfter: true - }); - } - if (electricAction.matchOpenBracket) { let endColumn = (lineTokens.getLineContent() + ch).lastIndexOf(electricAction.matchOpenBracket) + 1; let match = model.findMatchingBracketUp(electricAction.matchOpenBracket, { @@ -722,87 +742,44 @@ export class TypeOperations { return null; } - public static compositionEndWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[]): EditOperationResult | null { - if (config.autoClosingQuotes === 'never') { - return null; - } - - let commands: ICommand[] = []; - - for (let i = 0; i < selections.length; i++) { - if (!selections[i].isEmpty()) { - continue; + /** + * This is very similar with typing, but the character is already in the text buffer! + */ + public static compositionEndWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], autoClosedCharacters: Range[]): EditOperationResult | null { + let ch: string | null = null; + // extract last typed character + for (const selection of selections) { + if (!selection.isEmpty()) { + return null; } - const position = selections[i].getPosition(); - const lineText = model.getLineContent(position.lineNumber); - const ch = lineText.charAt(position.column - 2); - - if (config.autoClosingPairsClose.hasOwnProperty(ch)) { // first of all, it's a closing tag - if (ch === config.autoClosingPairsClose[ch] /** isEqualPair */) { - const lineTextBeforeCursor = lineText.substr(0, position.column - 2); - const chCntBefore = this._countNeedlesInHaystack(lineTextBeforeCursor, ch); - - if (chCntBefore % 2 === 1) { - continue; // it pairs with the opening tag. - } - } + const position = selection.getPosition(); + const currentChar = model.getValueInRange(new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column)); + if (ch === null) { + ch = currentChar; + } else if (ch !== currentChar) { + return null; } + } - // As we are not typing in a new character, so we don't need to run `_runAutoClosingCloseCharType` - // Next step, let's try to check if it's an open char. - if (config.autoClosingPairsOpen.hasOwnProperty(ch)) { - if (isQuote(ch) && position.column > 2) { - const wordSeparators = getMapForWordSeparators(config.wordSeparators); - const characterBeforeCode = lineText.charCodeAt(position.column - 3); - const characterBeforeType = wordSeparators.get(characterBeforeCode); - if (characterBeforeType === WordCharacterClass.Regular) { - continue; - } - } - - const characterAfter = lineText.charAt(position.column - 1); - - if (characterAfter) { - let isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, ch, characterAfter); - let shouldAutoCloseBefore = isQuote(ch) ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket; - if (isBeforeCloseBrace) { - // In normal auto closing logic, we will auto close if the cursor is even before a closing brace intentionally. - // However for composition mode, we do nothing here as users might clear all the characters for composition and we don't want to do a unnecessary auto close. - // Related: microsoft/vscode#57250. - continue; - } - if (!shouldAutoCloseBefore(characterAfter)) { - continue; - } - } - - if (!model.isCheapToTokenize(position.lineNumber)) { - // Do not force tokenization - continue; - } - - model.forceTokenization(position.lineNumber); - const lineTokens = model.getLineTokens(position.lineNumber); - - let shouldAutoClosePair = false; + if (!ch) { + return null; + } - try { - shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column - 1); - } catch (e) { - onUnexpectedError(e); - } + if (this._isAutoClosingCloseCharType(config, model, selections, autoClosedCharacters, ch)) { + // Unfortunately, the close character is at this point "doubled", so we need to delete it... + const commands = selections.map(s => new ReplaceCommand(new Range(s.positionLineNumber, s.positionColumn, s.positionLineNumber, s.positionColumn + 1), '', false)); + return new EditOperationResult(EditOperationType.Typing, commands, { + shouldPushStackElementBefore: true, + shouldPushStackElementAfter: false + }); + } - if (shouldAutoClosePair) { - const closeCharacter = config.autoClosingPairsOpen[ch]; - commands[i] = new ReplaceCommandWithOffsetCursorState(selections[i], closeCharacter, 0, -closeCharacter.length); - } - } + const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, false); + if (autoClosingPairOpenCharType) { + return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, false, autoClosingPairOpenCharType); } - return new EditOperationResult(EditOperationType.Typing, commands, { - shouldPushStackElementBefore: true, - shouldPushStackElementAfter: false - }); + return null; } public static typeWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], autoClosedCharacters: Range[], ch: string): EditOperationResult { @@ -840,8 +817,9 @@ export class TypeOperations { return this._runAutoClosingCloseCharType(prevEditOperationType, config, model, selections, ch); } - if (this._isAutoClosingOpenCharType(config, model, selections, ch)) { - return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch); + const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, true); + if (autoClosingPairOpenCharType) { + return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairOpenCharType); } if (this._isSurroundSelectionType(config, model, selections, ch)) { @@ -929,12 +907,14 @@ export class TypeOperations { export class TypeWithAutoClosingCommand extends ReplaceCommandWithOffsetCursorState { - private _closeCharacter: string; + private readonly _openCharacter: string; + private readonly _closeCharacter: string; public closeCharacterRange: Range | null; public enclosingRange: Range | null; - constructor(selection: Selection, openCharacter: string, closeCharacter: string) { - super(selection, openCharacter + closeCharacter, 0, -closeCharacter.length); + constructor(selection: Selection, openCharacter: string, insertOpenCharacter: boolean, closeCharacter: string) { + super(selection, (insertOpenCharacter ? openCharacter : '') + closeCharacter, 0, -closeCharacter.length); + this._openCharacter = openCharacter; this._closeCharacter = closeCharacter; this.closeCharacterRange = null; this.enclosingRange = null; @@ -944,7 +924,7 @@ export class TypeWithAutoClosingCommand extends ReplaceCommandWithOffsetCursorSt let inverseEditOperations = helper.getInverseEditOperations(); let range = inverseEditOperations[0].range; this.closeCharacterRange = new Range(range.startLineNumber, range.endColumn - this._closeCharacter.length, range.endLineNumber, range.endColumn); - this.enclosingRange = range; + this.enclosingRange = new Range(range.startLineNumber, range.endColumn - this._openCharacter.length - this._closeCharacter.length, range.endLineNumber, range.endColumn); return super.computeCursorState(model, helper); } } diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index 4bca048eec17c..ed8d2fdb4ae37 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -78,7 +78,9 @@ export interface LanguageConfiguration { * * @deprecated Will be replaced by a better API soon. */ - __electricCharacterSupport?: IBracketElectricCharacterContribution; + __electricCharacterSupport?: { + docComment?: IDocComment; + }; } /** @@ -155,10 +157,6 @@ export interface OnEnterRule { action: EnterAction; } -export interface IBracketElectricCharacterContribution { - docComment?: IDocComment; -} - /** * Definition of documentation comments (e.g. Javadoc/JSdoc) */ diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index baf47379af988..cee0469e30e0e 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -12,7 +12,7 @@ import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; -import { EnterAction, FoldingRules, IAutoClosingPair, IAutoClosingPairConditional, IndentAction, IndentationRule, LanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration'; +import { EnterAction, FoldingRules, IAutoClosingPair, IndentAction, IndentationRule, LanguageConfiguration, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { createScopedLineTokens } from 'vs/editor/common/modes/supports'; import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; @@ -97,16 +97,7 @@ export class RichEditSupport { public get electricCharacter(): BracketElectricCharacterSupport | null { if (!this._electricCharacter) { - let autoClosingPairs: IAutoClosingPairConditional[] = []; - if (this._conf.autoClosingPairs) { - autoClosingPairs = this._conf.autoClosingPairs; - } else if (this._conf.brackets) { - autoClosingPairs = this._conf.brackets.map(b => { - return { open: b[0], close: b[1] }; - }); - } - - this._electricCharacter = new BracketElectricCharacterSupport(this.brackets, autoClosingPairs, this._conf.__electricCharacterSupport); + this._electricCharacter = new BracketElectricCharacterSupport(this.brackets); } return this._electricCharacter; } @@ -261,7 +252,7 @@ export class LanguageConfigurationRegistryImpl { return value.characterPair || null; } - public getAutoClosingPairs(languageId: LanguageId): IAutoClosingPair[] { + public getAutoClosingPairs(languageId: LanguageId): StandardAutoClosingPairConditional[] { let characterPairSupport = this._getCharacterPairSupport(languageId); if (!characterPairSupport) { return []; @@ -285,13 +276,9 @@ export class LanguageConfigurationRegistryImpl { return characterPairSupport.getSurroundingPairs(); } - public shouldAutoClosePair(character: string, context: LineTokens, column: number): boolean { - let scopedLineTokens = createScopedLineTokens(context, column - 1); - let characterPairSupport = this._getCharacterPairSupport(scopedLineTokens.languageId); - if (!characterPairSupport) { - return false; - } - return characterPairSupport.shouldAutoClosePair(character, scopedLineTokens, column - scopedLineTokens.firstCharOffset); + public shouldAutoClosePair(autoClosingPair: StandardAutoClosingPairConditional, context: LineTokens, column: number): boolean { + const scopedLineTokens = createScopedLineTokens(context, column - 1); + return CharacterPairSupport.shouldAutoClosePair(autoClosingPair, scopedLineTokens, column - scopedLineTokens.firstCharOffset); } // end characterPair diff --git a/src/vs/editor/common/modes/supports/characterPair.ts b/src/vs/editor/common/modes/supports/characterPair.ts index bb8ecd4980ebd..b7cfa2ecdd70d 100644 --- a/src/vs/editor/common/modes/supports/characterPair.ts +++ b/src/vs/editor/common/modes/supports/characterPair.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharacterPair, IAutoClosingPair, IAutoClosingPairConditional, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; +import { IAutoClosingPair, StandardAutoClosingPairConditional, LanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration'; import { ScopedLineTokens } from 'vs/editor/common/modes/supports'; export class CharacterPairSupport { @@ -15,7 +15,7 @@ export class CharacterPairSupport { private readonly _surroundingPairs: IAutoClosingPair[]; private readonly _autoCloseBefore: string; - constructor(config: { brackets?: CharacterPair[]; autoClosingPairs?: IAutoClosingPairConditional[], surroundingPairs?: IAutoClosingPair[], autoCloseBefore?: string }) { + constructor(config: LanguageConfiguration) { if (config.autoClosingPairs) { this._autoClosingPairs = config.autoClosingPairs.map(el => new StandardAutoClosingPairConditional(el)); } else if (config.brackets) { @@ -24,12 +24,18 @@ export class CharacterPairSupport { this._autoClosingPairs = []; } + if (config.__electricCharacterSupport && config.__electricCharacterSupport.docComment) { + const docComment = config.__electricCharacterSupport.docComment; + // IDocComment is legacy, only partially supported + this._autoClosingPairs.push(new StandardAutoClosingPairConditional({ open: docComment.open, close: docComment.close || '' })); + } + this._autoCloseBefore = typeof config.autoCloseBefore === 'string' ? config.autoCloseBefore : CharacterPairSupport.DEFAULT_AUTOCLOSE_BEFORE_LANGUAGE_DEFINED; this._surroundingPairs = config.surroundingPairs || this._autoClosingPairs; } - public getAutoClosingPairs(): IAutoClosingPair[] { + public getAutoClosingPairs(): StandardAutoClosingPairConditional[] { return this._autoClosingPairs; } @@ -37,22 +43,15 @@ export class CharacterPairSupport { return this._autoCloseBefore; } - public shouldAutoClosePair(character: string, context: ScopedLineTokens, column: number): boolean { + public static shouldAutoClosePair(autoClosingPair: StandardAutoClosingPairConditional, context: ScopedLineTokens, column: number): boolean { // Always complete on empty line if (context.getTokenCount() === 0) { return true; } - let tokenIndex = context.findTokenIndexAtOffset(column - 2); - let standardTokenType = context.getStandardTokenType(tokenIndex); - - for (const autoClosingPair of this._autoClosingPairs) { - if (autoClosingPair.open === character) { - return autoClosingPair.isOK(standardTokenType); - } - } - - return false; + const tokenIndex = context.findTokenIndexAtOffset(column - 2); + const standardTokenType = context.getStandardTokenType(tokenIndex); + return autoClosingPair.isOK(standardTokenType); } public getSurroundingPairs(): IAutoClosingPair[] { diff --git a/src/vs/editor/common/modes/supports/electricCharacter.ts b/src/vs/editor/common/modes/supports/electricCharacter.ts index 878ef492e12d7..387994c28a42a 100644 --- a/src/vs/editor/common/modes/supports/electricCharacter.ts +++ b/src/vs/editor/common/modes/supports/electricCharacter.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAutoClosingPairConditional, IBracketElectricCharacterContribution, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { ScopedLineTokens, ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; import { BracketsUtils, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; @@ -12,29 +11,17 @@ import { BracketsUtils, RichEditBrackets } from 'vs/editor/common/modes/supports * @internal */ export interface IElectricAction { - // Only one of the following properties should be defined: - // The line will be indented at the same level of the line // which contains the matching given bracket type. - matchOpenBracket?: string; - - // The text will be appended after the electric character. - appendText?: string; + matchOpenBracket: string; } export class BracketElectricCharacterSupport { private readonly _richEditBrackets: RichEditBrackets | null; - private readonly _complexAutoClosePairs: StandardAutoClosingPairConditional[]; - constructor(richEditBrackets: RichEditBrackets | null, autoClosePairs: IAutoClosingPairConditional[], contribution: IBracketElectricCharacterContribution | null | undefined) { - contribution = contribution || {}; + constructor(richEditBrackets: RichEditBrackets | null) { this._richEditBrackets = richEditBrackets; - this._complexAutoClosePairs = autoClosePairs.filter(pair => pair.open.length > 1 && !!pair.close).map(el => new StandardAutoClosingPairConditional(el)); - if (contribution.docComment) { - // IDocComment is legacy, only partially supported - this._complexAutoClosePairs.push(new StandardAutoClosingPairConditional({ open: contribution.docComment.open, close: contribution.docComment.close || '' })); - } } public getElectricCharacters(): string[] { @@ -48,11 +35,6 @@ export class BracketElectricCharacterSupport { } } - // auto close - for (let pair of this._complexAutoClosePairs) { - result.push(pair.open.charAt(pair.open.length - 1)); - } - // Filter duplicate entries result = result.filter((item, pos, array) => { return array.indexOf(item) === pos; @@ -62,12 +44,6 @@ export class BracketElectricCharacterSupport { } public onElectricCharacter(character: string, context: ScopedLineTokens, column: number): IElectricAction | null { - return (this._onElectricAutoClose(character, context, column) || - this._onElectricAutoIndent(character, context, column)); - } - - private _onElectricAutoIndent(character: string, context: ScopedLineTokens, column: number): IElectricAction | null { - if (!this._richEditBrackets || this._richEditBrackets.brackets.length === 0) { return null; } @@ -103,44 +79,4 @@ export class BracketElectricCharacterSupport { matchOpenBracket: bracketText }; } - - private _onElectricAutoClose(character: string, context: ScopedLineTokens, column: number): IElectricAction | null { - if (!this._complexAutoClosePairs.length) { - return null; - } - - let line = context.getLineContent(); - - for (let i = 0, len = this._complexAutoClosePairs.length; i < len; i++) { - let pair = this._complexAutoClosePairs[i]; - - // See if the right electric character was pressed - if (character !== pair.open.charAt(pair.open.length - 1)) { - continue; - } - - // check if the full open bracket matches - let start = column - pair.open.length + 1; - let actual = line.substring(start - 1, column - 1) + character; - if (actual !== pair.open) { - continue; - } - - let lastTokenIndex = context.findTokenIndexAtOffset(column - 1); - let lastTokenStandardType = context.getStandardTokenType(lastTokenIndex); - // If we're in a scope listed in 'notIn', do nothing - if (!pair.isOK(lastTokenStandardType)) { - continue; - } - - // If this line already contains the closing tag, do nothing. - if (line.indexOf(pair.close, column - 1) >= 0) { - continue; - } - - return { appendText: pair.close }; - } - - return null; - } } diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index e925406c1b4d7..59e221a8c2fd7 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -3998,8 +3998,12 @@ suite('autoClosingPairs', () => { { open: '\'', close: '\'', notIn: ['string', 'comment'] }, { open: '\"', close: '\"', notIn: ['string'] }, { open: '`', close: '`', notIn: ['string', 'comment'] }, - { open: '/**', close: ' */', notIn: ['string'] } + { open: '/**', close: ' */', notIn: ['string'] }, + { open: 'begin', close: 'end', notIn: ['string'] } ], + __electricCharacterSupport: { + docComment: { open: '/**', close: ' */' } + } })); } @@ -4439,6 +4443,28 @@ suite('autoClosingPairs', () => { mode.dispose(); }); + test('multi-character autoclose', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + '', + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + + model.setValue('begi'); + cursor.setSelections('test', [new Selection(1, 5, 1, 5)]); + cursorCommand(cursor, H.Type, { text: 'n' }, 'keyboard'); + assert.strictEqual(model.getLineContent(1), 'beginend'); + + model.setValue('/*'); + cursor.setSelections('test', [new Selection(1, 3, 1, 3)]); + cursorCommand(cursor, H.Type, { text: '*' }, 'keyboard'); + assert.strictEqual(model.getLineContent(1), '/** */'); + }); + mode.dispose(); + }); + test('issue #55314: Do not auto-close when ending with open', () => { const languageId = new LanguageIdentifier('myElectricMode', 5); class ElectricMode extends MockMode { @@ -4477,7 +4503,7 @@ suite('autoClosingPairs', () => { model.forceTokenization(model.getLineCount()); assertType(model, cursor, 3, 4, '"', '"', `does not double quote when ending with open`); model.forceTokenization(model.getLineCount()); - assertType(model, cursor, 4, 2, '"', '""', `double quote when ending with open`); + assertType(model, cursor, 4, 2, '"', '"', `does not double quote when ending with open`); model.forceTokenization(model.getLineCount()); assertType(model, cursor, 4, 3, '"', '"', `does not double quote when ending with open`); }); @@ -4772,31 +4798,18 @@ suite('autoClosingPairs', () => { // on the mac US intl kb layout - // Typing ` + space + // Typing ' + space cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard'); cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); - assert.equal(model.getValue(), '\'\''); - // Typing " + space within string - cursor.setSelections('test', [new Selection(1, 2, 1, 2)]); - cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); - cursorCommand(cursor, H.Type, { text: '"' }, 'keyboard'); - cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '"' }, 'keyboard'); - cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); - - assert.equal(model.getValue(), '\'"\''); - - // Typing ' + space after ' - model.setValue('\''); - cursor.setSelections('test', [new Selection(1, 2, 1, 2)]); + // Typing one more ' + space cursorCommand(cursor, H.CompositionStart, null, 'keyboard'); cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard'); cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard'); cursorCommand(cursor, H.CompositionEnd, null, 'keyboard'); - assert.equal(model.getValue(), '\'\''); // Typing ' as a closing tag diff --git a/src/vs/editor/test/common/modes/supports/characterPair.test.ts b/src/vs/editor/test/common/modes/supports/characterPair.test.ts index ba9aa15f503b5..3f3f6880450ad 100644 --- a/src/vs/editor/test/common/modes/supports/characterPair.test.ts +++ b/src/vs/editor/test/common/modes/supports/characterPair.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { StandardTokenType } from 'vs/editor/common/modes'; import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair'; import { TokenText, createFakeScopedLineTokens } from 'vs/editor/test/common/modesTestUtils'; +import { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; suite('CharacterPairSupport', () => { @@ -52,8 +53,21 @@ suite('CharacterPairSupport', () => { assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []); }); + function findAutoClosingPair(characterPairSupport: CharacterPairSupport, character: string): StandardAutoClosingPairConditional | null { + for (const autoClosingPair of characterPairSupport.getAutoClosingPairs()) { + if (autoClosingPair.open === character) { + return autoClosingPair; + } + } + return null; + } + function testShouldAutoClose(characterPairSupport: CharacterPairSupport, line: TokenText[], character: string, column: number): boolean { - return characterPairSupport.shouldAutoClosePair(character, createFakeScopedLineTokens(line), column); + const autoClosingPair = findAutoClosingPair(characterPairSupport, character); + if (!autoClosingPair) { + return false; + } + return CharacterPairSupport.shouldAutoClosePair(autoClosingPair, createFakeScopedLineTokens(line), column); } test('shouldAutoClosePair in empty line', () => { diff --git a/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts index 6035379e05a5d..22b818c6b67e9 100644 --- a/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts +++ b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts @@ -21,86 +21,20 @@ suite('Editor Modes - Auto Indentation', () => { assert.deepEqual(actual, null); } - function testAppends(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number, appendText: string): void { - let actual = _testOnElectricCharacter(electricCharacterSupport, line, character, offset); - assert.deepEqual(actual, { appendText: appendText }); - } - function testMatchBracket(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number, matchOpenBracket: string): void { let actual = _testOnElectricCharacter(electricCharacterSupport, line, character, offset); assert.deepEqual(actual, { matchOpenBracket: matchOpenBracket }); } - test('Doc comments', () => { - let brackets = new BracketElectricCharacterSupport(null, [{ open: '/**', close: ' */' }], null); - - testAppends(brackets, [ - { text: '/*', type: StandardTokenType.Other }, - ], '*', 3, ' */'); - - testDoesNothing(brackets, [ - { text: '/*', type: StandardTokenType.Other }, - { text: ' ', type: StandardTokenType.Other }, - { text: '*/', type: StandardTokenType.Other }, - ], '*', 3); - }); - test('getElectricCharacters uses all sources and dedups', () => { let sup = new BracketElectricCharacterSupport( new RichEditBrackets(fakeLanguageIdentifier, [ ['{', '}'], ['(', ')'] - ]), [ - { open: '{', close: '}', notIn: ['string', 'comment'] }, - { open: '"', close: '"', notIn: ['string', 'comment'] }, - { open: 'begin', close: 'end', notIn: ['string'] } - ], - { docComment: { open: '/**', close: ' */' } } - ); - - assert.deepEqual(sup.getElectricCharacters(), ['}', ')', 'n', '*']); - }); - - test('auto-close', () => { - let sup = new BracketElectricCharacterSupport( - new RichEditBrackets(fakeLanguageIdentifier, [ - ['{', '}'], - ['(', ')'] - ]), [ - { open: '{', close: '}', notIn: ['string', 'comment'] }, - { open: '"', close: '"', notIn: ['string', 'comment'] }, - { open: 'begin', close: 'end', notIn: ['string'] } - ], - { docComment: { open: '/**', close: ' */' } } + ]) ); - testDoesNothing(sup, [], 'a', 0); - - testDoesNothing(sup, [{ text: 'egi', type: StandardTokenType.Other }], 'b', 1); - testDoesNothing(sup, [{ text: 'bgi', type: StandardTokenType.Other }], 'e', 2); - testDoesNothing(sup, [{ text: 'bei', type: StandardTokenType.Other }], 'g', 3); - testDoesNothing(sup, [{ text: 'beg', type: StandardTokenType.Other }], 'i', 4); - - testDoesNothing(sup, [{ text: 'egin', type: StandardTokenType.Other }], 'b', 1); - testDoesNothing(sup, [{ text: 'bgin', type: StandardTokenType.Other }], 'e', 2); - testDoesNothing(sup, [{ text: 'bein', type: StandardTokenType.Other }], 'g', 3); - testDoesNothing(sup, [{ text: 'begn', type: StandardTokenType.Other }], 'i', 4); - testAppends(sup, [{ text: 'begi', type: StandardTokenType.Other }], 'n', 5, 'end'); - - testDoesNothing(sup, [{ text: '3gin', type: StandardTokenType.Other }], 'b', 1); - testDoesNothing(sup, [{ text: 'bgin', type: StandardTokenType.Other }], '3', 2); - testDoesNothing(sup, [{ text: 'b3in', type: StandardTokenType.Other }], 'g', 3); - testDoesNothing(sup, [{ text: 'b3gn', type: StandardTokenType.Other }], 'i', 4); - testDoesNothing(sup, [{ text: 'b3gi', type: StandardTokenType.Other }], 'n', 5); - - testDoesNothing(sup, [{ text: 'begi', type: StandardTokenType.String }], 'n', 5); - - testAppends(sup, [{ text: '"', type: StandardTokenType.String }, { text: 'begi', type: StandardTokenType.Other }], 'n', 6, 'end'); - testDoesNothing(sup, [{ text: '"', type: StandardTokenType.String }, { text: 'begi', type: StandardTokenType.String }], 'n', 6); - - testAppends(sup, [{ text: '/*', type: StandardTokenType.String }], '*', 3, ' */'); - - testDoesNothing(sup, [{ text: 'begi', type: StandardTokenType.Other }, { text: 'end', type: StandardTokenType.Other }], 'n', 5); + assert.deepEqual(sup.getElectricCharacters(), ['}', ')']); }); test('matchOpenBracket', () => { @@ -108,12 +42,7 @@ suite('Editor Modes - Auto Indentation', () => { new RichEditBrackets(fakeLanguageIdentifier, [ ['{', '}'], ['(', ')'] - ]), [ - { open: '{', close: '}', notIn: ['string', 'comment'] }, - { open: '"', close: '"', notIn: ['string', 'comment'] }, - { open: 'begin', close: 'end', notIn: ['string'] } - ], - { docComment: { open: '/**', close: ' */' } } + ]) ); testDoesNothing(sup, [{ text: '\t{', type: StandardTokenType.Other }], '\t', 1); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 222b9e8466640..9845081a9e988 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4561,7 +4561,9 @@ declare namespace monaco.languages { * * @deprecated Will be replaced by a better API soon. */ - __electricCharacterSupport?: IBracketElectricCharacterContribution; + __electricCharacterSupport?: { + docComment?: IDocComment; + }; } /** @@ -4636,10 +4638,6 @@ declare namespace monaco.languages { action: EnterAction; } - export interface IBracketElectricCharacterContribution { - docComment?: IDocComment; - } - /** * Definition of documentation comments (e.g. Javadoc/JSdoc) */ From 606987ec3ddc24d5ceefb41d1d0f8fa301d041f2 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 20 Aug 2019 12:35:46 +0200 Subject: [PATCH 386/613] debugSession: respect sortText fixes #78408 --- src/vs/workbench/contrib/debug/browser/debugSession.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 24b9b18c788ab..ccee3cee540c1 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -554,7 +554,8 @@ export class DebugSession implements IDebugSession { insertText: item.text || item.label, kind: completionKindFromString(item.type || 'property'), filterText: (item.start && item.length) ? text.substr(item.start, item.length).concat(item.label) : undefined, - range: Range.fromPositions(position.delta(0, -(item.length || overwriteBefore)), position) + range: Range.fromPositions(position.delta(0, -(item.length || overwriteBefore)), position), + sortText: item.sortText }); } }); From bb384a83e586dda0e42227d55813c7ce744fc99b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 20 Aug 2019 12:51:49 +0200 Subject: [PATCH 387/613] Remove for-in usages (#79432) --- .../common/monarch/monarchCompile.ts | 158 ++++++++---------- .../standalone/common/monarch/monarchLexer.ts | 53 +++--- 2 files changed, 96 insertions(+), 115 deletions(-) diff --git a/src/vs/editor/standalone/common/monarch/monarchCompile.ts b/src/vs/editor/standalone/common/monarch/monarchCompile.ts index f056854e44aab..2c98c6ba429be 100644 --- a/src/vs/editor/standalone/common/monarch/monarchCompile.ts +++ b/src/vs/editor/standalone/common/monarch/monarchCompile.ts @@ -27,11 +27,9 @@ function isArrayOf(elemType: (x: any) => boolean, obj: any): boolean { if (!(Array.isArray(obj))) { return false; } - for (let idx in obj) { - if (obj.hasOwnProperty(idx)) { - if (!(elemType(obj[idx]))) { - return false; - } + for (const el of obj) { + if (!(elemType(el))) { + return false; } } return true; @@ -298,10 +296,8 @@ function compileAction(lexer: monarchCommon.ILexerMin, ruleName: string, action: } else if (Array.isArray(action)) { let results: monarchCommon.FuzzyAction[] = []; - for (let idx in action) { - if (action.hasOwnProperty(idx)) { - results[idx] = compileAction(lexer, ruleName, action[idx]); - } + for (let i = 0, len = action.length; i < len; i++) { + results[i] = compileAction(lexer, ruleName, action[i]); } return { group: results }; } @@ -331,13 +327,10 @@ function compileAction(lexer: monarchCommon.ILexerMin, ruleName: string, action: const def = lexer.defaultToken; return { test: function (id, matches, state, eos) { - for (let idx in cases) { - if (cases.hasOwnProperty(idx)) { - const _case = cases[idx]; - const didmatch = (!_case.test || _case.test(id, matches, state, eos)); - if (didmatch) { - return _case.value; - } + for (const _case of cases) { + const didmatch = (!_case.test || _case.test(id, matches, state, eos)); + if (didmatch) { + return _case.value; } } return def; @@ -425,64 +418,61 @@ export function compile(languageId: string, json: IMonarchLanguage): monarchComm // Compile an array of rules into newrules where RegExp objects are created. function addRules(state: string, newrules: monarchCommon.IRule[], rules: any[]) { - for (let idx in rules) { - if (rules.hasOwnProperty(idx)) { - const rule = rules[idx]; - let include = rule.include; - if (include) { - if (typeof (include) !== 'string') { - throw monarchCommon.createError(lexer, 'an \'include\' attribute must be a string at: ' + state); - } - if (include[0] === '@') { - include = include.substr(1); // peel off starting @ - } - if (!json.tokenizer[include]) { - throw monarchCommon.createError(lexer, 'include target \'' + include + '\' is not defined at: ' + state); - } - addRules(state + '.' + include, newrules, json.tokenizer[include]); - } - else { - const newrule = new Rule(state); + for (const rule of rules) { - - // Set up new rule attributes - if (Array.isArray(rule) && rule.length >= 1 && rule.length <= 3) { - newrule.setRegex(lexerMin, rule[0]); - if (rule.length >= 3) { - if (typeof (rule[1]) === 'string') { - newrule.setAction(lexerMin, { token: rule[1], next: rule[2] }); - } - else if (typeof (rule[1]) === 'object') { - const rule1 = rule[1]; - rule1.next = rule[2]; - newrule.setAction(lexerMin, rule1); - } - else { - throw monarchCommon.createError(lexer, 'a next state as the last element of a rule can only be given if the action is either an object or a string, at: ' + state); - } + let include = rule.include; + if (include) { + if (typeof (include) !== 'string') { + throw monarchCommon.createError(lexer, 'an \'include\' attribute must be a string at: ' + state); + } + if (include[0] === '@') { + include = include.substr(1); // peel off starting @ + } + if (!json.tokenizer[include]) { + throw monarchCommon.createError(lexer, 'include target \'' + include + '\' is not defined at: ' + state); + } + addRules(state + '.' + include, newrules, json.tokenizer[include]); + } + else { + const newrule = new Rule(state); + + // Set up new rule attributes + if (Array.isArray(rule) && rule.length >= 1 && rule.length <= 3) { + newrule.setRegex(lexerMin, rule[0]); + if (rule.length >= 3) { + if (typeof (rule[1]) === 'string') { + newrule.setAction(lexerMin, { token: rule[1], next: rule[2] }); + } + else if (typeof (rule[1]) === 'object') { + const rule1 = rule[1]; + rule1.next = rule[2]; + newrule.setAction(lexerMin, rule1); } else { - newrule.setAction(lexerMin, rule[1]); + throw monarchCommon.createError(lexer, 'a next state as the last element of a rule can only be given if the action is either an object or a string, at: ' + state); } } else { - if (!rule.regex) { - throw monarchCommon.createError(lexer, 'a rule must either be an array, or an object with a \'regex\' or \'include\' field at: ' + state); - } - if (rule.name) { - if (typeof rule.name === 'string') { - newrule.name = rule.name; - } - } - if (rule.matchOnlyAtStart) { - newrule.matchOnlyAtLineStart = bool(rule.matchOnlyAtLineStart, false); + newrule.setAction(lexerMin, rule[1]); + } + } + else { + if (!rule.regex) { + throw monarchCommon.createError(lexer, 'a rule must either be an array, or an object with a \'regex\' or \'include\' field at: ' + state); + } + if (rule.name) { + if (typeof rule.name === 'string') { + newrule.name = rule.name; } - newrule.setRegex(lexerMin, rule.regex); - newrule.setAction(lexerMin, rule.action); } - - newrules.push(newrule); + if (rule.matchOnlyAtStart) { + newrule.matchOnlyAtLineStart = bool(rule.matchOnlyAtLineStart, false); + } + newrule.setRegex(lexerMin, rule.regex); + newrule.setAction(lexerMin, rule.action); } + + newrules.push(newrule); } } } @@ -520,26 +510,24 @@ export function compile(languageId: string, json: IMonarchLanguage): monarchComm { open: '<', close: '>', token: 'delimiter.angle' }]; } let brackets: IMonarchLanguageBracket[] = []; - for (let bracketIdx in json.brackets) { - if (json.brackets.hasOwnProperty(bracketIdx)) { - let desc = json.brackets[bracketIdx]; - if (desc && Array.isArray(desc) && desc.length === 3) { - desc = { token: desc[2], open: desc[0], close: desc[1] }; - } - if (desc.open === desc.close) { - throw monarchCommon.createError(lexer, 'open and close brackets in a \'brackets\' attribute must be different: ' + desc.open + - '\n hint: use the \'bracket\' attribute if matching on equal brackets is required.'); - } - if (typeof desc.open === 'string' && typeof desc.token === 'string' && typeof desc.close === 'string') { - brackets.push({ - token: desc.token + lexer.tokenPostfix, - open: monarchCommon.fixCase(lexer, desc.open), - close: monarchCommon.fixCase(lexer, desc.close) - }); - } - else { - throw monarchCommon.createError(lexer, 'every element in the \'brackets\' array must be a \'{open,close,token}\' object or array'); - } + for (let el of json.brackets) { + let desc: any = el; + if (desc && Array.isArray(desc) && desc.length === 3) { + desc = { token: desc[2], open: desc[0], close: desc[1] }; + } + if (desc.open === desc.close) { + throw monarchCommon.createError(lexer, 'open and close brackets in a \'brackets\' attribute must be different: ' + desc.open + + '\n hint: use the \'bracket\' attribute if matching on equal brackets is required.'); + } + if (typeof desc.open === 'string' && typeof desc.token === 'string' && typeof desc.close === 'string') { + brackets.push({ + token: desc.token + lexer.tokenPostfix, + open: monarchCommon.fixCase(lexer, desc.open), + close: monarchCommon.fixCase(lexer, desc.close) + }); + } + else { + throw monarchCommon.createError(lexer, 'every element in the \'brackets\' array must be a \'{open,close,token}\' object or array'); } } lexer.brackets = brackets; diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts index a5c97ad6f288c..39642a4d6e60c 100644 --- a/src/vs/editor/standalone/common/monarch/monarchLexer.ts +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -228,8 +228,6 @@ class MonarchLineState implements modes.IState { } } -const hasOwnProperty = Object.hasOwnProperty; - interface IMonarchTokensCollector { enterMode(startOffset: number, modeId: string): void; emit(startOffset: number, type: string): void; @@ -423,22 +421,24 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { public getLoadStatus(): ILoadStatus { let promises: Thenable[] = []; for (let nestedModeId in this._embeddedModes) { - const tokenizationSupport = modes.TokenizationRegistry.get(nestedModeId); - if (tokenizationSupport) { - // The nested mode is already loaded - if (tokenizationSupport instanceof MonarchTokenizer) { - const nestedModeStatus = tokenizationSupport.getLoadStatus(); - if (nestedModeStatus.loaded === false) { - promises.push(nestedModeStatus.promise); + if (this._embeddedModes.hasOwnProperty(nestedModeId)) { + const tokenizationSupport = modes.TokenizationRegistry.get(nestedModeId); + if (tokenizationSupport) { + // The nested mode is already loaded + if (tokenizationSupport instanceof MonarchTokenizer) { + const nestedModeStatus = tokenizationSupport.getLoadStatus(); + if (nestedModeStatus.loaded === false) { + promises.push(nestedModeStatus.promise); + } } + continue; } - continue; - } - const tokenizationSupportPromise = modes.TokenizationRegistry.getPromise(nestedModeId); - if (tokenizationSupportPromise) { - // The nested mode is in the process of being loaded - promises.push(tokenizationSupportPromise); + const tokenizationSupportPromise = modes.TokenizationRegistry.getPromise(nestedModeId); + if (tokenizationSupportPromise) { + // The nested mode is in the process of being loaded + promises.push(tokenizationSupportPromise); + } } } @@ -490,11 +490,7 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { let popOffset = -1; let hasEmbeddedPopRule = false; - for (let idx in rules) { - if (!hasOwnProperty.call(rules, idx)) { - continue; - } - let rule: monarchCommon.IRule = rules[idx]; + for (const rule of rules) { if (!monarchCommon.isIAction(rule.action) || rule.action.nextEmbedded !== '@pop') { continue; } @@ -619,16 +615,13 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { // try each rule until we match let restOfLine = line.substr(pos); - for (let idx in rules) { - if (hasOwnProperty.call(rules, idx)) { - let rule: monarchCommon.IRule = rules[idx]; - if (pos === 0 || !rule.matchOnlyAtLineStart) { - matches = restOfLine.match(rule.regex); - if (matches) { - matched = matches[0]; - action = rule.action; - break; - } + for (const rule of rules) { + if (pos === 0 || !rule.matchOnlyAtLineStart) { + matches = restOfLine.match(rule.regex); + if (matches) { + matched = matches[0]; + action = rule.action; + break; } } } From 25c9b4bf6a32ab4f233e5168c5a0d3cbded351cd Mon Sep 17 00:00:00 2001 From: Belskiy Maksim Andreevich Date: Tue, 20 Aug 2019 15:02:32 +0300 Subject: [PATCH 388/613] Polish breadcrumbs icons spacing (#79005) --- .../browser/parts/editor/media/breadcrumbscontrol.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css index d3850e293079c..1e6e29ee935be 100644 --- a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css @@ -17,6 +17,10 @@ text-decoration-line: underline; } +.monaco-workbench .monaco-breadcrumb-item.shows-symbol-icon .symbol-icon.block { + padding-right: 6px; +} + /* todo@joh move somewhere else */ .monaco-workbench .monaco-breadcrumbs-picker .arrow { From ea7f62fa08586faad64007fde11dfe7d281b5f6d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 20 Aug 2019 14:28:46 +0200 Subject: [PATCH 389/613] Fixes #79496: Always go through the protocol handler --- src/vs/base/common/network.ts | 7 ++++--- src/vs/code/browser/workbench/workbench.html | 2 +- src/vs/code/electron-browser/workbench/workbench.html | 2 +- src/vs/code/electron-main/app.ts | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index bccc215360265..9c83d42dc7c8b 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -49,6 +49,8 @@ export namespace Schemas { export const vscodeRemote: string = 'vscode-remote'; + export const vscodeRemoteResource: string = 'vscode-remote-resource'; + export const userData: string = 'vscode-userdata'; } @@ -64,7 +66,7 @@ class RemoteAuthoritiesImpl { } public set(authority: string, host: string, port: number): void { - this._hosts[authority] = (host === 'localhost' ? '127.0.0.1' : host); + this._hosts[authority] = host; this._ports[authority] = port; } @@ -76,9 +78,8 @@ class RemoteAuthoritiesImpl { const host = this._hosts[authority]; const port = this._ports[authority]; const connectionToken = this._connectionTokens[authority]; - const scheme = (host === '127.0.0.1' ? Schemas.http : Schemas.vscodeRemote); return URI.from({ - scheme: scheme, + scheme: Schemas.vscodeRemoteResource, authority: `${host}:${port}`, path: `/vscode-remote2`, query: `path=${encodeURIComponent(path)}&tkn=${encodeURIComponent(connectionToken)}` diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 45a9d6d13fe12..8f8f45fb15f08 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -10,7 +10,7 @@ + content="default-src 'none'; img-src 'self' https: data: blob:; media-src 'none'; frame-src 'self' {{WEBVIEW_ENDPOINT}} https://*.vscode-webview-test.com; script-src 'self' https://az416426.vo.msecnd.net 'unsafe-eval' https:; style-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss: https:; font-src 'self' blob:;"> diff --git a/src/vs/code/electron-browser/workbench/workbench.html b/src/vs/code/electron-browser/workbench/workbench.html index bef3d951473ac..693082bb9edaa 100644 --- a/src/vs/code/electron-browser/workbench/workbench.html +++ b/src/vs/code/electron-browser/workbench/workbench.html @@ -3,7 +3,7 @@ - + diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index d269616e1b9dd..6d43b392dfb5c 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -688,9 +688,9 @@ export class CodeApplication extends Disposable { } private handleRemoteAuthorities(): void { - protocol.registerHttpProtocol(Schemas.vscodeRemote, (request, callback) => { + protocol.registerHttpProtocol(Schemas.vscodeRemoteResource, (request, callback) => { callback({ - url: request.url.replace(/^vscode-remote:/, 'http:'), + url: request.url.replace(/^vscode-remote-resource:/, 'http:'), method: request.method }); }); From a6cc555ddafc34163bc3f1eb588004199ca680e3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Aug 2019 14:38:01 +0200 Subject: [PATCH 390/613] window.open throws --- src/vs/workbench/electron-browser/window.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index a9c2344af12a5..7f13270ca830b 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -359,9 +359,7 @@ export class ElectronWindow extends Disposable { // Block window.open() calls const $this = this; window.open = function (): Window | null { - console.error(new Error('Prevented call to window.open(). Use IOpenerService instead!')); - - return null; + throw new Error('Prevented call to window.open(). Use IOpenerService instead!'); }; // Handle internal open() calls From 63fe017c5a4af7db96b78d7bd9ccc762ece02816 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 20 Aug 2019 14:38:42 +0200 Subject: [PATCH 391/613] make extensionService fit for running without remote connection --- .../extensions/browser/extensionService.ts | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 59e7baf1a17b5..e76e4044f5fc8 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -27,6 +27,7 @@ import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browse import { Schemas } from 'vs/base/common/network'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; +import { DeltaExtensionsResult } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -81,21 +82,22 @@ export class ExtensionService extends AbstractExtensionService implements IExten }; } - protected _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { + protected _createExtensionHosts(_isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { const result: ExtensionHostProcessManager[] = []; - const remoteAgentConnection = this._remoteAgentService.getConnection()!; + const remoteAgentConnection = this._remoteAgentService.getConnection(); const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => isWebExtension(ext, this._configService))); - const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !isWebExtension(ext, this._configService))); - const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.parse('empty:value')); //todo@joh - const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); + const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, remoteAgentConnection ? remoteAgentConnection.remoteAuthority : null, initialActivationEvents); result.push(webHostProcessManager); - const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); - const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); - result.push(remoteExtHostProcessManager); + if (remoteAgentConnection) { + const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !isWebExtension(ext, this._configService))); + const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); + const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); + result.push(remoteExtHostProcessManager); + } return result; } @@ -103,27 +105,35 @@ export class ExtensionService extends AbstractExtensionService implements IExten protected async _scanAndHandleExtensions(): Promise { // fetch the remote environment let [remoteEnv, localExtensions] = await Promise.all([ - >this._remoteAgentService.getEnvironment(), + this._remoteAgentService.getEnvironment(), this._staticExtensions.getExtensions() ]); + let result: DeltaExtensionsResult; + // local: only enabled and web'ish extension localExtensions = localExtensions.filter(ext => this._isEnabled(ext) && isWebExtension(ext, this._configService)); this._checkEnableProposedApi(localExtensions); - // remote: only enabled and none-web'ish extension - remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !isWebExtension(extension, this._configService)); - this._checkEnableProposedApi(remoteEnv.extensions); + if (!remoteEnv) { + result = this._registry.deltaExtensions(localExtensions, []); - // in case of overlap, the remote wins - const isRemoteExtension = new Set(); - remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier))); - localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier))); + } else { + // remote: only enabled and none-web'ish extension + remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !isWebExtension(extension, this._configService)); + this._checkEnableProposedApi(remoteEnv.extensions); - // save for remote extension's init data - this._remoteExtensionsEnvironmentData = remoteEnv; + // in case of overlap, the remote wins + const isRemoteExtension = new Set(); + remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier))); + localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier))); + + // save for remote extension's init data + this._remoteExtensionsEnvironmentData = remoteEnv; + + result = this._registry.deltaExtensions(remoteEnv.extensions.concat(localExtensions), []); + } - const result = this._registry.deltaExtensions(remoteEnv.extensions.concat(localExtensions), []); if (result.removedDueToLooping.length > 0) { this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); } From 26a8fa55d40a7748678b61b5e0aee3e2671d3275 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 20 Aug 2019 15:12:41 +0200 Subject: [PATCH 392/613] debug: data breakpoints that can not be persisted should be cleared when a session ends --- src/vs/workbench/contrib/debug/browser/debugService.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 8b139d2a3e3ff..7a0b0fe82c497 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -404,7 +404,7 @@ export class DebugService implements IDebugService { .then(() => false); } - return launch && launch.openConfigFile(false, true, undefined, this.initCancellationToken ? this.initCancellationToken.token : undefined).then(() => false); + return !!launch && launch.openConfigFile(false, true, undefined, this.initCancellationToken ? this.initCancellationToken.token : undefined).then(() => false); }); } @@ -542,6 +542,10 @@ export class DebugService implements IDebugService { if (this.layoutService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getValue('debug').openExplorerOnEnd) { this.viewletService.openViewlet(EXPLORER_VIEWLET_ID); } + + // Data breakpoints that can not be persisted should be cleared when a session ends + const dataBreakpoints = this.model.getDataBreakpoints().filter(dbp => !dbp.canPersist); + dataBreakpoints.forEach(dbp => this.model.removeDataBreakpoints(dbp.getId())); } })); From ba00f5ff80cfdc7ae94c3bc74b897a9767ffd018 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 20 Aug 2019 15:14:55 +0200 Subject: [PATCH 393/613] Folding last item inside #region hides all blank lines up to #endregion Fixes #79359 --- .../contrib/folding/indentRangeProvider.ts | 31 ++++++++++++------- .../folding/test/indentRangeProvider.test.ts | 14 ++++++++- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/contrib/folding/indentRangeProvider.ts b/src/vs/editor/contrib/folding/indentRangeProvider.ts index f13ed98e9af08..68a3a366faad9 100644 --- a/src/vs/editor/contrib/folding/indentRangeProvider.ts +++ b/src/vs/editor/contrib/folding/indentRangeProvider.ts @@ -105,7 +105,11 @@ export class RangesCollector { } -interface PreviousRegion { indent: number; line: number; marker: boolean; } +interface PreviousRegion { + indent: number; // indent or -2 if a marker + endAbove: number; // end line number for the region above + line: number; // start line of the region. Only used for marker regions. +} export function computeRanges(model: ITextModel, offSide: boolean, markers?: FoldingMarkers, foldingRangesLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT): FoldingRegions { const tabSize = model.getOptions().tabSize; @@ -117,16 +121,19 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol } let previousRegions: PreviousRegion[] = []; - previousRegions.push({ indent: -1, line: model.getLineCount() + 1, marker: false }); // sentinel, to make sure there's at least one entry + let line = model.getLineCount() + 1; + previousRegions.push({ indent: -1, endAbove: line, line }); // sentinel, to make sure there's at least one entry for (let line = model.getLineCount(); line > 0; line--) { let lineContent = model.getLineContent(line); let indent = TextModel.computeIndentLevel(lineContent, tabSize); let previous = previousRegions[previousRegions.length - 1]; if (indent === -1) { - if (offSide && !previous.marker) { - // for offSide languages, empty lines are associated to the next block - previous.line = line; + if (offSide) { + // for offSide languages, empty lines are associated to the previous block + // note: the next block is already written to the results, so this only + // impacts the end position of the block before + previous.endAbove = line; } continue; // only whitespace } @@ -136,7 +143,7 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol if (m[1]) { // start pattern match // discard all regions until the folding pattern let i = previousRegions.length - 1; - while (i > 0 && !previousRegions[i].marker) { + while (i > 0 && previousRegions[i].indent !== -2) { i--; } if (i > 0) { @@ -145,15 +152,15 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol // new folding range from pattern, includes the end line result.insertFirst(line, previous.line, indent); - previous.marker = false; - previous.indent = indent; previous.line = line; + previous.indent = indent; + previous.endAbove = line; continue; } else { // no end marker found, treat line as a regular line } } else { // end pattern match - previousRegions.push({ indent: -2, line, marker: true }); + previousRegions.push({ indent: -2, endAbove: line, line }); continue; } } @@ -165,16 +172,16 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol } while (previous.indent > indent); // new folding range - let endLineNumber = previous.line - 1; + let endLineNumber = previous.endAbove - 1; if (endLineNumber - line >= 1) { // needs at east size 1 result.insertFirst(line, endLineNumber, indent); } } if (previous.indent === indent) { - previous.line = line; + previous.endAbove = line; } else { // previous.indent < indent // new region with a bigger indent - previousRegions.push({ indent, line, marker: false }); + previousRegions.push({ indent, endAbove: line, line }); } } return result.toIndentRanges(model); diff --git a/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts b/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts index 882c097104dc6..07e2025666796 100644 --- a/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts +++ b/src/vs/editor/contrib/folding/test/indentRangeProvider.test.ts @@ -316,5 +316,17 @@ suite('Folding with regions', () => { /* 8*/ '#endregionff', ], [], true, markers); }); - + test('Issue 79359', () => { + assertRanges([ + /* 1*/ '#region', + /* 2*/ '', + /* 3*/ 'class A', + /* 4*/ ' foo', + /* 5*/ '', + /* 6*/ 'class A', + /* 7*/ ' foo', + /* 8*/ '', + /* 9*/ '#endregion', + ], [r(1, 9, -1, true), r(3, 4, 0), r(6, 7, 0)], true, markers); + }); }); From e1f96ca53fb2e7210fdb2008c64414cf1b2d5917 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 20 Aug 2019 15:35:15 +0200 Subject: [PATCH 394/613] debug: refresh variables when selection added to repl fixes #78299 --- .../contrib/debug/browser/debugEditorActions.ts | 7 +++---- .../workbench/contrib/debug/browser/debugSession.ts | 12 +++++++----- src/vs/workbench/contrib/debug/browser/repl.ts | 3 +-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index bf802266912d4..b7f89249ad911 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -169,7 +169,7 @@ class SelectionToReplAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const debugService = accessor.get(IDebugService); const panelService = accessor.get(IPanelService); const viewModel = debugService.getViewModel(); @@ -179,9 +179,8 @@ class SelectionToReplAction extends EditorAction { } const text = editor.getModel().getValueInRange(editor.getSelection()); - return session.addReplExpression(viewModel.focusedStackFrame!, text) - .then(() => panelService.openPanel(REPL_ID, true)) - .then(_ => undefined); + await session.addReplExpression(viewModel.focusedStackFrame!, text); + await panelService.openPanel(REPL_ID, true); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index ccee3cee540c1..01151248238c7 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -32,6 +32,7 @@ import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel'; import { onUnexpectedError } from 'vs/base/common/errors'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView'; export class DebugSession implements IDebugSession { @@ -906,12 +907,13 @@ export class DebugSession implements IDebugSession { this._onDidChangeREPLElements.fire(); } - addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise { + async addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise { const viewModel = this.debugService.getViewModel(); - return this.repl.addReplExpression(stackFrame, name) - .then(() => this._onDidChangeREPLElements.fire()) - // Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some. - .then(() => this.debugService.focusStackFrame(viewModel.focusedStackFrame, viewModel.focusedThread, viewModel.focusedSession)); + await this.repl.addReplExpression(stackFrame, name); + this._onDidChangeREPLElements.fire(); + // Evaluate all watch expressions and fetch variables again since repl evaluation might have changed some. + this.debugService.focusStackFrame(viewModel.focusedStackFrame, viewModel.focusedThread, viewModel.focusedSession); + variableSetEmitter.fire(); } appendToRepl(data: string | IExpression, severity: severity, source?: IReplElementSource): void { diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 2328778efd0b0..0fe9ac036b149 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -62,7 +62,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { VariablesRenderer, variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView'; +import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView'; const $ = dom.$; @@ -301,7 +301,6 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati revealLastElement(this.tree); this.history.add(this.replInput.getValue()); this.replInput.setValue(''); - variableSetEmitter.fire(); const shouldRelayout = this.replInputHeight > Repl.REPL_INPUT_INITIAL_HEIGHT; this.replInputHeight = Repl.REPL_INPUT_INITIAL_HEIGHT; if (shouldRelayout) { From 71c1fe1f3fd7d9c3b9dbb8abcedd370605659de4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Aug 2019 15:39:22 +0200 Subject: [PATCH 395/613] fix #79469 --- src/vs/workbench/browser/labels.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 7216a12b82f5b..35a19a119b411 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -321,14 +321,17 @@ class ResourceLabelWidget extends IconLabel { } setResource(label: IResourceLabelProps, options?: IResourceLabelOptions): void { + const hasPathLabelChanged = this.hasPathLabelChanged(label, options); + const clearIconCache = this.clearIconCache(label, options); + this.label = label; this.options = options; - if (this.hasPathLabelChanged(label, options)) { + if (hasPathLabelChanged) { this.computedPathLabel = undefined; // reset path label due to resource change } - this.render(this.clearIconCache(label, options)); + this.render(clearIconCache); } private clearIconCache(newLabel: IResourceLabelProps, newOptions?: IResourceLabelOptions): boolean { @@ -457,6 +460,7 @@ class ResourceLabelWidget extends IconLabel { } iconLabelOptions.extraClasses = this.computedIconClasses.slice(0); } + if (this.options && this.options.extraClasses) { iconLabelOptions.extraClasses!.push(...this.options.extraClasses); } From d746dccedab9233d24f1cac2e131e8aca8b4d263 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 20 Aug 2019 15:55:56 +0200 Subject: [PATCH 396/613] fix #79501 --- .../browser/parts/notifications/media/notificationsActions.css | 2 +- .../browser/parts/notifications/media/notificationsList.css | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsActions.css b/src/vs/workbench/browser/parts/notifications/media/notificationsActions.css index d5b23828af9ab..7f59f8471fb2f 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsActions.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsActions.css @@ -66,4 +66,4 @@ .vs .monaco-workbench > .notifications-center > .notifications-center-header .hide-all-notifications-action { background-image: url('tree-expanded-light.svg'); -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css index f97e52b3507f8..719b6265b0054 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css @@ -91,6 +91,7 @@ .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-toolbar-container { display: none; + height: 22px; } .monaco-workbench .notifications-list-container .notification-list-item:hover .notification-list-item-toolbar-container, @@ -142,4 +143,4 @@ .monaco-workbench .notifications-list-container .progress-bit { height: 2px; bottom: 0; -} \ No newline at end of file +} From 3d5238589ece772a028a79611422a92f0f6caea7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 20 Aug 2019 16:06:50 +0200 Subject: [PATCH 397/613] debounce outline requests based on average reply duration, #77096 --- .../contrib/documentSymbols/outlineModel.ts | 48 +++++++++++++++++-- .../browser/parts/editor/breadcrumbsModel.ts | 11 ++++- .../contrib/outline/browser/outlinePanel.ts | 3 +- 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index 180b0d5a55c0a..e5004629993f1 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -202,16 +202,33 @@ export class OutlineGroup extends TreeElement { } } +class MovingAverage { + + private _n = 1; + private _val = 0; + + update(value: number): this { + this._val = this._val + (value - this._val) / this._n; + this._n += 1; + return this; + } + + get value(): number { + return this._val; + } +} + export class OutlineModel extends TreeElement { + private static readonly _requestDurations = new LRUCache(50, 0.7); private static readonly _requests = new LRUCache, model: OutlineModel | undefined }>(9, 0.75); private static readonly _keys = new class { private _counter = 1; private _data = new WeakMap(); - for(textModel: ITextModel): string { - return `${textModel.id}/${textModel.getVersionId()}/${this._hash(DocumentSymbolProviderRegistry.all(textModel))}`; + for(textModel: ITextModel, version: boolean): string { + return `${textModel.id}/${version ? textModel.getVersionId() : ''}/${this._hash(DocumentSymbolProviderRegistry.all(textModel))}`; } private _hash(providers: DocumentSymbolProvider[]): string { @@ -231,7 +248,7 @@ export class OutlineModel extends TreeElement { static create(textModel: ITextModel, token: CancellationToken): Promise { - let key = this._keys.for(textModel); + let key = this._keys.for(textModel, true); let data = OutlineModel._requests.get(key); if (!data) { @@ -243,6 +260,18 @@ export class OutlineModel extends TreeElement { model: undefined, }; OutlineModel._requests.set(key, data); + + // keep moving average of request durations + const now = Date.now(); + data.promise.then(() => { + let key = this._keys.for(textModel, false); + let avg = this._requestDurations.get(key); + if (!avg) { + avg = new MovingAverage(); + this._requestDurations.set(key, avg); + } + avg.update(Date.now() - now); + }); } if (data!.model) { @@ -272,7 +301,18 @@ export class OutlineModel extends TreeElement { }); } - static _create(textModel: ITextModel, token: CancellationToken): Promise { + static getRequestDelay(textModel: ITextModel | null): number { + if (!textModel) { + return 350; + } + const avg = this._requestDurations.get(this._keys.for(textModel, false)); + if (!avg) { + return 350; + } + return Math.max(350, Math.floor(1.3 * avg.value)); + } + + private static _create(textModel: ITextModel, token: CancellationToken): Promise { const cts = new CancellationTokenSource(token); const result = new OutlineModel(textModel); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index 1c4f92cd45982..43faa87a61797 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -133,11 +133,18 @@ export class EditorBreadcrumbsModel { if (!this._editor) { return; } - // update as model changes + // update as language, model, providers changes this._disposables.push(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline())); this._disposables.push(this._editor.onDidChangeModel(_ => this._updateOutline())); this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._updateOutline())); - this._disposables.push(Event.debounce(this._editor.onDidChangeModelContent, _ => _, 350)(_ => this._updateOutline(true))); + + // update soon'ish as model content change + const updateSoon = new TimeoutTimer(); + this._disposables.push(updateSoon); + this._disposables.push(this._editor.onDidChangeModelContent(_ => { + const timeout = OutlineModel.getRequestDelay(this._editor!.getModel()); + updateSoon.cancelAndSet(() => this._updateOutline(true), timeout); + })); this._updateOutline(); // stop when editor dies diff --git a/src/vs/workbench/contrib/outline/browser/outlinePanel.ts b/src/vs/workbench/contrib/outline/browser/outlinePanel.ts index 001525543496f..90dfc9732bf68 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePanel.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePanel.ts @@ -122,7 +122,8 @@ class RequestOracle { let handle: any; let contentListener = codeEditor.onDidChangeModelContent(event => { clearTimeout(handle); - handle = setTimeout(() => this._callback(codeEditor!, event), 350); + const timeout = OutlineModel.getRequestDelay(codeEditor!.getModel()); + handle = setTimeout(() => this._callback(codeEditor!, event), timeout); }); let modeListener = codeEditor.onDidChangeModelLanguage(_ => { this._callback(codeEditor!, undefined); From ed576d082aa9b8a8f252db5ccb0361898541aec0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 20 Aug 2019 16:19:56 +0200 Subject: [PATCH 398/613] no remote authority for worker process manager --- .../services/extensions/browser/extensionService.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index e76e4044f5fc8..78076e314808d 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -85,13 +85,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten protected _createExtensionHosts(_isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { const result: ExtensionHostProcessManager[] = []; - const remoteAgentConnection = this._remoteAgentService.getConnection(); - const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => isWebExtension(ext, this._configService))); const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.parse('empty:value')); //todo@joh - const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, remoteAgentConnection ? remoteAgentConnection.remoteAuthority : null, initialActivationEvents); + const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents); result.push(webHostProcessManager); + const remoteAgentConnection = this._remoteAgentService.getConnection(); if (remoteAgentConnection) { const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !isWebExtension(ext, this._configService))); const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); From bab821e05833a2136f8243a867faeb11db288ba8 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 20 Aug 2019 16:24:04 +0200 Subject: [PATCH 399/613] Make custom tree view registration wait until extensions are registered --- .../api/browser/mainThreadTreeViews.ts | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 3e42831d23f70..3c2e1c7080177 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -11,6 +11,7 @@ import { distinct } from 'vs/base/common/arrays'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { isUndefinedOrNull, isNumber } from 'vs/base/common/types'; import { Registry } from 'vs/platform/registry/common/platform'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadTreeViews) export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape { @@ -21,25 +22,28 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie constructor( extHostContext: IExtHostContext, @IViewsService private readonly viewsService: IViewsService, - @INotificationService private readonly notificationService: INotificationService + @INotificationService private readonly notificationService: INotificationService, + @IExtensionService private readonly extensionService: IExtensionService ) { super(); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews); } $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 { - this.notificationService.error('No view is registered with id: ' + treeViewId); - } + this.extensionService.whenInstalledExtensionsRegistered().then(() => { + 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 { + this.notificationService.error('No view is registered with id: ' + treeViewId); + } + }); } $reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise { From c763a0e5bc519e0728b4b956dba2380e458faf50 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 20 Aug 2019 16:47:56 +0200 Subject: [PATCH 400/613] goto line announce column #78220 --- .../contrib/quickopen/browser/gotoLineHandler.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts b/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts index 724509b2d055a..19e76cbf3b78e 100644 --- a/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/gotoLineHandler.ts @@ -99,18 +99,17 @@ class GotoLineEntry extends EditorQuickOpenEntry { if (this.editorService.activeTextEditorWidget && this.invalidRange(maxLineNumber)) { const position = this.editorService.activeTextEditorWidget.getPosition(); if (position) { - const currentLine = position.lineNumber; if (maxLineNumber > 0) { - return nls.localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}. Type a line number between 1 and {1} to navigate to.", currentLine, maxLineNumber); + return nls.localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}, Column: {1}. Type a line number between 1 and {2} to navigate to.", position.lineNumber, position.column, maxLineNumber); } - return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine); + return nls.localize('gotoLineLabelEmpty', "Current Line: {0}, Column: {1}. Type a line number to navigate to.", position.lineNumber, position.column); } } // Input valid, indicate action - return this.column ? nls.localize('gotoLineColumnLabel', "Go to line {0} and character {1}.", this.line, this.column) : nls.localize('gotoLineLabel', "Go to line {0}.", this.line); + return this.column ? nls.localize('gotoLineColumnLabel', "Go to line {0} and column {1}.", this.line, this.column) : nls.localize('gotoLineLabel', "Go to line {0}.", this.line); } private invalidRange(maxLineNumber: number = this.getMaxLineNumber()): boolean { @@ -229,8 +228,7 @@ export class GotoLineHandler extends QuickOpenHandler { if (this.editorService.activeTextEditorWidget) { const position = this.editorService.activeTextEditorWidget.getPosition(); if (position) { - const currentLine = position.lineNumber; - return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine); + return nls.localize('gotoLineLabelEmpty', "Current Line: {0}, Column: {1}. Type a line number to navigate to.", position.lineNumber, position.column); } } From 0cec0816d0c69782c402d20d91ab6a62626be00f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 20 Aug 2019 17:09:46 +0200 Subject: [PATCH 401/613] debt - use DisposableStore --- .../browser/parts/editor/breadcrumbsModel.ts | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index 43faa87a61797..73b3e5f76646f 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -9,7 +9,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { size } from 'vs/base/common/collections'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { isEqual, dirname } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -36,14 +36,14 @@ type FileInfo = { path: FileElement[], folder?: IWorkspaceFolder }; export class EditorBreadcrumbsModel { - private readonly _disposables: IDisposable[] = []; + private readonly _disposables = new DisposableStore(); private readonly _fileInfo: FileInfo; private readonly _cfgFilePath: BreadcrumbsConfig<'on' | 'off' | 'last'>; private readonly _cfgSymbolPath: BreadcrumbsConfig<'on' | 'off' | 'last'>; private _outlineElements: Array = []; - private _outlineDisposables: IDisposable[] = []; + private _outlineDisposables = new DisposableStore(); private _onDidUpdate = new Emitter(); readonly onDidUpdate: Event = this._onDidUpdate.event; @@ -58,8 +58,8 @@ export class EditorBreadcrumbsModel { this._cfgFilePath = BreadcrumbsConfig.FilePath.bindTo(configurationService); this._cfgSymbolPath = BreadcrumbsConfig.SymbolPath.bindTo(configurationService); - this._disposables.push(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this))); - this._disposables.push(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this))); + this._disposables.add(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this))); + this._disposables.add(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this))); this._fileInfo = EditorBreadcrumbsModel._initFilePathInfo(this._uri, workspaceService); this._bindToEditor(); @@ -69,7 +69,7 @@ export class EditorBreadcrumbsModel { dispose(): void { this._cfgFilePath.dispose(); this._cfgSymbolPath.dispose(); - dispose(this._disposables); + this._disposables.dispose(); } isRelative(): boolean { @@ -134,26 +134,26 @@ export class EditorBreadcrumbsModel { return; } // update as language, model, providers changes - this._disposables.push(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline())); - this._disposables.push(this._editor.onDidChangeModel(_ => this._updateOutline())); - this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._updateOutline())); + this._disposables.add(DocumentSymbolProviderRegistry.onDidChange(_ => this._updateOutline())); + this._disposables.add(this._editor.onDidChangeModel(_ => this._updateOutline())); + this._disposables.add(this._editor.onDidChangeModelLanguage(_ => this._updateOutline())); // update soon'ish as model content change const updateSoon = new TimeoutTimer(); - this._disposables.push(updateSoon); - this._disposables.push(this._editor.onDidChangeModelContent(_ => { + this._disposables.add(updateSoon); + this._disposables.add(this._editor.onDidChangeModelContent(_ => { const timeout = OutlineModel.getRequestDelay(this._editor!.getModel()); updateSoon.cancelAndSet(() => this._updateOutline(true), timeout); })); this._updateOutline(); // stop when editor dies - this._disposables.push(this._editor.onDidDispose(() => this._outlineDisposables = dispose(this._outlineDisposables))); + this._disposables.add(this._editor.onDidDispose(() => this._outlineDisposables.clear())); } private _updateOutline(didChangeContent?: boolean): void { - this._outlineDisposables = dispose(this._outlineDisposables); + this._outlineDisposables.clear(); if (!didChangeContent) { this._updateOutlineElements([]); } @@ -169,7 +169,7 @@ export class EditorBreadcrumbsModel { const versionIdThen = buffer.getVersionId(); const timeout = new TimeoutTimer(); - this._outlineDisposables.push({ + this._outlineDisposables.add({ dispose: () => { source.cancel(); source.dispose(); @@ -187,7 +187,7 @@ export class EditorBreadcrumbsModel { model = model.adopt(); this._updateOutlineElements(this._getOutlineElements(model, editor.getPosition())); - this._outlineDisposables.push(editor.onDidChangeCursorPosition(_ => { + this._outlineDisposables.add(editor.onDidChangeCursorPosition(_ => { timeout.cancelAndSet(() => { if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId() && editor.getModel()) { this._updateOutlineElements(this._getOutlineElements(model, editor.getPosition())); From d0e60e01add52d2ed931be5e17d0a3f1d09a8805 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 20 Aug 2019 17:30:57 +0200 Subject: [PATCH 402/613] fixes #77063 --- src/vs/workbench/contrib/debug/browser/repl.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 0fe9ac036b149..96bb2f8da9da5 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -444,6 +444,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati private createReplInput(container: HTMLElement): void { this.replInputContainer = dom.append(container, $('.repl-input-wrapper')); + this.replInputContainer.title = nls.localize('debugConsole', "Debug Console"); const { scopedContextKeyService, historyNavigationEnablement } = createAndBindHistoryNavigationWidgetScopedContextKeyService(this.contextKeyService, { target: this.replInputContainer, historyNavigator: this }); this.historyNavigationEnablement = historyNavigationEnablement; From 78d48a09cda49d4e4fc6462432457a4a21986867 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 20 Aug 2019 17:37:29 +0200 Subject: [PATCH 403/613] Get recently opened --- src/vs/workbench/api/common/apiCommands.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index 0c5b098cd6aff..28bbc68d9bfc5 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -175,6 +175,11 @@ CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async functio return windowService.addRecentlyOpened([recent]); }); +CommandsRegistry.registerCommand('_workbench.getRecentlyOpened', async function (accessor: ServicesAccessor) { + const windowService = accessor.get(IWindowService); + return windowService.getRecentlyOpened(); +}); + export class SetEditorLayoutAPICommand { public static ID = 'vscode.setEditorLayout'; public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Promise { @@ -205,4 +210,4 @@ CommandsRegistry.registerCommand({ } }] } -}); \ No newline at end of file +}); From f9ee50e910f10a2259098b6ee992a4cdd7051b1d Mon Sep 17 00:00:00 2001 From: skprabhanjan Date: Tue, 20 Aug 2019 21:16:13 +0530 Subject: [PATCH 404/613] Added try catch for CustomEvent treated as MouseEvent --- .../electron-browser/webviewElement.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 68ad466ee19ed..ce80556e0d67d 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -327,12 +327,19 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { { const rawEvent = event.args[0]; const bounds = this._webview.getBoundingClientRect(); - window.dispatchEvent(new MouseEvent(rawEvent.type, { - ...rawEvent, - clientX: rawEvent.clientX + bounds.left, - clientY: rawEvent.clientY + bounds.top, - })); - return; + try { + window.dispatchEvent(new MouseEvent(rawEvent.type, { + ...rawEvent, + clientX: rawEvent.clientX + bounds.left, + clientY: rawEvent.clientY + bounds.top, + })); + return; + } + catch (TypeError) { + //CustomEvent was treated as MouseEvent - https://github.com/microsoft/vscode/issues/78915 + return; + } + } case 'did-set-content': From 3dcdba53d62f7fa37a7c51a09202903dff8ad577 Mon Sep 17 00:00:00 2001 From: skprabhanjan Date: Tue, 20 Aug 2019 21:22:31 +0530 Subject: [PATCH 405/613] Changed comment message --- .../contrib/webview/electron-browser/webviewElement.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index ce80556e0d67d..830fd2effc878 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -336,7 +336,7 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { return; } catch (TypeError) { - //CustomEvent was treated as MouseEvent - https://github.com/microsoft/vscode/issues/78915 + // CustomEvent was treated as MouseEvent so don't do anything - https://github.com/microsoft/vscode/issues/78915 return; } From 0f7db00b939f9c28ccf9fab8c9d2eb12165c714e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 20 Aug 2019 09:45:54 -0700 Subject: [PATCH 406/613] Add experimental terminal title event option This lets you use \[\033]0;\w\a\] in PS1 to set the dropdown value to the cwd for example Part of #22325 --- .../terminal/browser/terminal.contribution.ts | 5 ++ .../terminal/browser/terminalActions.ts | 4 +- .../terminal/browser/terminalInstance.ts | 58 +++++++++++++------ .../contrib/terminal/common/terminal.ts | 12 +++- .../contrib/terminal/node/terminalRemote.ts | 4 +- .../terminal/node/windowsShellHelper.ts | 4 +- 6 files changed, 61 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 2a343f63b0588..99c526eedcac0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -298,6 +298,11 @@ configurationRegistry.registerConfiguration({ description: nls.localize('terminal.integrated.experimentalRefreshOnResume', "An experimental setting that will refresh the terminal renderer when the system is resumed."), type: 'boolean', default: false + }, + 'terminal.integrated.experimentalUseTitleEvent': { + description: nls.localize('terminal.integrated.experimentalUseTitleEvent', "An experimental setting that will use the terminal title event for the dropdown title. This setting will only apply to new terminals."), + type: 'boolean', + default: false } } }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 4d07f85dc15aa..2492bd5804124 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -1034,7 +1034,7 @@ export class RenameTerminalAction extends Action { prompt: nls.localize('workbench.action.terminal.rename.prompt', "Enter terminal name"), }).then(name => { if (name) { - terminalInstance.setTitle(name, false); + terminalInstance.setTitle(name, TitleEventSource.Api); } }); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 9ca4fa8fb71a0..9ab3aa2138440 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -25,7 +25,7 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/terminalWidgetManager'; -import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, INavigationMode, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; @@ -973,11 +973,22 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._processManager.onProcessResolvedShellLaunchConfig(e => this._setResolvedShellLaunchConfig(e)); if (this._shellLaunchConfig.name) { - this.setTitle(this._shellLaunchConfig.name, false); + this.setTitle(this._shellLaunchConfig.name, TitleEventSource.Api); } else { // Only listen for process title changes when a name is not provided - this.setTitle(this._shellLaunchConfig.executable, true); - this._messageTitleDisposable = this._processManager.onProcessTitle(title => this.setTitle(title ? title : '', true)); + if (this._configHelper.config.experimentalUseTitleEvent) { + this._processManager.ptyProcessReady.then(() => { + this._terminalInstanceService.getDefaultShellAndArgs(false).then(e => { + this.setTitle(e.shell, TitleEventSource.Sequence); + }); + this._xtermReadyPromise.then(xterm => { + this._messageTitleDisposable = xterm.onTitleChange(e => this._onTitleChange(e)); + }); + }); + } else { + this.setTitle(this._shellLaunchConfig.executable, TitleEventSource.Process); + this._messageTitleDisposable = this._processManager.onProcessTitle(title => this.setTitle(title ? title : '', TitleEventSource.Process)); + } } if (platform.isWindows) { @@ -1149,7 +1160,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._createProcess(); if (oldTitle !== this._title) { - this.setTitle(this._title, true); + this.setTitle(this._title, TitleEventSource.Process); } this._processManager.onProcessData(data => this._onProcessData(data)); @@ -1168,6 +1179,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._sendLineData(buffer, buffer.baseY + buffer.cursorY); } + private _onTitleChange(title: string): void { + if (this.isTitleSetByProcess) { + this.setTitle(title, TitleEventSource.Sequence); + } + } + private _sendLineData(buffer: IBuffer, lineIndex: number): void { let line = buffer.getLine(lineIndex); if (!line) { @@ -1348,23 +1365,26 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._processManager.ptyProcessReady.then(() => this._processManager.setDimensions(cols, rows)); } - public setTitle(title: string | undefined, eventFromProcess: boolean): void { + public setTitle(title: string | undefined, eventSource: TitleEventSource): void { if (!title) { return; } - if (eventFromProcess) { - title = path.basename(title); - if (platform.isWindows) { - // Remove the .exe extension - title = title.split('.exe')[0]; - } - } else { - // If the title has not been set by the API or the rename command, unregister the handler that - // automatically updates the terminal name - dispose(this._messageTitleDisposable); - this._messageTitleDisposable = undefined; - dispose(this._windowsShellHelper); - this._windowsShellHelper = undefined; + switch (eventSource) { + case TitleEventSource.Process: + title = path.basename(title); + if (platform.isWindows) { + // Remove the .exe extension + title = title.split('.exe')[0]; + } + break; + case TitleEventSource.Api: + // If the title has not been set by the API or the rename command, unregister the handler that + // automatically updates the terminal name + dispose(this._messageTitleDisposable); + this._messageTitleDisposable = undefined; + dispose(this._windowsShellHelper); + this._windowsShellHelper = undefined; + break; } const didTitleChange = title !== this._title; this._title = title; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 4383fb9c82bb1..48698da20ee6e 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -117,6 +117,7 @@ export interface ITerminalConfiguration { splitCwd: 'workspaceRoot' | 'initial' | 'inherited'; windowsEnableConpty: boolean; experimentalRefreshOnResume: boolean; + experimentalUseTitleEvent: boolean; } export interface ITerminalConfigHelper { @@ -636,7 +637,7 @@ export interface ITerminalInstance { /** * Sets the title of the terminal instance. */ - setTitle(title: string, eventFromProcess: boolean): void; + setTitle(title: string, eventSource: TitleEventSource): void; waitForTitle(): Promise; @@ -769,6 +770,15 @@ export enum LinuxDistro { Unknown } +export enum TitleEventSource { + /** From the API or the rename command that overrides any other type */ + Api, + /** From the process name property*/ + Process, + /** From the VT sequence */ + Sequence +} + export interface IWindowsShellHelper extends IDisposable { getShellName(): Promise; } diff --git a/src/vs/workbench/contrib/terminal/node/terminalRemote.ts b/src/vs/workbench/contrib/terminal/node/terminalRemote.ts index e264cdfdd5d5d..3ebea6304cf6e 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalRemote.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalRemote.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { TERMINAL_ACTION_CATEGORY, ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_ACTION_CATEGORY, ITerminalService, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { Action } from 'vs/base/common/actions'; import { URI } from 'vs/base/common/uri'; @@ -39,7 +39,7 @@ export class CreateNewLocalTerminalAction extends Action { const disposable = instance.onTitleChanged(() => { if (instance.title && instance.title.trim().length > 0) { disposable.dispose(); - instance.setTitle(`${instance.title} (Local)`, false); + instance.setTitle(`${instance.title} (Local)`, TitleEventSource.Api); } }); diff --git a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts index 46a2661d1ffe6..0b20c735a89c3 100644 --- a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts +++ b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts @@ -5,7 +5,7 @@ import * as platform from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; -import { ITerminalInstance, IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalInstance, IWindowsShellHelper, TitleEventSource } from 'vs/workbench/contrib/terminal/common/terminal'; import { Terminal as XTermTerminal } from 'xterm'; import * as WindowsProcessTreeType from 'windows-process-tree'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -80,7 +80,7 @@ export class WindowsShellHelper extends Disposable implements IWindowsShellHelpe if (platform.isWindows && this._terminalInstance.isTitleSetByProcess) { this.getShellName().then(title => { if (!this._isDisposed) { - this._terminalInstance.setTitle(title, true); + this._terminalInstance.setTitle(title, TitleEventSource.Process); } }); } From 64716dd3b0a94133ab7e0e9a591e58f3e4a0ca76 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 20 Aug 2019 10:17:37 -0700 Subject: [PATCH 407/613] Fix smoke test driver types --- test/smoke/src/vscode/puppeteerDriver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 955460f9d0a0d..88cb471bdd01c 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -26,7 +26,7 @@ const vscodeToPuppeteerKey = { }; function buildDriver(browser: puppeteer.Browser, page: puppeteer.Page): IDriver { - const driver = { + const driver: IDriver = { _serviceBrand: undefined, getWindowIds: () => { return Promise.resolve([1]); @@ -180,7 +180,7 @@ export interface IDriver { getTitle(windowId: number): Promise; isActiveElement(windowId: number, selector: string): Promise; getElements(windowId: number, selector: string, recursive?: boolean): Promise; - getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number; }>; + 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; From 68660775ab49b500c91a3d02112d3a0ab503793b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 20 Aug 2019 10:17:50 -0700 Subject: [PATCH 408/613] Teardown server on SIGTERM --- test/smoke/src/vscode/puppeteerDriver.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/smoke/src/vscode/puppeteerDriver.ts b/test/smoke/src/vscode/puppeteerDriver.ts index 88cb471bdd01c..e7ae860b55112 100644 --- a/test/smoke/src/vscode/puppeteerDriver.ts +++ b/test/smoke/src/vscode/puppeteerDriver.ts @@ -98,6 +98,7 @@ export async function launch(_args: string[]): Promise { server.stdout.on('data', e => console.log('Server stdout: ' + e)); process.on('exit', teardown); process.on('SIGINT', teardown); + process.on('SIGTERM', teardown); endpoint = await waitForEndpoint(); } From aa7c5a44cc416baea1468b22cfa8d1a66d78b7c1 Mon Sep 17 00:00:00 2001 From: skprabhanjan Date: Tue, 20 Aug 2019 23:04:41 +0530 Subject: [PATCH 409/613] Added hyphen pattern case preserve logic --- src/vs/base/common/search.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/search.ts b/src/vs/base/common/search.ts index c18233d61b9dd..98aedcdef4a6b 100644 --- a/src/vs/base/common/search.ts +++ b/src/vs/base/common/search.ts @@ -12,7 +12,11 @@ export function buildReplaceStringWithCasePreserved(matches: string[] | null, pa } else if (matches[0].toLowerCase() === matches[0]) { return pattern.toLowerCase(); } else if (strings.containsUppercaseCharacter(matches[0][0])) { - return pattern[0].toUpperCase() + pattern.substr(1); + if (validateHyphenPattern(matches, pattern)) { + return buildReplaceStringForHyphenPatterns(matches, pattern); + } else { + return pattern[0].toUpperCase() + pattern.substr(1); + } } else { // we don't understand its pattern yet. return pattern; @@ -21,3 +25,20 @@ export function buildReplaceStringWithCasePreserved(matches: string[] | null, pa return pattern; } } + +function validateHyphenPattern(matches: string[], pattern: string): boolean { + const doesConatinHyphen = matches[0].indexOf('-') !== -1 && pattern.indexOf('-') !== -1; + const doesConatinSameNumberOfHyphens = matches[0].split('-').length === pattern.split('-').length; + return doesConatinHyphen && doesConatinSameNumberOfHyphens; +} + +function buildReplaceStringForHyphenPatterns(matches: string[], pattern: string): string { + const splitPatternAtHyphen = pattern.split('-'); + const splitMatchAtHyphen = matches[0].split('-'); + let replaceString: string = ''; + + splitPatternAtHyphen.forEach((splitValues, index) => { + replaceString += buildReplaceStringWithCasePreserved([splitMatchAtHyphen[index]], splitValues) + '-'; + }); + return replaceString.slice(0, -1); +} From 5c2738521209c6f026ce349495843f008c9a7d50 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 10:34:21 -0700 Subject: [PATCH 410/613] Restrict which subsets of the markdown grammar the js/ts injection eagerly loads Fixes #77990 --- .../syntaxes/jsdoc.injection.tmLanguage.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/extensions/typescript-basics/syntaxes/jsdoc.injection.tmLanguage.json b/extensions/typescript-basics/syntaxes/jsdoc.injection.tmLanguage.json index b7b4db2d2a54a..02053ebab1365 100644 --- a/extensions/typescript-basics/syntaxes/jsdoc.injection.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/jsdoc.injection.tmLanguage.json @@ -11,10 +11,13 @@ "while": "(^|\\G)\\s*\\*(?!/)(?=([^*]|[*](?!/))*$)", "patterns": [ { - "include": "text.html.markdown#fenced_code_block" + "include": "text.html.markdown#fenced_code_block_js" }, { - "include": "text.html.markdown#lists" + "include": "text.html.markdown#fenced_code_block_ts" + }, + { + "include": "text.html.markdown#fenced_code_block_unknown" }, { "include": "#example" @@ -47,4 +50,4 @@ } }, "scopeName": "documentation.injection" -} \ No newline at end of file +} From d1573608e12a73fd7ceb441f1bebacf9f552d1e8 Mon Sep 17 00:00:00 2001 From: skprabhanjan Date: Tue, 20 Aug 2019 23:25:22 +0530 Subject: [PATCH 411/613] Replace Pattern test updated --- .../contrib/find/test/replacePattern.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/vs/editor/contrib/find/test/replacePattern.test.ts b/src/vs/editor/contrib/find/test/replacePattern.test.ts index e4c78422b8fe3..8a651279a6dfe 100644 --- a/src/vs/editor/contrib/find/test/replacePattern.test.ts +++ b/src/vs/editor/contrib/find/test/replacePattern.test.ts @@ -176,6 +176,13 @@ suite('Replace Pattern test', () => { assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def'); actual = ['aBC']; assert.equal(buildReplaceStringWithCasePreserved(actual, replacePattern), 'Def'); + + actual = ['Foo-Bar']; + assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo-newbar'), 'Newfoo-Newbar'); + actual = ['Foo-Bar-Abc']; + assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo-newbar-newabc'), 'Newfoo-Newbar-Newabc'); + actual = ['Foo-Bar-abc']; + assert.equal(buildReplaceStringWithCasePreserved(actual, 'newfoo-newbar'), 'Newfoo-newbar'); }); test('preserve case', () => { @@ -198,5 +205,17 @@ suite('Replace Pattern test', () => { assert.equal(actual, 'Def'); actual = replacePattern.buildReplaceString(['aBC'], true); assert.equal(actual, 'Def'); + + replacePattern = parseReplaceString('newfoo-newbar'); + actual = replacePattern.buildReplaceString(['Foo-Bar'], true); + assert.equal(actual, 'Newfoo-Newbar'); + + replacePattern = parseReplaceString('newfoo-newbar-newabc'); + actual = replacePattern.buildReplaceString(['Foo-Bar-Abc'], true); + assert.equal(actual, 'Newfoo-Newbar-Newabc'); + + replacePattern = parseReplaceString('newfoo-newbar'); + actual = replacePattern.buildReplaceString(['Foo-Bar-abc'], true); + assert.equal(actual, 'Newfoo-newbar'); }); }); From 0b4c4dc892cc52a9a1b42d41579299d6a4ddbe92 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 20 Aug 2019 11:02:16 -0700 Subject: [PATCH 412/613] Ensure resolved cwd is passed back to window Fixes #1109 --- src/vs/workbench/api/node/extHostTerminalService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 99a7cc8309ca2..5c5d76564f8f4 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -497,6 +497,7 @@ export class ExtHostTerminalService implements IExtHostTerminalService, ExtHostT const terminalConfig = configProvider.getConfiguration('terminal.integrated'); const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), lastActiveWorkspace ? lastActiveWorkspace : undefined, this._variableResolver, activeWorkspaceRootUri, terminalConfig.cwd, this._logService); + shellLaunchConfig.cwd = initialCwd; const envFromConfig = this._apiInspectConfigToPlain(configProvider.getConfiguration('terminal.integrated').inspect(`env.${platformKey}`)); const baseEnv = terminalConfig.get('inheritEnv', true) ? process.env as platform.IProcessEnvironment : await this._getNonInheritedEnv(); From 1429e8182ff283ef5927aecd7ee7da96ab9fc715 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 11:21:23 -0700 Subject: [PATCH 413/613] Enable "restart ts server" command in js/tsconfig files Fixes #79530 --- .../src/features/task.ts | 5 +++-- .../src/utils/languageDescription.ts | 10 ++++++++++ .../src/utils/managedFileContext.ts | 18 +++++++++++++++--- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/extensions/typescript-language-features/src/features/task.ts b/extensions/typescript-language-features/src/features/task.ts index e47ee6d7fdcb9..64057555d5f73 100644 --- a/extensions/typescript-language-features/src/features/task.ts +++ b/extensions/typescript-language-features/src/features/task.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as fs from 'fs'; +import * as jsonc from 'jsonc-parser'; import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as jsonc from 'jsonc-parser'; import { ITypeScriptServiceClient } from '../typescriptService'; +import { isTsConfigFileName } from '../utils/languageDescription'; import { Lazy } from '../utils/lazy'; import { isImplicitProjectConfigFile } from '../utils/tsconfig'; import TsConfigProvider, { TSConfig } from '../utils/tsconfigProvider'; @@ -113,7 +114,7 @@ export default class TscTaskProvider implements vscode.TaskProvider { private async getTsConfigForActiveFile(token: vscode.CancellationToken): Promise { const editor = vscode.window.activeTextEditor; if (editor) { - if (path.basename(editor.document.fileName).match(/^tsconfig\.(.\.)?json$/)) { + if (isTsConfigFileName(editor.document.fileName)) { const uri = editor.document.uri; return [{ path: uri.fsPath, diff --git a/extensions/typescript-language-features/src/utils/languageDescription.ts b/extensions/typescript-language-features/src/utils/languageDescription.ts index 79f9ce9f47b0a..f6b066bd40011 100644 --- a/extensions/typescript-language-features/src/utils/languageDescription.ts +++ b/extensions/typescript-language-features/src/utils/languageDescription.ts @@ -2,6 +2,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + +import { basename } from 'path'; import * as languageModeIds from './languageModeIds'; export const enum DiagnosticLanguage { @@ -38,3 +40,11 @@ export const standardLanguageDescriptions: LanguageDescription[] = [ configFilePattern: /^jsconfig(\..*)?\.json$/gi } ]; + +export function isTsConfigFileName(fileName: string): boolean { + return /^tsconfig\.(.+\.)?json$/i.test(basename(fileName)); +} + +export function isJsConfigOrTsConfigFileName(fileName: string): boolean { + return /^[jt]sconfig\.(.+\.)?json$/i.test(basename(fileName)); +} diff --git a/extensions/typescript-language-features/src/utils/managedFileContext.ts b/extensions/typescript-language-features/src/utils/managedFileContext.ts index 81acf9ad50500..481f2482d2631 100644 --- a/extensions/typescript-language-features/src/utils/managedFileContext.ts +++ b/extensions/typescript-language-features/src/utils/managedFileContext.ts @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { isSupportedLanguageMode } from './languageModeIds'; import { Disposable } from './dispose'; +import { isJsConfigOrTsConfigFileName } from './languageDescription'; +import { isSupportedLanguageMode } from './languageModeIds'; /** * When clause context set when the current file is managed by vscode's built-in typescript extension. @@ -26,8 +27,7 @@ export default class ManagedFileContextManager extends Disposable { private onDidChangeActiveTextEditor(editor?: vscode.TextEditor): any { if (editor) { - const isManagedFile = isSupportedLanguageMode(editor.document) && this.normalizePath(editor.document.uri) !== null; - this.updateContext(isManagedFile); + this.updateContext(this.isManagedFile(editor)); } } @@ -39,4 +39,16 @@ export default class ManagedFileContextManager extends Disposable { vscode.commands.executeCommand('setContext', ManagedFileContextManager.contextName, newValue); this.isInManagedFileContext = newValue; } + + private isManagedFile(editor: vscode.TextEditor): boolean { + return this.isManagedScriptFile(editor) || this.isManagedConfigFile(editor); + } + + private isManagedScriptFile(editor: vscode.TextEditor): boolean { + return isSupportedLanguageMode(editor.document) && this.normalizePath(editor.document.uri) !== null; + } + + private isManagedConfigFile(editor: vscode.TextEditor): boolean { + return isJsConfigOrTsConfigFileName(editor.document.fileName); + } } From f509f7f141192052b64d020038da19ec4244b289 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 11:25:42 -0700 Subject: [PATCH 414/613] Rename command #75612 --- extensions/markdown-language-features/package.json | 2 +- .../markdown-language-features/src/commands/renderDocument.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 4a6c326c19e38..7bcf7ca8641ed 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -24,7 +24,7 @@ "onCommand:markdown.showLockedPreviewToSide", "onCommand:markdown.showSource", "onCommand:markdown.showPreviewSecuritySelector", - "onCommand:markdown.render", + "onCommand:markdown.api.render", "onWebviewPanel:markdown.preview" ], "contributes": { diff --git a/extensions/markdown-language-features/src/commands/renderDocument.ts b/extensions/markdown-language-features/src/commands/renderDocument.ts index 3d7a0bc29a029..a55da6ddbc1e9 100644 --- a/extensions/markdown-language-features/src/commands/renderDocument.ts +++ b/extensions/markdown-language-features/src/commands/renderDocument.ts @@ -10,7 +10,7 @@ import { MarkdownEngine } from '../markdownEngine'; import { SkinnyTextDocument } from '../tableOfContentsProvider'; export class RenderDocument implements Command { - public readonly id = 'markdown.render'; + public readonly id = 'markdown.api.render'; public constructor( private readonly engine: MarkdownEngine From 1802be3b703f242352a71a5e75b298eb1c8ccca7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 11:26:49 -0700 Subject: [PATCH 415/613] Always require an argument to command #75612 --- .../src/commands/renderDocument.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/extensions/markdown-language-features/src/commands/renderDocument.ts b/extensions/markdown-language-features/src/commands/renderDocument.ts index a55da6ddbc1e9..f9ec89fce3d3e 100644 --- a/extensions/markdown-language-features/src/commands/renderDocument.ts +++ b/extensions/markdown-language-features/src/commands/renderDocument.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; - import { Command } from '../commandManager'; import { MarkdownEngine } from '../markdownEngine'; import { SkinnyTextDocument } from '../tableOfContentsProvider'; @@ -16,15 +14,7 @@ export class RenderDocument implements Command { private readonly engine: MarkdownEngine ) { } - public async execute(document?: SkinnyTextDocument | string): Promise { - if (!document) { - if (!vscode.window.activeTextEditor) { - return; - } - - document = vscode.window.activeTextEditor.document; - } - + public async execute(document: SkinnyTextDocument | string): Promise { return this.engine.render(document); } } From e44d9a2888d818b9e472df3198ec9187874bb7e0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 11:28:31 -0700 Subject: [PATCH 416/613] Rename parameter --- .../markdown-language-features/src/markdownEngine.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 9b51dc06b86c8..bcd7dc43ff4ba 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -140,12 +140,12 @@ export class MarkdownEngine { return engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); } - public async render(document: SkinnyTextDocument | string): Promise { - const config = this.getConfig(typeof document === 'string' ? undefined : document.uri); + public async render(input: SkinnyTextDocument | string): Promise { + const config = this.getConfig(typeof input === 'string' ? undefined : input.uri); const engine = await this.getEngine(config); - const tokens = typeof document === 'string' - ? this.tokenizeString(document, engine) - : this.tokenizeDocument(document, config, engine); + const tokens = typeof input === 'string' + ? this.tokenizeString(input, engine) + : this.tokenizeDocument(input, config, engine); return engine.renderer.render(tokens, { ...(engine as any).options, From 67145b273200c4025dfb239b8ce17850b90ad064 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 20 Aug 2019 11:49:59 -0700 Subject: [PATCH 417/613] update replace input box size when resizing and display preserve case button properly --- src/vs/base/browser/ui/findinput/findInput.ts | 9 +- .../base/browser/ui/findinput/replaceInput.ts | 378 ++++++++++++++++++ src/vs/base/browser/ui/inputbox/inputBox.ts | 33 +- src/vs/editor/contrib/find/findWidget.css | 19 +- src/vs/editor/contrib/find/findWidget.ts | 107 ++--- .../browser/contextScopedHistoryWidget.ts | 11 + 6 files changed, 476 insertions(+), 81 deletions(-) create mode 100644 src/vs/base/browser/ui/findinput/replaceInput.ts diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index 0c64d37c49ed5..fcc27e402a771 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -24,6 +24,7 @@ export interface IFindInputOptions extends IFindInputStyles { readonly validation?: IInputValidator; readonly label: string; readonly flexibleHeight?: boolean; + readonly flexibleWidth?: boolean; readonly flexibleMaxHeight?: number; readonly appendCaseSensitiveLabel?: string; @@ -120,6 +121,7 @@ export class FindInput extends Widget { const appendRegexLabel = options.appendRegexLabel || ''; const history = options.history || []; const flexibleHeight = !!options.flexibleHeight; + const flexibleWidth = !!options.flexibleWidth; const flexibleMaxHeight = options.flexibleMaxHeight; this.domNode = document.createElement('div'); @@ -145,6 +147,7 @@ export class FindInput extends Widget { inputValidationErrorBorder: this.inputValidationErrorBorder, history, flexibleHeight, + flexibleWidth, flexibleMaxHeight })); @@ -197,11 +200,7 @@ export class FindInput extends Widget { })); if (this._showOptionButtons) { - const paddingRight = (this.caseSensitive.width() + this.wholeWords.width() + this.regex.width()) + 'px'; - this.inputBox.inputElement.style.paddingRight = paddingRight; - if (this.inputBox.mirrorElement) { - this.inputBox.mirrorElement.style.paddingRight = paddingRight; - } + this.inputBox.paddingRight = this.caseSensitive.width() + this.wholeWords.width() + this.regex.width(); } // Arrow-Key support to navigate between options diff --git a/src/vs/base/browser/ui/findinput/replaceInput.ts b/src/vs/base/browser/ui/findinput/replaceInput.ts new file mode 100644 index 0000000000000..db0c44edf2efb --- /dev/null +++ b/src/vs/base/browser/ui/findinput/replaceInput.ts @@ -0,0 +1,378 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./findInput'; + +import * as nls from 'vs/nls'; +import * as dom from 'vs/base/browser/dom'; +import { IMessage as InputBoxMessage, IInputValidator, IInputBoxStyles, HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { Widget } from 'vs/base/browser/ui/widget'; +import { Event, Emitter } from 'vs/base/common/event'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { Color } from 'vs/base/common/color'; +import { ICheckboxStyles, Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; +import { IFindInputCheckboxOpts } from 'vs/base/browser/ui/findinput/findInputCheckboxes'; + +export interface IReplaceInputOptions extends IReplaceInputStyles { + readonly placeholder?: string; + readonly width?: number; + readonly validation?: IInputValidator; + readonly label: string; + readonly flexibleHeight?: boolean; + readonly flexibleWidth?: boolean; + readonly flexibleMaxHeight?: number; + + readonly history?: string[]; +} + +export interface IReplaceInputStyles extends IInputBoxStyles { + inputActiveOptionBorder?: Color; +} + +const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input"); +const NLS_PRESERVE_CASE_LABEL = nls.localize('label.preserveCaseCheckbox', "Preserve Case"); + +export class PreserveCaseCheckbox extends Checkbox { + constructor(opts: IFindInputCheckboxOpts) { + super({ + // TODO: does this need its own icon? + actionClassName: 'monaco-case-sensitive', + title: NLS_PRESERVE_CASE_LABEL + opts.appendTitle, + isChecked: opts.isChecked, + inputActiveOptionBorder: opts.inputActiveOptionBorder + }); + } +} + +export class ReplaceInput extends Widget { + + static readonly OPTION_CHANGE: string = 'optionChange'; + + private contextViewProvider: IContextViewProvider | undefined; + private placeholder: string; + private validation?: IInputValidator; + private label: string; + private fixFocusOnOptionClickEnabled = true; + + private inputActiveOptionBorder?: Color; + private inputBackground?: Color; + private inputForeground?: Color; + private inputBorder?: Color; + + private inputValidationInfoBorder?: Color; + private inputValidationInfoBackground?: Color; + private inputValidationInfoForeground?: Color; + private inputValidationWarningBorder?: Color; + private inputValidationWarningBackground?: Color; + private inputValidationWarningForeground?: Color; + private inputValidationErrorBorder?: Color; + private inputValidationErrorBackground?: Color; + private inputValidationErrorForeground?: Color; + + private preserveCase: PreserveCaseCheckbox; + private cachedOptionsWidth: number = 0; + public domNode: HTMLElement; + public inputBox: HistoryInputBox; + + private readonly _onDidOptionChange = this._register(new Emitter()); + public readonly onDidOptionChange: Event = this._onDidOptionChange.event; + + private readonly _onKeyDown = this._register(new Emitter()); + public readonly onKeyDown: Event = this._onKeyDown.event; + + private readonly _onMouseDown = this._register(new Emitter()); + public readonly onMouseDown: Event = this._onMouseDown.event; + + private readonly _onInput = this._register(new Emitter()); + public readonly onInput: Event = this._onInput.event; + + private readonly _onKeyUp = this._register(new Emitter()); + public readonly onKeyUp: Event = this._onKeyUp.event; + + private _onPreserveCaseKeyDown = this._register(new Emitter()); + public readonly onPreserveCaseKeyDown: Event = this._onPreserveCaseKeyDown.event; + + constructor(parent: HTMLElement | null, contextViewProvider: IContextViewProvider | undefined, private readonly _showOptionButtons: boolean, options: IReplaceInputOptions) { + super(); + this.contextViewProvider = contextViewProvider; + this.placeholder = options.placeholder || ''; + this.validation = options.validation; + this.label = options.label || NLS_DEFAULT_LABEL; + + this.inputActiveOptionBorder = options.inputActiveOptionBorder; + this.inputBackground = options.inputBackground; + this.inputForeground = options.inputForeground; + this.inputBorder = options.inputBorder; + + this.inputValidationInfoBorder = options.inputValidationInfoBorder; + this.inputValidationInfoBackground = options.inputValidationInfoBackground; + this.inputValidationInfoForeground = options.inputValidationInfoForeground; + this.inputValidationWarningBorder = options.inputValidationWarningBorder; + this.inputValidationWarningBackground = options.inputValidationWarningBackground; + this.inputValidationWarningForeground = options.inputValidationWarningForeground; + this.inputValidationErrorBorder = options.inputValidationErrorBorder; + this.inputValidationErrorBackground = options.inputValidationErrorBackground; + this.inputValidationErrorForeground = options.inputValidationErrorForeground; + + const flexibleHeight = !!options.flexibleHeight; + const flexibleWidth = !!options.flexibleWidth; + const flexibleMaxHeight = options.flexibleMaxHeight; + + this.buildDomNode(options.history || [], flexibleHeight, flexibleWidth, flexibleMaxHeight); + + if (parent) { + parent.appendChild(this.domNode); + } + + this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown.fire(e)); + this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp.fire(e)); + this.oninput(this.inputBox.inputElement, (e) => this._onInput.fire()); + this.onmousedown(this.inputBox.inputElement, (e) => this._onMouseDown.fire(e)); + } + + public enable(): void { + dom.removeClass(this.domNode, 'disabled'); + this.inputBox.enable(); + this.preserveCase.enable(); + } + + public disable(): void { + dom.addClass(this.domNode, 'disabled'); + this.inputBox.disable(); + this.preserveCase.disable(); + } + + public setFocusInputOnOptionClick(value: boolean): void { + this.fixFocusOnOptionClickEnabled = value; + } + + public setEnabled(enabled: boolean): void { + if (enabled) { + this.enable(); + } else { + this.disable(); + } + } + + public clear(): void { + this.clearValidation(); + this.setValue(''); + this.focus(); + } + + public getValue(): string { + return this.inputBox.value; + } + + public setValue(value: string): void { + if (this.inputBox.value !== value) { + this.inputBox.value = value; + } + } + + public onSearchSubmit(): void { + this.inputBox.addToHistory(); + } + + public style(styles: IReplaceInputStyles): void { + this.inputActiveOptionBorder = styles.inputActiveOptionBorder; + this.inputBackground = styles.inputBackground; + this.inputForeground = styles.inputForeground; + this.inputBorder = styles.inputBorder; + + this.inputValidationInfoBackground = styles.inputValidationInfoBackground; + this.inputValidationInfoForeground = styles.inputValidationInfoForeground; + this.inputValidationInfoBorder = styles.inputValidationInfoBorder; + this.inputValidationWarningBackground = styles.inputValidationWarningBackground; + this.inputValidationWarningForeground = styles.inputValidationWarningForeground; + this.inputValidationWarningBorder = styles.inputValidationWarningBorder; + this.inputValidationErrorBackground = styles.inputValidationErrorBackground; + this.inputValidationErrorForeground = styles.inputValidationErrorForeground; + this.inputValidationErrorBorder = styles.inputValidationErrorBorder; + + this.applyStyles(); + } + + protected applyStyles(): void { + if (this.domNode) { + const checkBoxStyles: ICheckboxStyles = { + inputActiveOptionBorder: this.inputActiveOptionBorder, + }; + this.preserveCase.style(checkBoxStyles); + + const inputBoxStyles: IInputBoxStyles = { + inputBackground: this.inputBackground, + inputForeground: this.inputForeground, + inputBorder: this.inputBorder, + inputValidationInfoBackground: this.inputValidationInfoBackground, + inputValidationInfoForeground: this.inputValidationInfoForeground, + inputValidationInfoBorder: this.inputValidationInfoBorder, + inputValidationWarningBackground: this.inputValidationWarningBackground, + inputValidationWarningForeground: this.inputValidationWarningForeground, + inputValidationWarningBorder: this.inputValidationWarningBorder, + inputValidationErrorBackground: this.inputValidationErrorBackground, + inputValidationErrorForeground: this.inputValidationErrorForeground, + inputValidationErrorBorder: this.inputValidationErrorBorder + }; + this.inputBox.style(inputBoxStyles); + } + } + + public select(): void { + this.inputBox.select(); + } + + public focus(): void { + this.inputBox.focus(); + } + + public getPreserveCase(): boolean { + return this.preserveCase.checked; + } + + public setPreserveCase(value: boolean): void { + this.preserveCase.checked = value; + } + + public focusOnPreserve(): void { + this.preserveCase.focus(); + } + + private _lastHighlightFindOptions: number = 0; + public highlightFindOptions(): void { + dom.removeClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions)); + this._lastHighlightFindOptions = 1 - this._lastHighlightFindOptions; + dom.addClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions)); + } + + private buildDomNode(history: string[], flexibleHeight: boolean, flexibleWidth: boolean, flexibleMaxHeight: number | undefined): void { + this.domNode = document.createElement('div'); + dom.addClass(this.domNode, 'monaco-findInput'); + + this.inputBox = this._register(new HistoryInputBox(this.domNode, this.contextViewProvider, { + ariaLabel: this.label || '', + placeholder: this.placeholder || '', + validationOptions: { + validation: this.validation + }, + inputBackground: this.inputBackground, + inputForeground: this.inputForeground, + inputBorder: this.inputBorder, + inputValidationInfoBackground: this.inputValidationInfoBackground, + inputValidationInfoForeground: this.inputValidationInfoForeground, + inputValidationInfoBorder: this.inputValidationInfoBorder, + inputValidationWarningBackground: this.inputValidationWarningBackground, + inputValidationWarningForeground: this.inputValidationWarningForeground, + inputValidationWarningBorder: this.inputValidationWarningBorder, + inputValidationErrorBackground: this.inputValidationErrorBackground, + inputValidationErrorForeground: this.inputValidationErrorForeground, + inputValidationErrorBorder: this.inputValidationErrorBorder, + history, + flexibleHeight, + flexibleWidth, + flexibleMaxHeight + })); + + this.preserveCase = this._register(new PreserveCaseCheckbox({ + appendTitle: '', + isChecked: false, + inputActiveOptionBorder: this.inputActiveOptionBorder + })); + this._register(this.preserveCase.onChange(viaKeyboard => { + this._onDidOptionChange.fire(viaKeyboard); + if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) { + this.inputBox.focus(); + } + this.validate(); + })); + this._register(this.preserveCase.onKeyDown(e => { + this._onPreserveCaseKeyDown.fire(e); + })); + + if (this._showOptionButtons) { + this.cachedOptionsWidth = this.preserveCase.width(); + // const paddingRight = () + 'px'; + // this.inputBox.inputElement.style.paddingRight = paddingRight; + // if (this.inputBox.mirrorElement) { + // this.inputBox.mirrorElement.style.paddingRight = paddingRight; + // } + } else { + this.cachedOptionsWidth = 0; + } + + // Arrow-Key support to navigate between options + let indexes = [this.preserveCase.domNode]; + this.onkeydown(this.domNode, (event: IKeyboardEvent) => { + if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) { + let index = indexes.indexOf(document.activeElement); + if (index >= 0) { + let newIndex: number = -1; + if (event.equals(KeyCode.RightArrow)) { + newIndex = (index + 1) % indexes.length; + } else if (event.equals(KeyCode.LeftArrow)) { + if (index === 0) { + newIndex = indexes.length - 1; + } else { + newIndex = index - 1; + } + } + + if (event.equals(KeyCode.Escape)) { + indexes[index].blur(); + } else if (newIndex >= 0) { + indexes[newIndex].focus(); + } + + dom.EventHelper.stop(event, true); + } + } + }); + + + let controls = document.createElement('div'); + controls.className = 'controls'; + controls.style.display = this._showOptionButtons ? 'block' : 'none'; + controls.appendChild(this.preserveCase.domNode); + + this.domNode.appendChild(controls); + } + + public validate(): void { + if (this.inputBox) { + this.inputBox.validate(); + } + } + + public showMessage(message: InputBoxMessage): void { + if (this.inputBox) { + this.inputBox.showMessage(message); + } + } + + public clearMessage(): void { + if (this.inputBox) { + this.inputBox.hideMessage(); + } + } + + private clearValidation(): void { + if (this.inputBox) { + this.inputBox.hideMessage(); + } + } + + public set width(newWidth: number) { + this.inputBox.paddingRight = this.cachedOptionsWidth; + this.inputBox.width = newWidth; + this.domNode.style.width = newWidth + 'px'; + } + + public dispose(): void { + super.dispose(); + } +} diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 7af2a58f46d2b..3bd9a5ce12bf4 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -32,6 +32,7 @@ export interface IInputOptions extends IInputBoxStyles { readonly type?: string; readonly validationOptions?: IInputValidationOptions; readonly flexibleHeight?: boolean; + readonly flexibleWidth?: boolean; readonly flexibleMaxHeight?: number; readonly actions?: ReadonlyArray; } @@ -173,6 +174,12 @@ export class InputBox extends Widget { this.mirror.innerHTML = ' '; this.scrollableElement = new ScrollableElement(this.element, { vertical: ScrollbarVisibility.Auto }); + + if (this.options.flexibleWidth) { + this.input.setAttribute('wrap', 'off'); + this.mirror.style.whiteSpace = 'pre'; + } + dom.append(container, this.scrollableElement.getDomNode()); this._register(this.scrollableElement); @@ -321,12 +328,36 @@ export class InputBox extends Widget { } public set width(width: number) { - this.input.style.width = width + 'px'; + if (this.options.flexibleHeight && this.options.flexibleWidth) { + // textarea with horizontal scrolling + let horizontalPadding = 0; + if (this.mirror) { + const paddingLeft = parseFloat(this.mirror.style.paddingLeft || '') || 0; + const paddingRight = parseFloat(this.mirror.style.paddingRight || '') || 0; + horizontalPadding = paddingLeft + paddingRight; + } + this.input.style.width = (width - horizontalPadding) + 'px'; + } else { + this.input.style.width = width + 'px'; + } + if (this.mirror) { this.mirror.style.width = width + 'px'; } } + public set paddingRight(paddingRight: number) { + if (this.options.flexibleHeight && this.options.flexibleWidth) { + this.input.style.width = `calc(100% - ${paddingRight}px)`; + } else { + this.input.style.paddingRight = paddingRight + 'px'; + } + + if (this.mirror) { + this.mirror.style.paddingRight = paddingRight + 'px'; + } + } + private updateScrollDimensions(): void { if (typeof this.cachedContentHeight !== 'number' || typeof this.cachedHeight !== 'number') { return; diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index ac0a74d55b908..d9eb731d1e309 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -62,7 +62,7 @@ min-height: 0; } -.monaco-editor .find-widget .replace-input .input { +.monaco-editor .find-widget .monaco-findInput .input { font-size: 13px; } @@ -79,12 +79,8 @@ min-height: 25px; } -.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input { - width: 100% !important; - padding-right: 66px; -} -.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input { +.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .mirror { padding-right: 22px; } @@ -120,8 +116,7 @@ width: 100%; } -.monaco-editor .find-widget .monaco-findInput .monaco-scrollable-element .scrollbar.vertical, -.monaco-editor .find-widget .replace-input .monaco-scrollable-element .scrollbar.vertical { +.monaco-editor .find-widget .monaco-findInput .monaco-scrollable-element .scrollbar.vertical { /* Hide vertical scrollbar */ opacity: 0; } @@ -257,15 +252,17 @@ display: none; } -.monaco-editor .find-widget > .replace-part > .replace-input { +.monaco-editor .find-widget > .replace-part > .monaco-findInput { position: relative; display: flex; display: -webkit-flex; vertical-align: middle; - width: auto !important; + flex: auto; + flex-grow: 0; + flex-shrink: 0; } -.monaco-editor .find-widget > .replace-part > .replace-input > .controls { +.monaco-editor .find-widget > .replace-part > .monaco-findInput > .controls { position: absolute; top: 3px; right: 2px; diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 6b59dafaba9fb..865fb6c220dd1 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -10,10 +10,9 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput'; -import { HistoryInputBox, IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; import { IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; import { Widget } from 'vs/base/browser/ui/widget'; -import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { Delayer } from 'vs/base/common/async'; import { Color } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -30,9 +29,10 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { ContextScopedFindInput, ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget'; +import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { alert as alertFn } from 'vs/base/browser/ui/aria/aria'; +import { ReplaceInput } from 'vs/base/browser/ui/findinput/replaceInput'; export interface IFindController { replace(): void; @@ -48,7 +48,6 @@ const NLS_TOGGLE_SELECTION_FIND_TITLE = nls.localize('label.toggleSelectionFind' const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close"); const NLS_REPLACE_INPUT_LABEL = nls.localize('label.replace', "Replace"); const NLS_REPLACE_INPUT_PLACEHOLDER = nls.localize('placeholder.replace', "Replace"); -const NLS_PRESERVE_CASE_LABEL = nls.localize('label.preserveCaseCheckbox', "Preserve Case"); const NLS_REPLACE_BTN_LABEL = nls.localize('label.replaceButton', "Replace"); const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replace All"); const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode"); @@ -59,7 +58,6 @@ const NLS_NO_RESULTS = nls.localize('label.noResults', "No Results"); const FIND_WIDGET_INITIAL_WIDTH = 411; const PART_WIDTH = 275; const FIND_INPUT_AREA_WIDTH = PART_WIDTH - 54; -const REPLACE_INPUT_AREA_WIDTH = FIND_INPUT_AREA_WIDTH; let MAX_MATCHES_COUNT_WIDTH = 69; let FIND_ALL_CONTROLS_WIDTH = 17/** Find Input margin-left */ + (MAX_MATCHES_COUNT_WIDTH + 3 + 1) /** Match Results */ + 23 /** Button */ * 4 + 2/** sash */; @@ -110,7 +108,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private _domNode!: HTMLElement; private _cachedHeight: number | null; private _findInput!: FindInput; - private _replaceInputBox!: HistoryInputBox; + private _replaceInput!: ReplaceInput; private _toggleReplaceBtn!: SimpleButton; private _matchesCount!: HTMLElement; @@ -118,7 +116,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private _nextBtn!: SimpleButton; private _toggleSelectionFind!: SimpleCheckbox; private _closeBtn!: SimpleButton; - private _preserveCase!: Checkbox; private _replaceBtn!: SimpleButton; private _replaceAllBtn!: SimpleButton; @@ -218,7 +215,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas })); this._replaceInputFocused = CONTEXT_REPLACE_INPUT_FOCUSED.bindTo(contextKeyService); - this._replaceFocusTracker = this._register(dom.trackFocus(this._replaceInputBox.inputElement)); + this._replaceFocusTracker = this._register(dom.trackFocus(this._replaceInput.inputBox.inputElement)); this._register(this._replaceFocusTracker.onDidFocus(() => { this._replaceInputFocused.set(true); this._updateSearchScope(); @@ -288,7 +285,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._updateButtons(); } if (e.replaceString) { - this._replaceInputBox.value = this._state.replaceString; + this._replaceInput.inputBox.value = this._state.replaceString; } if (e.isRevealed) { if (this._state.isRevealed) { @@ -301,7 +298,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (this._state.isReplaceRevealed) { if (!this._codeEditor.getConfiguration().readOnly && !this._isReplaceVisible) { this._isReplaceVisible = true; - this._replaceInputBox.width = this._findInput.inputBox.width; + this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode); this._updateButtons(); } } else { @@ -358,7 +355,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._findInput.inputBox.addToHistory(); } if (this._state.replaceString) { - this._replaceInputBox.addToHistory(); + this._replaceInput.inputBox.addToHistory(); } } @@ -423,7 +420,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private _updateButtons(): void { this._findInput.setEnabled(this._isVisible); - this._replaceInputBox.setEnabled(this._isVisible && this._isReplaceVisible); + this._replaceInput.setEnabled(this._isVisible && this._isReplaceVisible); this._updateToggleSelectionFindButton(); this._closeBtn.setEnabled(this._isVisible); @@ -617,8 +614,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder), }; this._findInput.style(inputStyles); - this._replaceInputBox.style(inputStyles); - this._preserveCase.style(inputStyles); + this._replaceInput.style(inputStyles); } private _tryUpdateWidgetWidth() { @@ -648,7 +644,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (widgetWidth > FIND_WIDGET_INITIAL_WIDTH) { // as the widget is resized by users, we may need to change the max width of the widget as the editor width changes. this._domNode.style.maxWidth = `${editorWidth - 28 - minimapWidth - 15}px`; - this._replaceInputBox.width = dom.getTotalWidth(this._findInput.inputBox.inputElement); + this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode); return; } } @@ -675,7 +671,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._findInput.inputBox.layout(); let findInputWidth = this._findInput.inputBox.width; if (findInputWidth > 0) { - this._replaceInputBox.width = findInputWidth; + this._replaceInput.width = findInputWidth; } } } @@ -693,7 +689,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas // replace input margin totalheight += 4; - totalheight += this._replaceInputBox.height + 2 /** input box border */; + totalheight += this._replaceInput.inputBox.height + 2 /** input box border */; } // margin bottom @@ -722,9 +718,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } public focusReplaceInput(): void { - this._replaceInputBox.select(); + this._replaceInput.select(); // Edge browser requires focus() in addition to select() - this._replaceInputBox.focus(); + this._replaceInput.focus(); } public highlightFindOptions(): void { @@ -790,7 +786,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (e.equals(KeyCode.Tab)) { if (this._isReplaceVisible) { - this._replaceInputBox.focus(); + this._replaceInput.focus(); } else { this._findInput.focusOnCaseSensitive(); } @@ -822,16 +818,16 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } if (e.equals(KeyMod.WinCtrl | KeyCode.Enter)) { - const inputElement = this._replaceInputBox.inputElement; + const inputElement = this._replaceInput.inputBox.inputElement; const start = inputElement.selectionStart; const end = inputElement.selectionEnd; const content = inputElement.value; if (start && end) { const value = content.substr(0, start) + '\n' + content.substr(end); - this._replaceInputBox.value = value; + this._replaceInput.inputBox.value = value; inputElement.setSelectionRange(start + 1, start + 1); - this._replaceInputBox.layout(); + this._replaceInput.inputBox.layout(); e.preventDefault(); return; } @@ -862,11 +858,11 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } if (e.equals(KeyCode.UpArrow)) { - return stopPropagationForMultiLineUpwards(e, this._replaceInputBox.value, this._replaceInputBox.element.querySelector('textarea')); + return stopPropagationForMultiLineUpwards(e, this._replaceInput.inputBox.value, this._replaceInput.inputBox.element.querySelector('textarea')); } if (e.equals(KeyCode.DownArrow)) { - return stopPropagationForMultiLineDownwards(e, this._replaceInputBox.value, this._replaceInputBox.element.querySelector('textarea')); + return stopPropagationForMultiLineDownwards(e, this._replaceInput.inputBox.value, this._replaceInput.inputBox.element.querySelector('textarea')); } } @@ -913,6 +909,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } }, flexibleHeight: true, + flexibleWidth: true, flexibleMaxHeight: 118 }, this._contextKeyService, true)); this._findInput.setRegex(!!this._state.isRegex); @@ -935,7 +932,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._register(this._findInput.onCaseSensitiveKeyDown((e) => { if (e.equals(KeyMod.Shift | KeyCode.Tab)) { if (this._isReplaceVisible) { - this._replaceInputBox.focus(); + this._replaceInput.focus(); e.preventDefault(); } } @@ -943,7 +940,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._register(this._findInput.onRegexKeyDown((e) => { if (e.equals(KeyCode.Tab)) { if (this._isReplaceVisible) { - this._preserveCase.focus(); + this._replaceInput.focusOnPreserve(); e.preventDefault(); } } @@ -1034,41 +1031,30 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas actionsContainer.appendChild(this._closeBtn.domNode); // Replace input - let replaceInput = document.createElement('div'); - replaceInput.className = 'replace-input'; - replaceInput.style.width = REPLACE_INPUT_AREA_WIDTH + 'px'; - this._replaceInputBox = this._register(new ContextScopedHistoryInputBox(replaceInput, undefined, { - ariaLabel: NLS_REPLACE_INPUT_LABEL, + this._replaceInput = this._register(new ContextScopedReplaceInput(null, undefined, { + label: NLS_REPLACE_INPUT_LABEL, placeholder: NLS_REPLACE_INPUT_PLACEHOLDER, history: [], flexibleHeight: true, + flexibleWidth: true, flexibleMaxHeight: 118 - }, this._contextKeyService)); - - - this._register(dom.addStandardDisposableListener(this._replaceInputBox.inputElement, 'keydown', (e) => this._onReplaceInputKeyDown(e))); - this._register(this._replaceInputBox.onDidChange(() => { - this._state.change({ replaceString: this._replaceInputBox.value }, false); + }, this._contextKeyService, true)); + this._replaceInput.setPreserveCase(!!this._state.preserveCase); + this._register(this._replaceInput.onKeyDown((e) => this._onReplaceInputKeyDown(e))); + this._register(this._replaceInput.inputBox.onDidChange(() => { + this._state.change({ replaceString: this._replaceInput.inputBox.value }, false); })); - this._register(this._replaceInputBox.onDidHeightChange((e) => { + this._register(this._replaceInput.inputBox.onDidHeightChange((e) => { if (this._isReplaceVisible && this._tryUpdateHeight()) { this._showViewZone(); } })); - - this._preserveCase = this._register(new Checkbox({ - actionClassName: 'monaco-preserve-case', - title: NLS_PRESERVE_CASE_LABEL, - isChecked: false, - })); - this._preserveCase.checked = !!this._state.preserveCase; - this._register(this._preserveCase.onChange(viaKeyboard => { - if (!viaKeyboard) { - this._state.change({ preserveCase: !this._state.preserveCase }, false); - this._replaceInputBox.focus(); - } + this._register(this._replaceInput.onDidOptionChange(() => { + this._state.change({ + preserveCase: this._replaceInput.getPreserveCase() + }, true); })); - this._register(this._preserveCase.onKeyDown((e) => { + this._register(this._replaceInput.onPreserveCaseKeyDown((e) => { if (e.equals(KeyCode.Tab)) { if (this._prevBtn.isEnabled()) { this._prevBtn.focus(); @@ -1082,8 +1068,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas e.preventDefault(); } - } - )); + })); // Replace one button this._replaceBtn = this._register(new SimpleButton({ @@ -1109,15 +1094,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } })); - let controls = document.createElement('div'); - controls.className = 'controls'; - controls.style.display = 'block'; - controls.appendChild(this._preserveCase.domNode); - replaceInput.appendChild(controls); - let replacePart = document.createElement('div'); replacePart.className = 'replace-part'; - replacePart.appendChild(replaceInput); + replacePart.appendChild(this._replaceInput.domNode); const replaceActionsContainer = document.createElement('div'); replaceActionsContainer.className = 'replace-actions'; @@ -1133,8 +1112,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas onTrigger: () => { this._state.change({ isReplaceRevealed: !this._isReplaceVisible }, false); if (this._isReplaceVisible) { - this._replaceInputBox.width = this._findInput.inputBox.width; - this._replaceInputBox.layout(); + this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode); + this._replaceInput.inputBox.layout(); } this._showViewZone(); } @@ -1179,7 +1158,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._domNode.style.width = `${width}px`; this._findInput.inputBox.width = inputBoxWidth; if (this._isReplaceVisible) { - this._replaceInputBox.width = inputBoxWidth; + this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode); } this._findInput.inputBox.layout(); diff --git a/src/vs/platform/browser/contextScopedHistoryWidget.ts b/src/vs/platform/browser/contextScopedHistoryWidget.ts index 5e5e8603683b9..8625db10ee845 100644 --- a/src/vs/platform/browser/contextScopedHistoryWidget.ts +++ b/src/vs/platform/browser/contextScopedHistoryWidget.ts @@ -10,6 +10,7 @@ import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview import { IHistoryNavigationWidget } from 'vs/base/browser/history'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ReplaceInput, IReplaceInputOptions } from 'vs/base/browser/ui/findinput/replaceInput'; export const HistoryNavigationWidgetContext = 'historyNavigationWidget'; export const HistoryNavigationEnablementContext = 'historyNavigationEnabled'; @@ -60,6 +61,16 @@ export class ContextScopedFindInput extends FindInput { super(container, contextViewProvider, showFindOptions, options); this._register(createAndBindHistoryNavigationWidgetScopedContextKeyService(contextKeyService, { target: this.inputBox.element, historyNavigator: this.inputBox }).scopedContextKeyService); } +} + +export class ContextScopedReplaceInput extends ReplaceInput { + + constructor(container: HTMLElement | null, contextViewProvider: IContextViewProvider | undefined, options: IReplaceInputOptions, + @IContextKeyService contextKeyService: IContextKeyService, showReplaceOptions: boolean = false + ) { + super(container, contextViewProvider, showReplaceOptions, options); + this._register(createAndBindHistoryNavigationWidgetScopedContextKeyService(contextKeyService, { target: this.inputBox.element, historyNavigator: this.inputBox }).scopedContextKeyService); + } } From 3d4e72b0bd3b5a88a301eb631f49775a3f92a0dd Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 20 Aug 2019 11:50:59 -0700 Subject: [PATCH 418/613] remove commented code --- src/vs/base/browser/ui/findinput/replaceInput.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/vs/base/browser/ui/findinput/replaceInput.ts b/src/vs/base/browser/ui/findinput/replaceInput.ts index db0c44edf2efb..d597fb8bd49c7 100644 --- a/src/vs/base/browser/ui/findinput/replaceInput.ts +++ b/src/vs/base/browser/ui/findinput/replaceInput.ts @@ -296,11 +296,6 @@ export class ReplaceInput extends Widget { if (this._showOptionButtons) { this.cachedOptionsWidth = this.preserveCase.width(); - // const paddingRight = () + 'px'; - // this.inputBox.inputElement.style.paddingRight = paddingRight; - // if (this.inputBox.mirrorElement) { - // this.inputBox.mirrorElement.style.paddingRight = paddingRight; - // } } else { this.cachedOptionsWidth = 0; } From 3b3490c29c0f636c3fa1323f5c1a6869dcd301a0 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 20 Aug 2019 14:08:15 -0700 Subject: [PATCH 419/613] Sort imports --- src/vs/base/browser/ui/inputbox/inputBox.ts | 1 + src/vs/editor/contrib/find/findWidget.ts | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 3bd9a5ce12bf4..430f250ce386d 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -178,6 +178,7 @@ export class InputBox extends Widget { if (this.options.flexibleWidth) { this.input.setAttribute('wrap', 'off'); this.mirror.style.whiteSpace = 'pre'; + this.mirror.style.wordWrap = 'initial'; } dom.append(container, this.scrollableElement.getDomNode()); diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 865fb6c220dd1..c98923dff4274 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -9,8 +9,10 @@ import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { alert as alertFn } from 'vs/base/browser/ui/aria/aria'; import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput'; import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; +import { ReplaceInput } from 'vs/base/browser/ui/findinput/replaceInput'; import { IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; import { Widget } from 'vs/base/browser/ui/widget'; import { Delayer } from 'vs/base/common/async'; @@ -31,8 +33,6 @@ import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatch import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { alert as alertFn } from 'vs/base/browser/ui/aria/aria'; -import { ReplaceInput } from 'vs/base/browser/ui/findinput/replaceInput'; export interface IFindController { replace(): void; @@ -888,6 +888,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } private _buildDomNode(): void { + const flexibleHeight = platform.isNative; + const flexibleWidth = platform.isNative; // Find input this._findInput = this._register(new ContextScopedFindInput(null, this._contextViewProvider, { width: FIND_INPUT_AREA_WIDTH, @@ -908,8 +910,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas return { content: e.message }; } }, - flexibleHeight: true, - flexibleWidth: true, + flexibleHeight, + flexibleWidth, flexibleMaxHeight: 118 }, this._contextKeyService, true)); this._findInput.setRegex(!!this._state.isRegex); @@ -1035,8 +1037,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas label: NLS_REPLACE_INPUT_LABEL, placeholder: NLS_REPLACE_INPUT_PLACEHOLDER, history: [], - flexibleHeight: true, - flexibleWidth: true, + flexibleHeight, + flexibleWidth, flexibleMaxHeight: 118 }, this._contextKeyService, true)); this._replaceInput.setPreserveCase(!!this._state.preserveCase); From e37d0e7e43f26f387b92e9b54c60d1f199f21608 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 14:18:13 -0700 Subject: [PATCH 420/613] Auto rewrite vscode-resource entries in csp in web iframe based webviews --- src/vs/workbench/contrib/webview/browser/pre/main.js | 5 +++++ src/vs/workbench/contrib/webview/browser/webviewElement.ts | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 0b25585c61197..c766b83c11493 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -289,6 +289,11 @@ const csp = newDocument.querySelector('meta[http-equiv="Content-Security-Policy"]'); if (!csp) { host.postMessage('no-csp-found'); + } else { + // Rewrite vscode-resource in csp + if (data.endpoint) { + csp.setAttribute('content', csp.getAttribute('content').replace(/vscode-resource:/g, data.endpoint)); + } } // set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 65b09a14ddedd..7a0015be053cc 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -202,7 +202,8 @@ export class IFrameWebview extends Disposable implements Webview { this._send('content', { contents: this.content.html, options: this.content.options, - state: this.content.state + state: this.content.state, + endpoint: this.endpoint, }); } From 5bfea5092d4e300e72cb597cb82ebef82c7258b8 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 20 Aug 2019 14:52:53 -0700 Subject: [PATCH 421/613] Move more inputbox keybindings to Commands --- src/vs/editor/contrib/find/findController.ts | 85 ++++++++++++++++++-- src/vs/editor/contrib/find/findWidget.ts | 50 ++++++------ 2 files changed, 102 insertions(+), 33 deletions(-) diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index ed3544d10ff8a..664bc8c8bf32e 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -12,19 +12,20 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding } from 'vs/editor/contrib/find/findModel'; +import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding, CONTEXT_REPLACE_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel'; import { FindOptionsWidget } from 'vs/editor/contrib/find/findOptionsWidget'; import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/findState'; import { FindWidget, IFindController } from 'vs/editor/contrib/find/findWidget'; import { MenuId } from 'vs/platform/actions/common/actions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { INotificationService } from 'vs/platform/notification/common/notification'; const SEARCH_STRING_MAX_LENGTH = 524288; @@ -75,7 +76,7 @@ export class CommonFindController extends Disposable implements editorCommon.IEd protected _state: FindReplaceState; protected _updateHistoryDelayer: Delayer; private _model: FindModelBoundToEditorModel | null; - private readonly _storageService: IStorageService; + protected readonly _storageService: IStorageService; private readonly _clipboardService: IClipboardService; protected readonly _contextKeyService: IContextKeyService; @@ -383,10 +384,11 @@ export class FindController extends CommonFindController implements IFindControl @IContextKeyService _contextKeyService: IContextKeyService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @IThemeService private readonly _themeService: IThemeService, - @IStorageService storageService: IStorageService, - @optional(IClipboardService) clipboardService: IClipboardService + @INotificationService private readonly _notificationService: INotificationService, + @IStorageService _storageService: IStorageService, + @optional(IClipboardService) clipboardService: IClipboardService, ) { - super(editor, _contextKeyService, storageService, clipboardService); + super(editor, _contextKeyService, _storageService, clipboardService); this._widget = null; this._findOptionsWidget = null; } @@ -422,7 +424,7 @@ export class FindController extends CommonFindController implements IFindControl } private _createFindWidget() { - this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService)); + this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService)); this._findOptionsWidget = this._register(new FindOptionsWidget(this._editor, this._state, this._keybindingService, this._themeService)); } } @@ -540,6 +542,27 @@ export class NextMatchFindAction extends MatchFindAction { } } +export class NextMatchFindAction2 extends MatchFindAction { + + constructor() { + super({ + id: FIND_IDS.NextMatchFindAction, + label: nls.localize('findNextMatchAction', "Find Next"), + alias: 'Find Next', + precondition: undefined, + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_FIND_INPUT_FOCUSED), + primary: KeyCode.Enter, + weight: KeybindingWeight.EditorContrib + } + }); + } + + protected _run(controller: CommonFindController): boolean { + return controller.moveToNextMatch(); + } +} + export class PreviousMatchFindAction extends MatchFindAction { constructor() { @@ -562,6 +585,27 @@ export class PreviousMatchFindAction extends MatchFindAction { } } +export class PreviousMatchFindAction2 extends MatchFindAction { + + constructor() { + super({ + id: FIND_IDS.PreviousMatchFindAction, + label: nls.localize('findPreviousMatchAction', "Find Previous"), + alias: 'Find Previous', + precondition: undefined, + kbOpts: { + kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_FIND_INPUT_FOCUSED), + primary: KeyMod.Shift | KeyCode.Enter, + weight: KeybindingWeight.EditorContrib + } + }); + } + + protected _run(controller: CommonFindController): boolean { + return controller.moveToPrevMatch(); + } +} + export abstract class SelectionMatchFindAction extends EditorAction { public run(accessor: ServicesAccessor | null, editor: ICodeEditor): void { let controller = CommonFindController.get(editor); @@ -695,7 +739,9 @@ registerEditorContribution(FindController); registerEditorAction(StartFindAction); registerEditorAction(StartFindWithSelectionAction); registerEditorAction(NextMatchFindAction); +registerEditorAction(NextMatchFindAction2); registerEditorAction(PreviousMatchFindAction); +registerEditorAction(PreviousMatchFindAction2); registerEditorAction(NextSelectionMatchFindAction); registerEditorAction(PreviousSelectionMatchFindAction); registerEditorAction(StartFindReplaceAction); @@ -781,6 +827,17 @@ registerEditorCommand(new FindCommand({ } })); +registerEditorCommand(new FindCommand({ + id: FIND_IDS.ReplaceOneAction, + precondition: CONTEXT_FIND_WIDGET_VISIBLE, + handler: x => x.replace(), + kbOpts: { + weight: KeybindingWeight.EditorContrib + 5, + kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_REPLACE_INPUT_FOCUSED), + primary: KeyCode.Enter + } +})); + registerEditorCommand(new FindCommand({ id: FIND_IDS.ReplaceAllAction, precondition: CONTEXT_FIND_WIDGET_VISIBLE, @@ -792,6 +849,20 @@ registerEditorCommand(new FindCommand({ } })); +registerEditorCommand(new FindCommand({ + id: FIND_IDS.ReplaceAllAction, + precondition: CONTEXT_FIND_WIDGET_VISIBLE, + handler: x => x.replaceAll(), + kbOpts: { + weight: KeybindingWeight.EditorContrib + 5, + kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_REPLACE_INPUT_FOCUSED), + primary: undefined, + mac: { + primary: KeyMod.CtrlCmd | KeyCode.Enter, + } + } +})); + registerEditorCommand(new FindCommand({ id: FIND_IDS.SelectAllMatchesAction, precondition: CONTEXT_FIND_WIDGET_VISIBLE, diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index c98923dff4274..d56ad01239861 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -33,6 +33,8 @@ import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatch import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { INotificationService } from 'vs/platform/notification/common/notification'; export interface IFindController { replace(): void; @@ -63,6 +65,7 @@ let MAX_MATCHES_COUNT_WIDTH = 69; let FIND_ALL_CONTROLS_WIDTH = 17/** Find Input margin-left */ + (MAX_MATCHES_COUNT_WIDTH + 3 + 1) /** Match Results */ + 23 /** Button */ * 4 + 2/** sash */; const FIND_INPUT_AREA_HEIGHT = 33; // The height of Find Widget when Replace Input is not visible. +const ctrlEnterReplaceAllWarningPromptedKey = 'ctrlEnterReplaceAll.windows.donotask'; export class FindWidgetViewZone implements IViewZone { public readonly afterLineNumber: number; @@ -104,6 +107,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private readonly _contextViewProvider: IContextViewProvider; private readonly _keybindingService: IKeybindingService; private readonly _contextKeyService: IContextKeyService; + private readonly _storageService: IStorageService; + private readonly _notificationService: INotificationService; private _domNode!: HTMLElement; private _cachedHeight: number | null; @@ -122,6 +127,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private _isVisible: boolean; private _isReplaceVisible: boolean; private _ignoreChangeEvent: boolean; + private _ctrlEnterReplaceAllWarningPrompted: boolean; private readonly _findFocusTracker: dom.IFocusTracker; private readonly _findInputFocused: IContextKey; @@ -141,7 +147,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas contextViewProvider: IContextViewProvider, keybindingService: IKeybindingService, contextKeyService: IContextKeyService, - themeService: IThemeService + themeService: IThemeService, + storageService: IStorageService, + notificationService: INotificationService, ) { super(); this._codeEditor = codeEditor; @@ -150,6 +158,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._contextViewProvider = contextViewProvider; this._keybindingService = keybindingService; this._contextKeyService = contextKeyService; + this._storageService = storageService; + this._notificationService = notificationService; + + this._ctrlEnterReplaceAllWarningPrompted = !!storageService.getBoolean(ctrlEnterReplaceAllWarningPromptedKey, StorageScope.GLOBAL); this._isVisible = false; this._isReplaceVisible = false; @@ -755,19 +767,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } private _onFindInputKeyDown(e: IKeyboardEvent): void { - - if (e.equals(KeyCode.Enter)) { - this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().then(undefined, onUnexpectedError); - e.preventDefault(); - return; - } - - if (e.equals(KeyMod.Shift | KeyCode.Enter)) { - this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().then(undefined, onUnexpectedError); - e.preventDefault(); - return; - } - if (e.equals(KeyMod.WinCtrl | KeyCode.Enter)) { const inputElement = this._findInput.inputBox.inputElement; const start = inputElement.selectionStart; @@ -810,14 +809,19 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } private _onReplaceInputKeyDown(e: IKeyboardEvent): void { + if (e.equals(KeyMod.WinCtrl | KeyCode.Enter)) { + if (platform.isWindows && platform.isNative && !this._ctrlEnterReplaceAllWarningPrompted) { + // this is the first time when users press Ctrl + Enter to replace all + this._notificationService.info( + nls.localize('ctrlEnter.keybindingChanged', + 'Ctrl+Enter now inserts line break instead of replacing all. You can modify the keybinding for editor.action.replaceAll to override this behavior.') + ); - if (e.equals(KeyCode.Enter)) { - this._controller.replace(); - e.preventDefault(); - return; - } + this._ctrlEnterReplaceAllWarningPrompted = true; + this._storageService.store(ctrlEnterReplaceAllWarningPromptedKey, true, StorageScope.GLOBAL); + + } - if (e.equals(KeyMod.WinCtrl | KeyCode.Enter)) { const inputElement = this._replaceInput.inputBox.inputElement; const start = inputElement.selectionStart; const end = inputElement.selectionEnd; @@ -833,12 +837,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } } - if (e.equals(KeyMod.CtrlCmd | KeyCode.Enter)) { - this._controller.replaceAll(); - e.preventDefault(); - return; - } - if (e.equals(KeyCode.Tab)) { this._findInput.focusOnCaseSensitive(); e.preventDefault(); From 7cbc0d78f20a72d5bad1423eb5f904e56f1881be Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 14:56:35 -0700 Subject: [PATCH 422/613] Add try/catch around .focus call Fixes #79171 --- .../contrib/webview/electron-browser/webviewElement.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 830fd2effc878..bdd1c7e395680 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -496,7 +496,11 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { if (!this._webview) { return; } - this._webview.focus(); + try { + this._webview.focus(); + } catch { + // noop + } this._send('focus'); // Handle focus change programmatically (do not rely on event from ) From 646d4ddb759171479bd6a02a2b306610c30efc96 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 15:07:25 -0700 Subject: [PATCH 423/613] Use _register --- .../src/features/preview.ts | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index 775734d1140a3..3ee4d26f4f20e 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -172,19 +172,19 @@ export class MarkdownPreview extends Disposable { this._locked = locked; this.editor = webview; - this.editor.onDidDispose(() => { + this._register(this.editor.onDidDispose(() => { this.dispose(); - }, null, this._disposables); + })); - this.editor.onDidChangeViewState(e => { + this._register(this.editor.onDidChangeViewState(e => { this._onDidChangeViewStateEmitter.fire(e); - }, null, this._disposables); + })); - _contributionProvider.onContributionsChanged(() => { + this._register(_contributionProvider.onContributionsChanged(() => { setImmediate(() => this.refresh()); - }, null, this._disposables); + })); - this.editor.webview.onDidReceiveMessage((e: CacheImageSizesMessage | RevealLineMessage | DidClickMessage | ClickLinkMessage | ShowPreviewSecuritySelectorMessage | PreviewStyleLoadErrorMessage) => { + this._register(this.editor.webview.onDidReceiveMessage((e: CacheImageSizesMessage | RevealLineMessage | DidClickMessage | ClickLinkMessage | ShowPreviewSecuritySelectorMessage | PreviewStyleLoadErrorMessage) => { if (e.source !== this._resource.toString()) { return; } @@ -214,21 +214,21 @@ export class MarkdownPreview extends Disposable { vscode.window.showWarningMessage(localize('onPreviewStyleLoadError', "Could not load 'markdown.styles': {0}", e.body.unloadedStyles.join(', '))); break; } - }, null, this._disposables); + })); - vscode.workspace.onDidChangeTextDocument(event => { + this._register(vscode.workspace.onDidChangeTextDocument(event => { if (this.isPreviewOf(event.document.uri)) { this.refresh(); } - }, null, this._disposables); + })); - topmostLineMonitor.onDidChangeTopmostLine(event => { + this._register(topmostLineMonitor.onDidChangeTopmostLine(event => { if (this.isPreviewOf(event.resource)) { this.updateForView(event.resource, event.line); } - }, null, this._disposables); + })); - vscode.window.onDidChangeTextEditorSelection(event => { + this._register(vscode.window.onDidChangeTextEditorSelection(event => { if (this.isPreviewOf(event.textEditor.document.uri)) { this.postMessage({ type: 'onDidChangeTextEditorSelection', @@ -236,19 +236,19 @@ export class MarkdownPreview extends Disposable { source: this.resource.toString() }); } - }, null, this._disposables); + })); - vscode.window.onDidChangeActiveTextEditor(editor => { + this._register(vscode.window.onDidChangeActiveTextEditor(editor => { if (editor && isMarkdownFile(editor.document) && !this._locked) { this.update(editor.document.uri); } - }, null, this._disposables); + })); } - private readonly _onDisposeEmitter = new vscode.EventEmitter(); + private readonly _onDisposeEmitter = this._register(new vscode.EventEmitter()); public readonly onDispose = this._onDisposeEmitter.event; - private readonly _onDidChangeViewStateEmitter = new vscode.EventEmitter(); + private readonly _onDidChangeViewStateEmitter = this._register(new vscode.EventEmitter()); public readonly onDidChangeViewState = this._onDidChangeViewStateEmitter.event; public get resource(): vscode.Uri { From 5e5721210771b3b12dfc35b233f96c8d542ff748 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 15:52:56 -0700 Subject: [PATCH 424/613] Better handle cases where webview view state in api can get out sync with real view state Fixes #79492 Simplifies view state management logic in `mainThreadWebviews` to: * Not be stateful * Handle cases where a webview's view state changes through a more complex means (see #79492 for an example of this) --- .../api/browser/mainThreadWebview.ts | 85 ++++++------------- src/vs/workbench/api/common/extHostWebview.ts | 5 +- 2 files changed, 30 insertions(+), 60 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index d4beebbd5ffd6..a405a4b108c9f 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -17,7 +17,7 @@ import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } fr import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { extHostNamedCustomer } from '../common/extHostCustomers'; import { IProductService } from 'vs/platform/product/common/product'; @@ -47,8 +47,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews private readonly _webviews = new Map(); private readonly _revivers = new Map(); - private _activeWebview: WebviewPanelHandle | undefined = undefined; - constructor( context: IExtHostContext, @IExtensionService extensionService: IExtensionService, @@ -62,8 +60,8 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews super(); this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews); - this._register(_editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this)); - this._register(_editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this)); + this._register(_editorService.onDidActiveEditorChange(this.updateWebviewViewStates, this)); + this._register(_editorService.onDidVisibleEditorsChange(this.updateWebviewViewStates, this)); // This reviver's only job is to activate webview extensions // This should trigger the real reviver to be registered from the extension host side. @@ -246,70 +244,41 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews }); } - private onActiveEditorChanged() { + private updateWebviewViewStates() { const activeEditor = this._editorService.activeControl; - let newActiveWebview: { input: WebviewEditorInput, handle: WebviewPanelHandle } | undefined = undefined; - if (activeEditor && activeEditor.input instanceof WebviewEditorInput) { - for (const handle of map.keys(this._webviewEditorInputs)) { - const input = this._webviewEditorInputs.get(handle)!; - if (input.matches(activeEditor.input)) { - newActiveWebview = { input, handle }; - break; - } - } - } - - if (newActiveWebview && newActiveWebview.handle === this._activeWebview) { - // Webview itself unchanged but position may have changed - this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, { - active: true, - visible: true, - position: editorGroupToViewColumn(this._editorGroupService, newActiveWebview.input.group || 0) - }); - return; - } - // Broadcast view state update for currently active - if (typeof this._activeWebview !== 'undefined') { - const oldActiveWebview = this._webviewEditorInputs.get(this._activeWebview); - if (oldActiveWebview) { - this._proxy.$onDidChangeWebviewPanelViewState(this._activeWebview, { - active: false, - visible: this._editorService.visibleControls.some(editor => !!editor.input && editor.input.matches(oldActiveWebview)), - position: editorGroupToViewColumn(this._editorGroupService, oldActiveWebview.group || 0), + const webviews = new Map(); + for (const group of this._editorGroupService.groups) { + for (const input of group.editors) { + if (!(input instanceof WebviewEditorInput)) { + continue; + } + webviews.set(input, { + group: group.id, + visible: input === group.activeEditor, + active: !!activeEditor && input === activeEditor.input }); } } - // Then for newly active - if (newActiveWebview) { - this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, { - active: true, - visible: true, - position: editorGroupToViewColumn(this._editorGroupService, activeEditor ? activeEditor.group : ACTIVE_GROUP), - }); - this._activeWebview = newActiveWebview.handle; - } else { - this._activeWebview = undefined; - } - } - - private onVisibleEditorsChanged(): void { - this._webviewEditorInputs.forEach((input, handle) => { - for (const workbenchEditor of this._editorService.visibleControls) { - if (workbenchEditor.input && workbenchEditor.input.matches(input)) { - const editorPosition = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group!); - - input.updateGroup(workbenchEditor.group!.id); + for (const webview of map.keys(webviews)) { + const state = webviews.get(webview)!; + for (const handle of map.keys(this._webviewEditorInputs)) { + const input = this._webviewEditorInputs.get(handle)!; + if (input === webview) { this._proxy.$onDidChangeWebviewPanelViewState(handle, { - active: handle === this._activeWebview, - visible: true, - position: editorPosition + active: state.active, + visible: state.visible, + position: editorGroupToViewColumn(this._editorGroupService, state.group || 0), }); break; } } - }); + } } private onDidClickLink(handle: WebviewPanelHandle, link: URI): void { diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 44b99bcbd4c59..adfe974bcf456 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -89,11 +89,12 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel { private readonly _options: vscode.WebviewPanelOptions; private readonly _webview: ExtHostWebview; - private _isDisposed: boolean = false; private _viewColumn: vscode.ViewColumn | undefined; private _visible: boolean = true; private _active: boolean = true; + _isDisposed: boolean = false; + readonly _onDisposeEmitter = new Emitter(); public readonly onDidDispose: Event = this._onDisposeEmitter.event; @@ -302,7 +303,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { newState: WebviewPanelViewState ): void { const panel = this.getWebviewPanel(handle); - if (!panel) { + if (!panel || panel._isDisposed) { return; } From 1c05a14d3cacb19dacbe614056d67d3905d9e71d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 16:00:23 -0700 Subject: [PATCH 425/613] Don't track webviews separately from inputs The webviews should always be accessible through the inputs --- src/vs/workbench/api/browser/mainThreadWebview.ts | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index a405a4b108c9f..a87e8ba83bea3 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -44,7 +44,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews private readonly _proxy: ExtHostWebviewsShape; private readonly _webviewEditorInputs = new Map(); - private readonly _webviews = new Map(); private readonly _revivers = new Map(); constructor( @@ -103,7 +102,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews this.hookupWebviewEventDelegate(handle, webview); this._webviewEditorInputs.set(handle, webview); - this._webviews.set(handle, webview.webview); /* __GDPR__ "webviews:createWebviewPanel" : { @@ -174,7 +172,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews const handle = `revival-${MainThreadWebviews.revivalPool++}`; this._webviewEditorInputs.set(handle, webviewEditorInput); - this._webviews.set(handle, webviewEditorInput.webview); this.hookupWebviewEventDelegate(handle, webviewEditorInput); let state = undefined; @@ -232,7 +229,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews input.onDispose(() => { this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { this._webviewEditorInputs.delete(handle); - this._webviews.delete(handle); }); }); input.webview.onDidUpdateState((newState: any) => { @@ -315,15 +311,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews } private getWebview(handle: WebviewPanelHandle): Webview { - const webview = this.tryGetWebview(handle); - if (!webview) { - throw new Error('Unknown webview handle:' + handle); - } - return webview; - } - - private tryGetWebview(handle: WebviewPanelHandle): Webview | undefined { - return this._webviews.get(handle); + return this.getWebviewEditorInput(handle).webview; } private static getDeserializationFailedContents(viewType: string) { From 4d9460470a66905a7d7950dcc35bb42ddf1fbfe5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 16:01:31 -0700 Subject: [PATCH 426/613] Simplify csp for getDeserializationFailedContents This page should never contain anything except text --- src/vs/workbench/api/browser/mainThreadWebview.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index a87e8ba83bea3..cd44af83844e5 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -318,9 +318,8 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews return ` - - + ${localize('errorMessage', "An error occurred while restoring view:{0}", viewType)} `; From b1dd95c883668f6014973908f7c27cfe0a9d8cda Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 16:20:42 -0700 Subject: [PATCH 427/613] Fire a single batched event internally to update all webview view states Previously we fired one event per webview. This change switches to fire on event per update --- .../api/browser/mainThreadWebview.ts | 46 +++++++++---------- .../workbench/api/common/extHost.protocol.ts | 12 +++-- src/vs/workbench/api/common/extHostWebview.ts | 40 ++++++++-------- 3 files changed, 48 insertions(+), 50 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index cd44af83844e5..fdff6a4e3871b 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -12,7 +12,7 @@ import { localize } from 'vs/nls'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions, WebviewPanelViewStateData } from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; @@ -23,6 +23,7 @@ import { extHostNamedCustomer } from '../common/extHostCustomers'; import { IProductService } from 'vs/platform/product/common/product'; import { startsWith } from 'vs/base/common/strings'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; +import { find } from 'vs/base/common/arrays'; interface OldMainThreadWebviewState { readonly viewType: string; @@ -241,40 +242,35 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews } private updateWebviewViewStates() { - const activeEditor = this._editorService.activeControl; + if (!this._webviewEditorInputs.size) { + return; + } - const webviews = new Map(); + const activeInput = this._editorService.activeControl && this._editorService.activeControl.input; + const viewStates: WebviewPanelViewStateData = {}; for (const group of this._editorGroupService.groups) { for (const input of group.editors) { if (!(input instanceof WebviewEditorInput)) { continue; } - webviews.set(input, { - group: group.id, - visible: input === group.activeEditor, - active: !!activeEditor && input === activeEditor.input - }); - } - } - for (const webview of map.keys(webviews)) { - const state = webviews.get(webview)!; - for (const handle of map.keys(this._webviewEditorInputs)) { - const input = this._webviewEditorInputs.get(handle)!; - if (input === webview) { - this._proxy.$onDidChangeWebviewPanelViewState(handle, { - active: state.active, - visible: state.visible, - position: editorGroupToViewColumn(this._editorGroupService, state.group || 0), - }); - break; + const handle = find( + map.keys(this._webviewEditorInputs), + handle => input === this._webviewEditorInputs.get(handle)); + + if (handle) { + viewStates[handle] = { + visible: input === group.activeEditor, + active: input === activeInput, + position: editorGroupToViewColumn(this._editorGroupService, group.id || 0), + }; } } } + + if (Object.keys(viewStates).length) { + this._proxy.$onDidChangeWebviewPanelViewStates(viewStates); + } } private onDidClickLink(handle: WebviewPanelHandle, link: URI): void { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 055786be74228..b10008dbe563a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -549,15 +549,17 @@ export interface MainThreadWebviewsShape extends IDisposable { $unregisterSerializer(viewType: string): void; } -export interface WebviewPanelViewState { - readonly active: boolean; - readonly visible: boolean; - readonly position: EditorViewColumn; +export interface WebviewPanelViewStateData { + [handle: string]: { + readonly active: boolean; + readonly visible: boolean; + readonly position: EditorViewColumn; + }; } export interface ExtHostWebviewsShape { $onMessage(handle: WebviewPanelHandle, message: any): void; - $onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, newState: WebviewPanelViewState): void; + $onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void; $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; } diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index adfe974bcf456..d0eea9511b9ae 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -5,15 +5,15 @@ import { Emitter, Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; +import * as modes from 'vs/editor/common/modes'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; +import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview'; import * as vscode from 'vscode'; -import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol'; +import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol'; import { Disposable } from './extHostTypes'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import * as modes from 'vs/editor/common/modes'; -import { WebviewInitData, asWebviewUri } from 'vs/workbench/api/common/shared/webview'; -import { generateUuid } from 'vs/base/common/uuid'; type IconPath = URI | { light: URI, dark: URI }; @@ -298,21 +298,21 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { } } - public $onDidChangeWebviewPanelViewState( - handle: WebviewPanelHandle, - newState: WebviewPanelViewState - ): void { - const panel = this.getWebviewPanel(handle); - if (!panel || panel._isDisposed) { - return; - } - - const viewColumn = typeConverters.ViewColumn.to(newState.position); - if (panel.active !== newState.active || panel.visible !== newState.visible || panel.viewColumn !== viewColumn) { - panel._setActive(newState.active); - panel._setVisible(newState.visible); - panel._setViewColumn(viewColumn); - panel._onDidChangeViewStateEmitter.fire({ webviewPanel: panel }); + public $onDidChangeWebviewPanelViewStates(newStates: WebviewPanelViewStateData): void { + for (const handle of Object.keys(newStates)) { + const panel = this.getWebviewPanel(handle); + if (!panel || panel._isDisposed) { + continue; + } + + const newState = newStates[handle]; + const viewColumn = typeConverters.ViewColumn.to(newState.position); + if (panel.active !== newState.active || panel.visible !== newState.visible || panel.viewColumn !== viewColumn) { + panel._setActive(newState.active); + panel._setVisible(newState.visible); + panel._setViewColumn(viewColumn); + panel._onDidChangeViewStateEmitter.fire({ webviewPanel: panel }); + } } } From d241987248081955f2f53863351117c2bbc7dc3b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 16:27:43 -0700 Subject: [PATCH 428/613] Use bi-direcitonal map for mapping webviews to handles --- .../api/browser/mainThreadWebview.ts | 54 ++++++++++++++----- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index fdff6a4e3871b..78a62fbba9bd3 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -5,31 +5,62 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import * as map from 'vs/base/common/map'; +import { startsWith } from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as modes from 'vs/editor/common/modes'; import { localize } from 'vs/nls'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IProductService } from 'vs/platform/product/common/product'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions, WebviewPanelViewStateData } from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; +import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { extHostNamedCustomer } from '../common/extHostCustomers'; -import { IProductService } from 'vs/platform/product/common/product'; -import { startsWith } from 'vs/base/common/strings'; -import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; -import { find } from 'vs/base/common/arrays'; interface OldMainThreadWebviewState { readonly viewType: string; state: any; } +/** + * Bi-directional map between webview handles and inputs. + */ +class WebviewHandleStore { + private readonly _handlesToInputs = new Map(); + private readonly _inputsToHandles = new Map(); + + public add(handle: string, input: WebviewEditorInput): void { + this._handlesToInputs.set(handle, input); + this._inputsToHandles.set(input, handle); + } + + public getHandleForInput(input: WebviewEditorInput): string | undefined { + return this._inputsToHandles.get(input); + } + + public getInputForHandle(handle: string): WebviewEditorInput | undefined { + return this._handlesToInputs.get(handle); + } + + public delete(handle: string): void { + const input = this.getInputForHandle(handle); + this._handlesToInputs.delete(handle); + if (input) { + this._inputsToHandles.delete(input); + } + } + + public get size(): number { + return this._handlesToInputs.size; + } +} + @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape { @@ -44,7 +75,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews private static revivalPool = 0; private readonly _proxy: ExtHostWebviewsShape; - private readonly _webviewEditorInputs = new Map(); + private readonly _webviewEditorInputs = new WebviewHandleStore(); private readonly _revivers = new Map(); constructor( @@ -102,7 +133,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews }); this.hookupWebviewEventDelegate(handle, webview); - this._webviewEditorInputs.set(handle, webview); + this._webviewEditorInputs.add(handle, webview); /* __GDPR__ "webviews:createWebviewPanel" : { @@ -172,7 +203,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews } const handle = `revival-${MainThreadWebviews.revivalPool++}`; - this._webviewEditorInputs.set(handle, webviewEditorInput); + this._webviewEditorInputs.add(handle, webviewEditorInput); this.hookupWebviewEventDelegate(handle, webviewEditorInput); let state = undefined; @@ -254,10 +285,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews continue; } - const handle = find( - map.keys(this._webviewEditorInputs), - handle => input === this._webviewEditorInputs.get(handle)); - + const handle = this._webviewEditorInputs.getHandleForInput(input); if (handle) { viewStates[handle] = { visible: input === group.activeEditor, @@ -303,7 +331,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews } private tryGetWebviewEditorInput(handle: WebviewPanelHandle): WebviewEditorInput | undefined { - return this._webviewEditorInputs.get(handle); + return this._webviewEditorInputs.getInputForHandle(handle); } private getWebview(handle: WebviewPanelHandle): Webview { From 6e530127a1bb8ffbd1bfb77dc680c321dc0d71f5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 20 Aug 2019 16:32:23 -0700 Subject: [PATCH 429/613] Make sure we update the webview's internally tracked group for restoration --- src/vs/workbench/api/browser/mainThreadWebview.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 78a62fbba9bd3..4c5b97e060de9 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -285,6 +285,8 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews continue; } + input.updateGroup(group.id); + const handle = this._webviewEditorInputs.getHandleForInput(input); if (handle) { viewStates[handle] = { From 74ddff2d2ee83044be42ec881ddcf3c0a9b1f3c3 Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Tue, 20 Aug 2019 16:42:26 -0700 Subject: [PATCH 430/613] chore: upgrade native-watchdog@1.1.0 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 559c11a1763d9..959edd7a376cb 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "keytar": "^4.11.0", "native-is-elevated": "0.3.0", "native-keymap": "2.0.0", - "native-watchdog": "1.0.0", + "native-watchdog": "1.1.0", "node-pty": "0.9.0-beta19", "nsfw": "1.2.5", "onigasm-umd": "^2.2.2", diff --git a/yarn.lock b/yarn.lock index 90211b1e51536..a7c03232237c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6060,10 +6060,10 @@ native-keymap@2.0.0: resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-2.0.0.tgz#7491ba8f9cc75bd6ada7e754dadb7716c793a3e3" integrity sha512-KIlDZp0yKaHaGIkEVdlYN3QIaZICXwG1qh/oeBeQdM8TwAi90IAZlAD57qsNDkEvIJIzerCzb5jYYQAdHGBgYg== -native-watchdog@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.0.0.tgz#97344e83cd6815a8c8e6c44a52e7be05832e65ca" - integrity sha512-HKQATz5KLUMPyQQ/QaalzgTXaGz2plYPBxjyalaR4ECIu/UznXY8YJD+a9SLkkcvtxnJ8/zHLY3xik06vUZ7uA== +native-watchdog@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.1.0.tgz#aea2a4bc7ddd69b774682ae7705d91fa5ac0dcb6" + integrity sha512-cMJryYPR+8rtCqU3yAfGS3sCO6uA+fsrSv9MgBG8JE2cu4hwOWBhvR44IhUeJsGrwrNkgbeok9RYmyb/5LttSQ== natural-compare@^1.4.0: version "1.4.0" From 62461ec4af255484d30eee4403b90e0f12f4b05c Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Tue, 20 Aug 2019 17:41:19 -0700 Subject: [PATCH 431/613] Refactor OpenerService. Fix #79487 --- .../editor/browser/services/openerService.ts | 104 ++++-------- .../standalone/browser/standaloneEditor.ts | 11 +- .../browser/services/openerService.test.ts | 143 ++++------------- src/vs/platform/opener/common/opener.ts | 11 ++ .../contrib/url/common/url.contribution.ts | 148 +++++++++++++++--- 5 files changed, 197 insertions(+), 220 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 7632af1b45cab..1a57b3731f5f9 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -4,37 +4,30 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { LinkedList } from 'vs/base/common/linkedList'; import { parse } from 'vs/base/common/marshalling'; import { Schemas } from 'vs/base/common/network'; import * as resources from 'vs/base/common/resources'; +import { equalsIgnoreCase } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; -import { IOpenerService, IOpener } from 'vs/platform/opener/common/opener'; -import { equalsIgnoreCase } from 'vs/base/common/strings'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { LinkedList } from 'vs/base/common/linkedList'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { localize } from 'vs/nls'; -import { IProductService } from 'vs/platform/product/common/product'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import Severity from 'vs/base/common/severity'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IOpener, IOpenerService, IValidator } from 'vs/platform/opener/common/opener'; -export class OpenerService implements IOpenerService { +export class OpenerService extends Disposable implements IOpenerService { _serviceBrand!: ServiceIdentifier; private readonly _opener = new LinkedList(); + private readonly _validatorMap = new Map>(); constructor( @ICodeEditorService private readonly _editorService: ICodeEditorService, @ICommandService private readonly _commandService: ICommandService, - @IStorageService private readonly _storageService: IStorageService, - @IDialogService private readonly _dialogService: IDialogService, - @IProductService private readonly _productService: IProductService ) { - // + super(); } registerOpener(opener: IOpener): IDisposable { @@ -42,11 +35,30 @@ export class OpenerService implements IOpenerService { return { dispose: remove }; } + registerValidator(uriScheme: string, validator: IValidator): IDisposable { + if (!this._validatorMap.has(uriScheme)) { + this._validatorMap.set(uriScheme, new LinkedList()); + } + const remove = this._validatorMap.get(uriScheme)!.push(validator); + return { dispose: remove }; + } + async open(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise { // no scheme ?!? if (!resource.scheme) { return Promise.resolve(false); } + + // check with contributed validators + if (this._validatorMap.has(resource.scheme)) { + const validators = this._validatorMap.get(resource.scheme)!.toArray(); + for (const validator of validators) { + if (!(await validator.shouldOpen(resource))) { + return false; + } + } + } + // check with contributed openers for (const opener of this._opener.toArray()) { const handled = await opener.open(resource, options); @@ -60,7 +72,7 @@ export class OpenerService implements IOpenerService { private _doOpen(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise { - const { scheme, authority, path, query, fragment } = resource; + const { scheme, path, query, fragment } = resource; if (equalsIgnoreCase(scheme, Schemas.mailto) || (options && options.openExternal)) { // open default mail application @@ -68,48 +80,8 @@ export class OpenerService implements IOpenerService { } if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) { - let trustedDomains: string[] = ['https://code.visualstudio.com']; - try { - const trustedDomainsSrc = this._storageService.get('http.trustedDomains', StorageScope.GLOBAL); - if (trustedDomainsSrc) { - trustedDomains = JSON.parse(trustedDomainsSrc); - } - } catch (err) { } - - const domainToOpen = `${scheme}://${authority}`; - - if (isDomainTrusted(domainToOpen, trustedDomains)) { - return this._doOpenExternal(resource); - } else { - return this._dialogService.show( - Severity.Info, - localize( - 'openExternalLinkAt', - 'Do you want {0} to open the external website?\n{1}', - this._productService.nameShort, - resource.toString(true) - ), - [ - localize('openLink', 'Open Link'), - localize('cancel', 'Cancel'), - localize('configureTrustedDomains', 'Configure Trusted Domains') - ], - { - cancelId: 1 - }).then((choice) => { - if (choice === 0) { - return this._doOpenExternal(resource); - } else if (choice === 2) { - return this._commandService.executeCommand('workbench.action.configureTrustedDomains', domainToOpen).then((pickedDomains: string[]) => { - if (pickedDomains.indexOf(domainToOpen) !== -1) { - return this._doOpenExternal(resource); - } - return Promise.resolve(false); - }); - } - return Promise.resolve(false); - }); - } + // open link in default browser + return this._doOpenExternal(resource); } else if (equalsIgnoreCase(scheme, Schemas.command)) { // run command or bail out if command isn't known if (!CommandsRegistry.getCommand(path)) { @@ -158,22 +130,8 @@ export class OpenerService implements IOpenerService { return Promise.resolve(true); } -} - -/** - * Check whether a domain like https://www.microsoft.com matches - * the list of trusted domains. - */ -function isDomainTrusted(domain: string, trustedDomains: string[]) { - for (let i = 0; i < trustedDomains.length; i++) { - if (trustedDomains[i] === '*') { - return true; - } - if (trustedDomains[i] === domain) { - return true; - } + dispose() { + this._validatorMap.clear(); } - - return false; } diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index ddba66741d915..8750db8f94301 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -38,9 +38,6 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IProductService } from 'vs/platform/product/common/product'; -import { IStorageService } from 'vs/platform/storage/common/storage'; type Omit = Pick>; @@ -54,13 +51,7 @@ function withAllStandaloneServices(domElement: H } if (!services.has(IOpenerService)) { - services.set(IOpenerService, new OpenerService( - services.get(ICodeEditorService), - services.get(ICommandService), - services.get(IStorageService), - services.get(IDialogService), - services.get(IProductService) - )); + services.set(IOpenerService, new OpenerService(services.get(ICodeEditorService), services.get(ICommandService))); } let result = callback(services); diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index 3e1ef9e80cbe3..19b679643cdee 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -7,10 +7,6 @@ import { URI } from 'vs/base/common/uri'; import { OpenerService } from 'vs/editor/browser/services/openerService'; import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices'; import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands'; -import { deepClone } from 'vs/base/common/objects'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IProductService } from 'vs/platform/product/common/product'; -import { IStorageService } from 'vs/platform/storage/common/storage'; suite('OpenerService', function () { @@ -28,51 +24,6 @@ suite('OpenerService', function () { } }; - function getStorageService(trustedDomainsSetting: string[]) { - let _settings = deepClone(trustedDomainsSetting); - - return new class implements IStorageService { - get = () => JSON.stringify(_settings); - store = (key: string, val: string) => _settings = JSON.parse(val); - - // Don't care - _serviceBrand: any; - - onDidChangeStorage = () => ({ dispose: () => { } }); - onWillSaveState = () => ({ dispose: () => { } }); - - getBoolean = () => true; - getNumber = () => 0; - remove = () => { }; - logStorage = () => { }; - }; - } - - function getDialogService() { - return new class implements IDialogService { - _showInvoked = 0; - show = () => { - this._showInvoked++; - return Promise.resolve({} as any); - } - get confirmInvoked() { return this._showInvoked; } - - // Don't care - _serviceBrand: any; - confirm = () => { - return Promise.resolve({} as any); - } - }; - } - - function getProductService(): IProductService { - return new class { - nameShort: 'VS Code'; - - _serviceBrand: any; - } as IProductService; - } - setup(function () { lastCommand = undefined; @@ -81,10 +32,7 @@ suite('OpenerService', function () { test('delegate to editorService, scheme:///fff', function () { const openerService = new OpenerService( editorService, - NullCommandService, - getStorageService([]), - getDialogService(), - getProductService() + NullCommandService ); openerService.open(URI.parse('another:///somepath')); assert.equal(editorService.lastInput!.options!.selection, undefined); @@ -94,10 +42,7 @@ suite('OpenerService', function () { const openerService = new OpenerService( editorService, - NullCommandService, - getStorageService([]), - getDialogService(), - getProductService() + NullCommandService ); openerService.open(URI.parse('file:///somepath#L23')); @@ -123,10 +68,7 @@ suite('OpenerService', function () { const openerService = new OpenerService( editorService, - NullCommandService, - getStorageService([]), - getDialogService(), - getProductService() + NullCommandService ); openerService.open(URI.parse('file:///somepath#23')); @@ -148,10 +90,7 @@ suite('OpenerService', function () { const openerService = new OpenerService( editorService, - commandService, - getStorageService([]), - getDialogService(), - getProductService() + commandService ); const id = `aCommand${Math.random()}`; @@ -173,69 +112,41 @@ suite('OpenerService', function () { assert.equal(lastCommand!.args[1], true); }); - test('links are protected by dialog.show', function () { - const dialogService = getDialogService(); - const openerService = new OpenerService( - editorService, - commandService, - getStorageService([]), - dialogService, - getProductService() - ); - - openerService.open(URI.parse('https://www.microsoft.com')); - assert.equal(dialogService.confirmInvoked, 1); - }); - - test('links on the whitelisted domains can be opened without dialog.show', function () { - const dialogService = getDialogService(); + test('links are protected by validators', async function () { const openerService = new OpenerService( editorService, - commandService, - getStorageService(['https://microsoft.com']), - dialogService, - getProductService() + commandService ); - openerService.open(URI.parse('https://microsoft.com')); - openerService.open(URI.parse('https://microsoft.com/')); - openerService.open(URI.parse('https://microsoft.com/en-us/')); - openerService.open(URI.parse('https://microsoft.com/en-us/?foo=bar')); - openerService.open(URI.parse('https://microsoft.com/en-us/?foo=bar#baz')); + openerService.registerValidator('http', { shouldOpen: () => Promise.resolve(false) }); + openerService.registerValidator('https', { shouldOpen: () => Promise.resolve(false) }); - assert.equal(dialogService.confirmInvoked, 0); + const httpResult = await openerService.open(URI.parse('https://www.microsoft.com')); + assert.equal(httpResult, false); + const httpsResult = await openerService.open(URI.parse('https://www.microsoft.com')); + assert.equal(httpsResult, false); }); - test('variations of links are protected by dialog confirmation', function () { - const dialogService = getDialogService(); + test('links validated by validators go to openers', async function () { const openerService = new OpenerService( editorService, - commandService, - getStorageService(['https://microsoft.com']), - dialogService, - getProductService() + commandService ); - openerService.open(URI.parse('http://microsoft.com')); - openerService.open(URI.parse('https://www.microsoft.com')); - - assert.equal(dialogService.confirmInvoked, 2); - }); - - test('* removes all link protection', function () { - const dialogService = getDialogService(); - const openerService = new OpenerService( - editorService, - commandService, - getStorageService(['*']), - dialogService, - getProductService() - ); + openerService.registerValidator('http', { shouldOpen: () => Promise.resolve(true) }); + openerService.registerValidator('https', { shouldOpen: () => Promise.resolve(true) }); - openerService.open(URI.parse('https://code.visualstudio.com/')); - openerService.open(URI.parse('https://www.microsoft.com')); - openerService.open(URI.parse('https://www.github.com')); + let openCount = 0; + openerService.registerOpener({ + open: (resource: URI) => { + openCount++; + return Promise.resolve(true); + } + }); - assert.equal(dialogService.confirmInvoked, 0); + await openerService.open(URI.parse('http://microsoft.com')); + assert.equal(openCount, 1); + await openerService.open(URI.parse('https://microsoft.com')); + assert.equal(openCount, 2); }); }); diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 17cea7f5b3abe..d77caa7852d9d 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -14,6 +14,10 @@ export interface IOpener { open(resource: URI, options?: { openExternal?: boolean }): Promise; } +export interface IValidator { + shouldOpen(resource: URI): Promise; +} + export interface IOpenerService { _serviceBrand: any; @@ -23,6 +27,12 @@ export interface IOpenerService { */ registerOpener(opener: IOpener): IDisposable; + /** + * Register a participant that can validate if the URI resource be opened. + * validators are run before openers. + */ + registerValidator(uriScheme: string, validator: IValidator): IDisposable; + /** * Opens a resource, like a webaddress, a document uri, or executes command. * @@ -36,5 +46,6 @@ export interface IOpenerService { export const NullOpenerService: IOpenerService = Object.freeze({ _serviceBrand: undefined, registerOpener() { return { dispose() { } }; }, + registerValidator() { return { dispose() { } }; }, open() { return Promise.resolve(false); }, }); diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 475265542b12f..02fae3d3fc9ff 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -14,17 +14,24 @@ import { Action } from 'vs/base/common/actions'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IProductService } from 'vs/platform/product/common/product'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { equalsIgnoreCase } from 'vs/base/common/strings'; +import { Schemas } from 'vs/base/common/network'; +import Severity from 'vs/base/common/severity'; export class OpenUrlAction extends Action { - static readonly ID = 'workbench.action.url.openUrl'; - static readonly LABEL = localize('openUrl', "Open URL"); + static readonly LABEL = localize('openUrl', 'Open URL'); constructor( id: string, label: string, @IURLService private readonly urlService: IURLService, - @IQuickInputService private readonly quickInputService: IQuickInputService, + @IQuickInputService private readonly quickInputService: IQuickInputService ) { super(id, label); } @@ -66,7 +73,7 @@ const configureTrustedDomainsHandler = ( type: 'item', label: d, id: d, - picked: true, + picked: true }; }); @@ -91,23 +98,26 @@ const configureTrustedDomainsHandler = ( specialQuickPickItems.push(domainToConfigureItem); } - const quickPickItems: (IQuickPickItem | IQuickPickSeparator)[] = domainQuickPickItems.length === 0 - ? specialQuickPickItems - : [...specialQuickPickItems, { type: 'separator' }, ...domainQuickPickItems]; - - return quickInputService.pick(quickPickItems, { - canPickMany: true, - activeItem: domainToConfigureItem - }).then(result => { - if (result) { - const pickedDomains = result.map(r => r.id); - storageService.store('http.trustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL); - - return pickedDomains; - } - - return []; - }); + const quickPickItems: (IQuickPickItem | IQuickPickSeparator)[] = + domainQuickPickItems.length === 0 + ? specialQuickPickItems + : [...specialQuickPickItems, { type: 'separator' }, ...domainQuickPickItems]; + + return quickInputService + .pick(quickPickItems, { + canPickMany: true, + activeItem: domainToConfigureItem + }) + .then(result => { + if (result) { + const pickedDomains: string[] = result.map(r => r.id!); + storageService.store('http.trustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL); + + return pickedDomains; + } + + return []; + }); }; const configureTrustedDomainCommand = { @@ -131,3 +141,99 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { title: configureTrustedDomainCommand.description.description } }); + +class OpenerValidatorContributions implements IWorkbenchContribution { + constructor( + @IOpenerService private readonly _openerService: IOpenerService, + @IStorageService private readonly _storageService: IStorageService, + @IDialogService private readonly _dialogService: IDialogService, + @IProductService private readonly _productService: IProductService, + @IQuickInputService private readonly _quickInputService: IQuickInputService + ) { + this._openerService.registerValidator('http', { shouldOpen: r => this.validateLink(r) }); + this._openerService.registerValidator('https', { shouldOpen: r => this.validateLink(r) }); + } + + validateLink(resource: URI): Promise { + const { scheme, authority } = resource; + + if (!equalsIgnoreCase(scheme, Schemas.http) && !equalsIgnoreCase(scheme, Schemas.https)) { + return Promise.resolve(false); + } + + let trustedDomains: string[] = [VSCODE_DOMAIN]; + try { + const trustedDomainsSrc = this._storageService.get('http.trustedDomains', StorageScope.GLOBAL); + if (trustedDomainsSrc) { + trustedDomains = JSON.parse(trustedDomainsSrc); + } + } catch (err) { } + + const domainToOpen = `${scheme}://${authority}`; + + if (isDomainTrusted(domainToOpen, trustedDomains)) { + return Promise.resolve(true); + } else { + return this._dialogService + .show( + Severity.Info, + localize( + 'openExternalLinkAt', + 'Do you want {0} to open the external website?\n{1}', + this._productService.nameShort, + resource.toString(true) + ), + [ + localize('openLink', 'Open Link'), + localize('cancel', 'Cancel'), + localize('configureTrustedDomains', 'Configure Trusted Domains') + ], + { + cancelId: 1 + } + ) + .then(choice => { + // Open Link + if (choice === 0) { + return true; + } + // Configure Trusted Domains + else if (choice === 2) { + return configureTrustedDomainsHandler(this._quickInputService, this._storageService, domainToOpen).then( + (pickedDomains: string[]) => { + if (pickedDomains.indexOf(domainToOpen) !== -1) { + return true; + } + return false; + } + ); + } + + return false; + }); + } + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + OpenerValidatorContributions, + LifecyclePhase.Restored +); + +/** + * Check whether a domain like https://www.microsoft.com matches + * the list of trusted domains. + */ +function isDomainTrusted(domain: string, trustedDomains: string[]) { + for (let i = 0; i < trustedDomains.length; i++) { + if (trustedDomains[i] === '*') { + return true; + } + + if (trustedDomains[i] === domain) { + return true; + } + } + + return false; +} From 9ecd1be8c14091832fae31fc1f7e7a9d4ee5fd1f Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Tue, 20 Aug 2019 17:58:35 -0700 Subject: [PATCH 432/613] Just simple object, no need for Map --- src/vs/editor/browser/services/openerService.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 1a57b3731f5f9..8668c51a26c99 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -21,7 +21,7 @@ export class OpenerService extends Disposable implements IOpenerService { _serviceBrand!: ServiceIdentifier; private readonly _opener = new LinkedList(); - private readonly _validatorMap = new Map>(); + private readonly _validatorMap: { [k: string]: LinkedList } = {}; constructor( @ICodeEditorService private readonly _editorService: ICodeEditorService, @@ -36,10 +36,10 @@ export class OpenerService extends Disposable implements IOpenerService { } registerValidator(uriScheme: string, validator: IValidator): IDisposable { - if (!this._validatorMap.has(uriScheme)) { - this._validatorMap.set(uriScheme, new LinkedList()); + if (!this._validatorMap[uriScheme]) { + this._validatorMap[uriScheme] = new LinkedList(); } - const remove = this._validatorMap.get(uriScheme)!.push(validator); + const remove = this._validatorMap[uriScheme].push(validator); return { dispose: remove }; } @@ -50,8 +50,8 @@ export class OpenerService extends Disposable implements IOpenerService { } // check with contributed validators - if (this._validatorMap.has(resource.scheme)) { - const validators = this._validatorMap.get(resource.scheme)!.toArray(); + if (this._validatorMap[resource.scheme]) { + const validators = this._validatorMap[resource.scheme].toArray(); for (const validator of validators) { if (!(await validator.shouldOpen(resource))) { return false; @@ -132,6 +132,8 @@ export class OpenerService extends Disposable implements IOpenerService { } dispose() { - this._validatorMap.clear(); + for (let key in this._validatorMap) { + delete this._validatorMap[key]; + } } } From 2ebaa78e8753efa3c224a755d2f8527486f76afd Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Tue, 20 Aug 2019 20:57:33 -0500 Subject: [PATCH 433/613] Don't auto expand if it's root in indexTreeModel. Fixes #79540 --- src/vs/base/browser/ui/tree/indexTreeModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index a568881e7c2ff..bdf19be09f5de 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -219,7 +219,7 @@ export class IndexTreeModel, TFilterData = voi const result = this._setListNodeCollapsed(node, listIndex, revealed, collapsed!, recursive || false); - if (this.autoExpandSingleChildren && !collapsed! && !recursive) { + if (node !== this.root && this.autoExpandSingleChildren && !collapsed! && !recursive) { let onlyVisibleChildIndex = -1; for (let i = 0; i < node.children.length; i++) { From 6e6c3e7acba6f989b140e08bbdc62314349cc134 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 20 Aug 2019 19:33:37 -0700 Subject: [PATCH 434/613] unset VSCODE_LOGS to fix spdlog issue --- scripts/code.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/code.bat b/scripts/code.bat index 8c058365dca7f..f4689608e4a73 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -33,6 +33,7 @@ set VSCODE_CLI=1 set ELECTRON_DEFAULT_ERROR_MODE=1 set ELECTRON_ENABLE_LOGGING=1 set ELECTRON_ENABLE_STACK_DUMPING=1 +set VSCODE_LOGS= :: Launch Code From e563f98c1381b8d296543fd82c3ac225b7fe6c7e Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 20 Aug 2019 20:07:33 -0700 Subject: [PATCH 435/613] animation for single line and no luck for multiple line --- src/vs/editor/contrib/find/findWidget.css | 25 +++++++++++++++++++---- src/vs/editor/contrib/find/findWidget.ts | 8 ++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index d9eb731d1e309..d8f2f70a79831 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -32,11 +32,11 @@ .monaco-editor .find-widget { position: absolute; z-index: 10; - top: unset; - bottom: 10px; + top: -44px; + height: 33px; overflow: hidden; line-height: 19px; - transition: top bottom 200ms linear; + transition: top 200ms linear; padding: 0 4px; } @@ -45,6 +45,11 @@ } /* Find widget when replace is toggled on */ +.monaco-editor .find-widget.replaceToggled { + top: -74px; + /* find input height + replace input height + shadow (10px) */ +} + .monaco-editor .find-widget.replaceToggled > .replace-part { display: flex; display: -webkit-flex; @@ -52,7 +57,19 @@ .monaco-editor .find-widget.visible, .monaco-editor .find-widget.replaceToggled.visible { - top: 0; + top: 0px; +} + +/* Multiple line find widget */ + +.monaco-editor .find-widget.multipleline { + top: unset; + bottom: 10px; +} + +.monaco-editor .find-widget.multipleline.visible, +.monaco-editor .find-widget.multipleline.replaceToggled.visible { + top: 0px; bottom: unset; } diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index d56ad01239861..2ea8ba03dd73b 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -173,6 +173,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._buildDomNode(); this._updateButtons(); this._tryUpdateWidgetWidth(); + this._findInput.inputBox.layout(); this._register(this._codeEditor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => { if (e.readOnly) { @@ -288,6 +289,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas private _onStateChanged(e: FindReplaceStateChangedEvent): void { if (e.searchString) { + if (this._state.searchString.indexOf('\n') >= 0) { + dom.addClass(this._domNode, 'multipleline'); + } else { + dom.removeClass(this._domNode, 'multipleline'); + } + try { this._ignoreChangeEvent = true; this._findInput.setValue(this._state.searchString); @@ -312,6 +319,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._isReplaceVisible = true; this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode); this._updateButtons(); + this._replaceInput.inputBox.layout(); } } else { if (this._isReplaceVisible) { From 4de4433c711674fd33912aaa5a46a29a90a94377 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 20 Aug 2019 20:09:07 -0700 Subject: [PATCH 436/613] :lipstick: --- src/vs/editor/contrib/find/findWidget.css | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index d8f2f70a79831..d6c6f602e484a 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -46,10 +46,8 @@ /* Find widget when replace is toggled on */ .monaco-editor .find-widget.replaceToggled { - top: -74px; - /* find input height + replace input height + shadow (10px) */ + top: -74px; /* find input height + replace input height + shadow (10px) */ } - .monaco-editor .find-widget.replaceToggled > .replace-part { display: flex; display: -webkit-flex; @@ -57,7 +55,7 @@ .monaco-editor .find-widget.visible, .monaco-editor .find-widget.replaceToggled.visible { - top: 0px; + top: 0; } /* Multiple line find widget */ From 39e0512f232dc0dd72c9e10c665ba90eb9ba3c40 Mon Sep 17 00:00:00 2001 From: skprabhanjan Date: Wed, 21 Aug 2019 10:59:02 +0530 Subject: [PATCH 437/613] Made the function generic and passed hyphen as a paramater --- src/vs/base/common/search.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/base/common/search.ts b/src/vs/base/common/search.ts index 98aedcdef4a6b..0812df76b6e32 100644 --- a/src/vs/base/common/search.ts +++ b/src/vs/base/common/search.ts @@ -12,8 +12,8 @@ export function buildReplaceStringWithCasePreserved(matches: string[] | null, pa } else if (matches[0].toLowerCase() === matches[0]) { return pattern.toLowerCase(); } else if (strings.containsUppercaseCharacter(matches[0][0])) { - if (validateHyphenPattern(matches, pattern)) { - return buildReplaceStringForHyphenPatterns(matches, pattern); + if (validateSpecificSpecialCharacter(matches, pattern, '-')) { + return buildReplaceStringForSpecificSpecialCharacter(matches, pattern, '-'); } else { return pattern[0].toUpperCase() + pattern.substr(1); } @@ -26,19 +26,19 @@ export function buildReplaceStringWithCasePreserved(matches: string[] | null, pa } } -function validateHyphenPattern(matches: string[], pattern: string): boolean { - const doesConatinHyphen = matches[0].indexOf('-') !== -1 && pattern.indexOf('-') !== -1; - const doesConatinSameNumberOfHyphens = matches[0].split('-').length === pattern.split('-').length; - return doesConatinHyphen && doesConatinSameNumberOfHyphens; +function validateSpecificSpecialCharacter(matches: string[], pattern: string, specialCharacter: string): boolean { + const doesConatinSpecialCharacter = matches[0].indexOf(specialCharacter) !== -1 && pattern.indexOf(specialCharacter) !== -1; + const doesConatinSameNumberOfSpecialCharacters = matches[0].split(specialCharacter).length === pattern.split(specialCharacter).length; + return doesConatinSpecialCharacter && doesConatinSameNumberOfSpecialCharacters; } -function buildReplaceStringForHyphenPatterns(matches: string[], pattern: string): string { - const splitPatternAtHyphen = pattern.split('-'); - const splitMatchAtHyphen = matches[0].split('-'); +function buildReplaceStringForSpecificSpecialCharacter(matches: string[], pattern: string, specialCharacter: string): string { + const splitPatternAtSpecialCharacter = pattern.split(specialCharacter); + const splitMatchAtSpecialCharacter = matches[0].split(specialCharacter); let replaceString: string = ''; - - splitPatternAtHyphen.forEach((splitValues, index) => { - replaceString += buildReplaceStringWithCasePreserved([splitMatchAtHyphen[index]], splitValues) + '-'; + splitPatternAtSpecialCharacter.forEach((splitValue, index) => { + replaceString += buildReplaceStringWithCasePreserved([splitMatchAtSpecialCharacter[index]], splitValue) + specialCharacter; }); + return replaceString.slice(0, -1); } From 7621355f7e0c560dc30ad0c04f62d30bc1659b61 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 20 Aug 2019 22:37:19 -0700 Subject: [PATCH 438/613] Avoid redundant keymap change --- .../workbench/services/keybinding/browser/keymapService.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/keybinding/browser/keymapService.ts b/src/vs/workbench/services/keybinding/browser/keymapService.ts index c90e58fc0a967..e3744643efd64 100644 --- a/src/vs/workbench/services/keybinding/browser/keymapService.ts +++ b/src/vs/workbench/services/keybinding/browser/keymapService.ts @@ -176,6 +176,7 @@ export class BrowserKeyboardMapperFactoryBase { } setActiveKeyMapping(keymap: IKeyboardMapping | null) { + let keymapUpdated = false; let matchedKeyboardLayout = this.getMatchedKeymapInfo(keymap); if (matchedKeyboardLayout) { // let score = matchedKeyboardLayout.score; @@ -209,18 +210,21 @@ export class BrowserKeyboardMapperFactoryBase { if (!this._activeKeymapInfo) { this._activeKeymapInfo = matchedKeyboardLayout.result; + keymapUpdated = true; } else if (keymap) { if (matchedKeyboardLayout.result.getScore(keymap) > this._activeKeymapInfo.getScore(keymap)) { this._activeKeymapInfo = matchedKeyboardLayout.result; + keymapUpdated = true; } } } if (!this._activeKeymapInfo) { this._activeKeymapInfo = this.getUSStandardLayout(); + keymapUpdated = true; } - if (!this._activeKeymapInfo) { + if (!this._activeKeymapInfo || !keymapUpdated) { return; } From acb6d6209111ddc249ae14bc8a9d79a50ea4c80c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 21 Aug 2019 11:14:25 +0200 Subject: [PATCH 439/613] fix #79558 --- src/vs/base/common/filters.ts | 7 ++++++- src/vs/base/test/common/filters.test.ts | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index d01c32668caf6..fe233f874bf84 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -549,7 +549,7 @@ export function fuzzyScore(pattern: string, patternLow: string, patternPos: numb const patternStartPos = patternPos; const wordStartPos = wordPos; - // There will be a mach, fill in tables + // There will be a match, fill in tables for (patternPos = patternStartPos + 1; patternPos <= patternLen; patternPos++) { for (wordPos = 1; wordPos <= wordLen; wordPos++) { @@ -573,6 +573,11 @@ export function fuzzyScore(pattern: string, patternLow: string, patternPos: numb } else { score = 5; } + } else if (isSeparatorAtPos(wordLow, wordPos - 1) && (wordPos === 1 || !isSeparatorAtPos(wordLow, wordPos - 2))) { + // hitting a separator: `. <-> foo.bar` + // ^ + score = 5; + } else if (isSeparatorAtPos(wordLow, wordPos - 2) || isWhitespaceAtPos(wordLow, wordPos - 2)) { // post separator: `foo <-> bar_foo` // ^^^ diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index 25cc50d578043..42b741436610b 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -366,6 +366,10 @@ suite('Filters', () => { assertMatches('f', ':foo', ':^foo', fuzzyScore); }); + test('Separator only match should not be weak #79558', function () { + assertMatches('.', 'foo.bar', 'foo^.bar', fuzzyScore); + }); + test('Cannot set property \'1\' of undefined, #26511', function () { let word = new Array(123).join('a'); let pattern = new Array(120).join('a'); From 72803aaeee7a80838f373de7854b6e5723e798e6 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 21 Aug 2019 11:18:09 +0200 Subject: [PATCH 440/613] debug: always inline one session fixes #79121 --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 4786ae44870e8..c39bdb652ca97 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -599,7 +599,7 @@ class CallStackDataSource implements IAsyncDataSource 1 || this.debugService.getViewModel().isMultiSessionView()) { + if (sessions.length > 1) { return Promise.resolve(sessions.filter(s => !s.parentSession)); } From b8ec1d77339d61f4331c74b7bfc273fd0128bb44 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 21 Aug 2019 11:35:15 +0200 Subject: [PATCH 441/613] debug: callstack always inline thread children even if there are child sessions fixes #79121 --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index c39bdb652ca97..c89c4b8324724 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -593,7 +593,7 @@ class CallStackDataSource implements IAsyncDataSource { + async getChildren(element: IDebugModel | CallStackItem): Promise { if (isDebugModel(element)) { const sessions = element.getSessions(); if (sessions.length === 0) { @@ -609,9 +609,10 @@ class CallStackDataSource implements IAsyncDataSource s.parentSession === element); const threads: CallStackItem[] = element.getAllThreads(); - if (threads.length === 1 && childSessions.length === 0) { + if (threads.length === 1) { // Do not show thread when there is only one to be compact. - return this.getThreadChildren(threads[0]); + const children = await this.getThreadChildren(threads[0]); + return children.concat(childSessions); } return Promise.resolve(threads.concat(childSessions)); From 38aa4766107555dce5e16e15c94135a3c681e497 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 21 Aug 2019 12:14:32 +0200 Subject: [PATCH 442/613] debug: do not focus new session automatically fixes #79121 --- src/vs/workbench/contrib/debug/browser/debugService.ts | 6 +++--- src/vs/workbench/contrib/debug/browser/debugToolBar.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 7a0b0fe82c497..86c6bb089b607 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -479,11 +479,11 @@ export class DebugService implements IDebugService { }); } - private launchOrAttachToSession(session: IDebugSession, focus = true): Promise { + private launchOrAttachToSession(session: IDebugSession, forceFocus = false): Promise { const dbgr = this.configurationManager.getDebugger(session.configuration.type); return session.initialize(dbgr!).then(() => { return session.launchOrAttach(session.configuration).then(() => { - if (focus) { + if (forceFocus || !this.viewModel.focusedSession) { this.focusStackFrame(undefined, undefined, session); } }); @@ -572,7 +572,7 @@ export class DebugService implements IDebugService { return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.extensionHostDebugService.reload(session.getId()) : undefined); } - const shouldFocus = this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId(); + const shouldFocus = !!this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId(); // If the restart is automatic -> disconnect, otherwise -> terminate #55064 return (isAutoRestart ? session.disconnect(true) : session.terminate(true)).then(() => { diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 5146eac7f7810..ba384a37df3cc 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -131,6 +131,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { private registerListeners(): void { this._register(this.debugService.onDidChangeState(() => this.updateScheduler.schedule())); this._register(this.debugService.getViewModel().onDidFocusSession(() => this.updateScheduler.schedule())); + this._register(this.debugService.onDidNewSession(() => this.updateScheduler.schedule())); this._register(this.configurationService.onDidChangeConfiguration(e => this.onDidConfigurationChange(e))); this._register(this.actionBar.actionRunner.onDidRun((e: IRunEvent) => { // check for error From 9f7925556d519c79ea50dff5e6d31e9623946d14 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Aug 2019 12:30:03 +0200 Subject: [PATCH 443/613] Support window logging in web: - Implement File service based log service - Implement In memory log provider - Use in memory log provider for window logging --- src/vs/platform/log/common/fileLogService.ts | 130 ++++++++++++++++++ src/vs/workbench/browser/web.main.ts | 13 +- .../workbench/browser/web.simpleservices.ts | 8 +- .../contrib/logs/common/logs.contribution.ts | 75 +++++++++- .../electron-browser/logs.contribution.ts | 68 --------- .../environment/browser/environmentService.ts | 12 +- .../environment/common/environmentService.ts | 3 + .../environment/node/environmentService.ts | 4 + .../log/common/inMemoryLogProvider.ts | 77 +++++++++++ .../fileUserDataProvider.test.ts | 4 +- src/vs/workbench/workbench.desktop.main.ts | 3 - 11 files changed, 306 insertions(+), 91 deletions(-) create mode 100644 src/vs/platform/log/common/fileLogService.ts delete mode 100644 src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts create mode 100644 src/vs/workbench/services/log/common/inMemoryLogProvider.ts diff --git a/src/vs/platform/log/common/fileLogService.ts b/src/vs/platform/log/common/fileLogService.ts new file mode 100644 index 0000000000000..60b904a865504 --- /dev/null +++ b/src/vs/platform/log/common/fileLogService.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log'; +import { URI } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { Queue } from 'vs/base/common/async'; +import { VSBuffer } from 'vs/base/common/buffer'; + +export class FileLogService extends AbstractLogService implements ILogService { + + _serviceBrand: any; + + private readonly queue: Queue; + + constructor( + private readonly name: string, + private readonly resource: URI, + level: LogLevel, + @IFileService private readonly fileService: IFileService + ) { + super(); + this.setLevel(level); + this.queue = this._register(new Queue()); + } + + trace(): void { + if (this.getLevel() <= LogLevel.Trace) { + this._log(LogLevel.Trace, this.format(arguments)); + } + } + + debug(): void { + if (this.getLevel() <= LogLevel.Debug) { + this._log(LogLevel.Debug, this.format(arguments)); + } + } + + info(): void { + if (this.getLevel() <= LogLevel.Info) { + this._log(LogLevel.Info, this.format(arguments)); + } + } + + warn(): void { + if (this.getLevel() <= LogLevel.Warning) { + this._log(LogLevel.Warning, this.format(arguments)); + } + } + + error(): void { + if (this.getLevel() <= LogLevel.Error) { + const arg = arguments[0]; + + if (arg instanceof Error) { + const array = Array.prototype.slice.call(arguments) as any[]; + array[0] = arg.stack; + this._log(LogLevel.Error, this.format(array)); + } else { + this._log(LogLevel.Error, this.format(arguments)); + } + } + } + + critical(): void { + if (this.getLevel() <= LogLevel.Critical) { + this._log(LogLevel.Critical, this.format(arguments)); + } + } + + flush(): Promise { + return this.queue.queue(() => Promise.resolve()); + } + + private _log(level: LogLevel, message: string): void { + this.queue.queue(async () => { + let content = await this.loadContent(); + content += `[${this.getCurrentTimestamp()}] [${this.name}] [${this.stringifyLogLevel(level)}] ${message}\n`; + await this.fileService.writeFile(this.resource, VSBuffer.fromString(content)); + }); + } + + private getCurrentTimestamp(): string { + const toTwoDigits = (v: number) => v < 10 ? `0${v}` : v; + const toThreeDigits = (v: number) => v < 10 ? `00${v}` : v < 100 ? `0${v}` : v; + const currentTime = new Date(); + return `${currentTime.getFullYear()}-${toTwoDigits(currentTime.getMonth() + 1)}-${toTwoDigits(currentTime.getDate())} ${toTwoDigits(currentTime.getHours())}:${toTwoDigits(currentTime.getMinutes())}:${toTwoDigits(currentTime.getSeconds())}.${toThreeDigits(currentTime.getMilliseconds())}`; + } + + private async loadContent(): Promise { + try { + const content = await this.fileService.readFile(this.resource); + return content.value.toString(); + } catch (e) { + return ''; + } + } + + private stringifyLogLevel(level: LogLevel): string { + switch (level) { + case LogLevel.Critical: return 'critical'; + case LogLevel.Debug: return 'debug'; + case LogLevel.Error: return 'error'; + case LogLevel.Info: return 'info'; + case LogLevel.Trace: return 'trace'; + case LogLevel.Warning: return 'warning'; + } + return ''; + } + + private format(args: any): string { + let result = ''; + + for (let i = 0; i < args.length; i++) { + let a = args[i]; + + if (typeof a === 'object') { + try { + a = JSON.stringify(a); + } catch (e) { } + } + + result += (i > 0 ? ' ' : '') + a; + } + + return result; + } +} diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index dcb44e8c137bb..3ea46019652b5 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -8,7 +8,6 @@ import { domContentLoaded, addDisposableListener, EventType, addClass } from 'vs import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; -import { SimpleLogService } from 'vs/workbench/browser/web.simpleservices'; import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { Workbench } from 'vs/workbench/browser/workbench'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; @@ -42,6 +41,9 @@ import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/th import { InMemoryUserDataProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider'; import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; +import { BufferLogService } from 'vs/platform/log/common/bufferLog'; +import { INMEMORY_LOG_SCHEME, InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider'; +import { FileLogService } from 'vs/platform/log/common/fileLogService'; class CodeRendererMain extends Disposable { @@ -117,13 +119,14 @@ class CodeRendererMain extends Disposable { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Log - const logService = new SimpleLogService(); + const logFile = URI.file(`window.log`).with({ scheme: INMEMORY_LOG_SCHEME }); + const logService = new BufferLogService(); serviceCollection.set(ILogService, logService); const payload = await this.resolveWorkspaceInitializationPayload(); // Environment - const environmentService = new BrowserWorkbenchEnvironmentService(payload.id, this.configuration); + const environmentService = new BrowserWorkbenchEnvironmentService({ workspaceId: payload.id, logFile, ...this.configuration }); serviceCollection.set(IWorkbenchEnvironmentService, environmentService); // Product @@ -146,6 +149,10 @@ class CodeRendererMain extends Disposable { const fileService = this._register(new FileService(logService)); serviceCollection.set(IFileService, fileService); + // InMemory Log + fileService.registerProvider(INMEMORY_LOG_SCHEME, new InMemoryLogProvider()); + logService.logger = new FileLogService('window', logFile, logService.getLevel(), fileService); + // Static Extensions const staticExtensions = new StaticExtensionsService(this.configuration.staticExtensions || []); serviceCollection.set(IStaticExtensionsService, staticExtensions); diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index ce24f3df7bf3b..df37618b47194 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -11,7 +11,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IExtensionTipsService, ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IURLHandler, IURLService } from 'vs/platform/url/common/url'; -import { ConsoleLogService, ILogService } from 'vs/platform/log/common/log'; +import { ILogService } from 'vs/platform/log/common/log'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IUpdateService, State } from 'vs/platform/update/common/update'; @@ -102,12 +102,6 @@ registerSingleton(IExtensionUrlHandler, SimpleExtensionURLHandler, true); //#endregion -//#region Log - -export class SimpleLogService extends ConsoleLogService { } - -//#endregion - //#region Update export class SimpleUpdateService implements IUpdateService { diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index fa261bc537bd6..7030e38f8ee96 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -4,11 +4,82 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import { join } from 'vs/base/common/path'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { SetLogLevelAction } from 'vs/workbench/contrib/logs/common/logsActions'; +import { SetLogLevelAction, OpenLogsFolderAction } from 'vs/workbench/contrib/logs/common/logsActions'; +import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; +import { URI } from 'vs/base/common/uri'; +import { IOutputChannelRegistry, Extensions as OutputExt } from 'vs/workbench/contrib/output/common/output'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +import { dirname } from 'vs/base/common/resources'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { isWeb } from 'vs/base/common/platform'; const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); const devCategory = nls.localize('developer', "Developer"); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.LABEL), 'Developer: Set Log Level...', devCategory); \ No newline at end of file +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.LABEL), 'Developer: Set Log Level...', devCategory); + +class LogOutputChannels extends Disposable implements IWorkbenchContribution { + + constructor( + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @ILogService private readonly logService: ILogService, + @IFileService private readonly fileService: IFileService + ) { + super(); + if (isWeb) { + this.registerWebContributions(); + } else { + this.registerNativeContributions(); + } + } + + private registerWebContributions(): void { + Registry.as(OutputExt.OutputChannels).registerChannel({ id: Constants.rendererLogChannelId, label: nls.localize('rendererLog', "Window"), file: this.environmentService.logFile, log: true }); + } + + private registerNativeContributions(): void { + this.registerLogChannel(Constants.mainLogChannelId, nls.localize('mainLog', "Main"), URI.file(join(this.environmentService.logsPath, `main.log`))); + this.registerLogChannel(Constants.sharedLogChannelId, nls.localize('sharedLog', "Shared"), URI.file(join(this.environmentService.logsPath, `sharedprocess.log`))); + this.registerLogChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Window"), this.environmentService.logFile); + + const registerTelemetryChannel = (level: LogLevel) => { + if (level === LogLevel.Trace && !Registry.as(OutputExt.OutputChannels).getChannel(Constants.telemetryLogChannelId)) { + this.registerLogChannel(Constants.telemetryLogChannelId, nls.localize('telemetryLog', "Telemetry"), URI.file(join(this.environmentService.logsPath, `telemetry.log`))); + } + }; + registerTelemetryChannel(this.logService.getLevel()); + this.logService.onDidChangeLogLevel(registerTelemetryChannel); + + const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); + const devCategory = nls.localize('developer', "Developer"); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory); + } + + private async registerLogChannel(id: string, label: string, file: URI): Promise { + const outputChannelRegistry = Registry.as(OutputExt.OutputChannels); + const exists = await this.fileService.exists(file); + if (exists) { + outputChannelRegistry.registerChannel({ id, label, file, log: true }); + return; + } + + const watcher = this.fileService.watch(dirname(file)); + const disposable = this.fileService.onFileChanges(e => { + if (e.contains(file, FileChangeType.ADDED)) { + watcher.dispose(); + disposable.dispose(); + outputChannelRegistry.registerChannel({ id, label, file, log: true }); + } + }); + } + +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts b/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts deleted file mode 100644 index d8d381fa5252b..0000000000000 --- a/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts +++ /dev/null @@ -1,68 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { join } from 'vs/base/common/path'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/contrib/output/common/output'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; -import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; -import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { OpenLogsFolderAction } from 'vs/workbench/contrib/logs/common/logsActions'; -import { ILogService, LogLevel } from 'vs/platform/log/common/log'; -import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; -import { dirname } from 'vs/base/common/resources'; - -class LogOutputChannels extends Disposable implements IWorkbenchContribution { - - constructor( - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, - @ILogService logService: ILogService, - @IFileService private readonly fileService: IFileService - ) { - super(); - this.registerLogChannel(Constants.mainLogChannelId, nls.localize('mainLog', "Main"), URI.file(join(environmentService.logsPath, `main.log`))); - this.registerLogChannel(Constants.sharedLogChannelId, nls.localize('sharedLog', "Shared"), URI.file(join(environmentService.logsPath, `sharedprocess.log`))); - this.registerLogChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Window"), URI.file(join(environmentService.logsPath, `renderer${environmentService.configuration.windowId}.log`))); - - const registerTelemetryChannel = (level: LogLevel) => { - if (level === LogLevel.Trace && !Registry.as(OutputExt.OutputChannels).getChannel(Constants.telemetryLogChannelId)) { - this.registerLogChannel(Constants.telemetryLogChannelId, nls.localize('telemetryLog', "Telemetry"), URI.file(join(environmentService.logsPath, `telemetry.log`))); - } - }; - registerTelemetryChannel(logService.getLevel()); - logService.onDidChangeLogLevel(registerTelemetryChannel); - - const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); - const devCategory = nls.localize('developer', "Developer"); - workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory); - } - - private async registerLogChannel(id: string, label: string, file: URI): Promise { - const outputChannelRegistry = Registry.as(OutputExt.OutputChannels); - const exists = await this.fileService.exists(file); - if (exists) { - outputChannelRegistry.registerChannel({ id, label, file, log: true }); - return; - } - - const watcher = this.fileService.watch(dirname(file)); - const disposable = this.fileService.onFileChanges(e => { - if (e.contains(file, FileChangeType.ADDED)) { - watcher.dispose(); - disposable.dispose(); - outputChannelRegistry.registerChannel({ id, label, file, log: true }); - } - }); - } - -} - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Restored); \ No newline at end of file diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 6cbe59689dbd7..bdac4d340ef37 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -62,11 +62,9 @@ export class BrowserWindowConfiguration implements IWindowConfiguration { termProgram?: string; } -export interface IBrowserWindowConfiguration { +interface IBrowserWorkbenchEnvironemntConstructionOptions extends IWorkbenchConstructionOptions { workspaceId: string; - remoteAuthority?: string; - webviewEndpoint?: string; - connectionToken?: string; + logFile: URI; } export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironmentService { @@ -75,8 +73,9 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment readonly configuration: IWindowConfiguration = new BrowserWindowConfiguration(); - constructor(workspaceId: string, public readonly options: IWorkbenchConstructionOptions) { + constructor(readonly options: IBrowserWorkbenchEnvironemntConstructionOptions) { this.args = { _: [] }; + this.logFile = options.logFile; this.appRoot = '/web/'; this.appNameLong = 'Visual Studio Code - Web'; @@ -88,7 +87,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment this.keyboardLayoutResource = joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); this.localeResource = joinPath(this.userRoamingDataHome, 'locale.json'); this.backupHome = joinPath(this.userRoamingDataHome, BACKUPS); - this.configuration.backupWorkspaceResource = joinPath(this.backupHome, workspaceId); + this.configuration.backupWorkspaceResource = joinPath(this.backupHome, options.workspaceId); this.configuration.connectionToken = options.connectionToken || this.getConnectionTokenFromLocation(); this.logsPath = '/web/logs'; @@ -183,6 +182,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment driverVerbose: boolean; webviewEndpoint?: string; galleryMachineIdResource?: URI; + readonly logFile: URI; get webviewResourceRoot(): string { return this.webviewEndpoint ? this.webviewEndpoint + '/vscode-resource{{resource}}' : 'vscode-resource:{{resource}}'; diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index 72c98796b9ac3..967ad5e43cfd4 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -7,6 +7,7 @@ import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/co import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; +import { URI } from 'vs/base/common/uri'; export const IWorkbenchEnvironmentService = createDecorator('environmentService'); @@ -16,5 +17,7 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { readonly configuration: IWindowConfiguration; + readonly logFile: URI; + readonly options?: IWorkbenchConstructionOptions; } diff --git a/src/vs/workbench/services/environment/node/environmentService.ts b/src/vs/workbench/services/environment/node/environmentService.ts index 2ebf10365a2ec..0a46f3fa8e574 100644 --- a/src/vs/workbench/services/environment/node/environmentService.ts +++ b/src/vs/workbench/services/environment/node/environmentService.ts @@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { toBackupWorkspaceResource } from 'vs/workbench/services/backup/common/backup'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { join } from 'vs/base/common/path'; export class WorkbenchEnvironmentService extends EnvironmentService implements IWorkbenchEnvironmentService { @@ -31,4 +32,7 @@ export class WorkbenchEnvironmentService extends EnvironmentService implements I @memoize get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.userData }); } + + @memoize + get logFile(): URI { return URI.file(join(this.logsPath, `renderer${this.configuration.windowId}.log`)); } } diff --git a/src/vs/workbench/services/log/common/inMemoryLogProvider.ts b/src/vs/workbench/services/log/common/inMemoryLogProvider.ts new file mode 100644 index 0000000000000..6a01a1dbe1bc3 --- /dev/null +++ b/src/vs/workbench/services/log/common/inMemoryLogProvider.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; +import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions, FileChangeType } from 'vs/platform/files/common/files'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import { VSBuffer } from 'vs/base/common/buffer'; + +export const INMEMORY_LOG_SCHEME = 'vscode-logs-inmemory'; + +interface ILog { + content: string; + version: number; +} + +export class InMemoryLogProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { + + readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; + readonly onDidChangeCapabilities: Event = Event.None; + + private readonly _onDidChangeFile: Emitter = this._register(new Emitter()); + readonly onDidChangeFile: Event = this._onDidChangeFile.event; + + private readonly logs: Map = new Map(); + + constructor( + ) { + super(); + } + + watch(resource: URI, opts: IWatchOptions): IDisposable { + return Disposable.None; + } + + mkdir(resource: URI): Promise { + return Promise.reject(new Error('Not Supported')); + } + + rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + return Promise.reject(new Error('Not Supported')); + } + + readdir(resource: URI): Promise<[string, FileType][]> { + return Promise.reject(new Error('Not Supported')); + } + + delete(resource: URI, opts: FileDeleteOptions): Promise { + return Promise.reject(new Error('Not Supported')); + } + + async stat(resource: URI): Promise { + const log = this.logs.get(resource.toString()); + return { + ctime: 0, + mtime: log ? log.version : 0, + size: log ? log.content.length : 0, + type: FileType.File + }; + } + + async readFile(resource: URI): Promise { + const log = this.logs.get(resource.toString()); + return VSBuffer.fromString(log ? log.content : '').buffer; + } + + async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { + const log = this.logs.get(resource.toString()) || { content: '', version: 0 }; + log.content = VSBuffer.wrap(content).toString(); + log.version = log.version + 1; + this.logs.set(resource.toString(), log); + this._onDidChangeFile.fire([{ resource, type: FileChangeType.UPDATED }]); + } + +} diff --git a/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts b/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts index 854e9582f5a0a..777fcedf37b74 100644 --- a/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts +++ b/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts @@ -47,7 +47,7 @@ suite('FileUserDataProvider', () => { userDataResource = URI.file(userDataPath).with({ scheme: Schemas.userData }); await Promise.all([pfs.mkdirp(userDataPath), pfs.mkdirp(backupsPath)]); - const environmentService = new BrowserWorkbenchEnvironmentService('workspaceId', { remoteAuthority: 'remote' }); + const environmentService = new BrowserWorkbenchEnvironmentService({ remoteAuthority: 'remote', workspaceId: 'workspaceId', logFile: URI.file('logFile') }); environmentService.userRoamingDataHome = userDataResource; const userDataFileSystemProvider = new FileUserDataProvider(URI.file(userDataPath), URI.file(backupsPath), diskFileSystemProvider, environmentService); @@ -321,7 +321,7 @@ suite('FileUserDataProvider - Watching', () => { localUserDataResource = URI.file(userDataPath); userDataResource = localUserDataResource.with({ scheme: Schemas.userData }); - const environmentService = new BrowserWorkbenchEnvironmentService('workspaceId', { remoteAuthority: 'remote' }); + const environmentService = new BrowserWorkbenchEnvironmentService({ remoteAuthority: 'remote', workspaceId: 'workspaceId', logFile: URI.file('logFile') }); environmentService.userRoamingDataHome = userDataResource; const userDataFileSystemProvider = new FileUserDataProvider(localUserDataResource, localBackupsResource, new TestFileSystemProvider(fileEventEmitter.event), environmentService); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index b44e341f9c1c6..edfd8a273f3e8 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -95,9 +95,6 @@ registerSingleton(IStaticExtensionsService, class extends StaticExtensionsServic // Localizations import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; -// Logs -import 'vs/workbench/contrib/logs/electron-browser/logs.contribution'; - // Stats import 'vs/workbench/contrib/stats/electron-browser/workspaceStatsService'; import 'vs/workbench/contrib/stats/electron-browser/stats.contribution'; From 2dcfacba59646fa5683c64a59b12830c7bc20b0f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Aug 2019 13:00:15 +0200 Subject: [PATCH 444/613] implement IndexedDBLogProvider --- .../log/browser/indexedDBLogProvider.ts | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/vs/workbench/services/log/browser/indexedDBLogProvider.ts diff --git a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts new file mode 100644 index 0000000000000..31c389f4e503f --- /dev/null +++ b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; +import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions, FileChangeType, FileSystemProviderErrorCode } from 'vs/platform/files/common/files'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { FileSystemError } from 'vs/workbench/api/common/extHostTypes'; + +const LOGS_OBJECT_STORE = 'logs'; +export const INDEXEDDB_LOG_SCHEME = 'vscode-logs-indexedbd'; + +export class IndexedDBLogProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { + + readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; + readonly onDidChangeCapabilities: Event = Event.None; + + private readonly _onDidChangeFile: Emitter = this._register(new Emitter()); + readonly onDidChangeFile: Event = this._onDidChangeFile.event; + + private readonly versions: Map = new Map(); + + private readonly database: Promise; + + constructor( + ) { + super(); + this.database = this.openDatabase(2); + } + + private openDatabase(version: number): Promise { + return new Promise((c, e) => { + const request = window.indexedDB.open('LoggingDatabase', version); + request.onerror = (err) => e(request.error); + request.onsuccess = () => { + const db = request.result; + if (db.objectStoreNames.contains(LOGS_OBJECT_STORE)) { + c(db); + } + }; + request.onupgradeneeded = (e) => { + const db = request.result; + if (!db.objectStoreNames.contains(LOGS_OBJECT_STORE)) { + db.createObjectStore(LOGS_OBJECT_STORE); + } + c(db); + }; + }); + } + + watch(resource: URI, opts: IWatchOptions): IDisposable { + return Disposable.None; + } + + mkdir(resource: URI): Promise { + return Promise.reject(new Error('Not Supported')); + } + + rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + return Promise.reject(new Error('Not Supported')); + } + + readdir(resource: URI): Promise<[string, FileType][]> { + return Promise.reject(new Error('Not Supported')); + } + + delete(resource: URI, opts: FileDeleteOptions): Promise { + return Promise.reject(new Error('Not Supported')); + } + + async stat(resource: URI): Promise { + try { + const content = await this.readFile(resource); + return { + type: FileType.File, + ctime: 0, + mtime: this.versions.get(resource.toString()) || 0, + size: content.byteLength + }; + } catch (e) { + return { + type: FileType.File, + ctime: 0, + mtime: 0, + size: 0 + }; + } + } + + async readFile(resource: URI): Promise { + return new Promise(async (c, e) => { + const db = await this.database; + const transaction = db.transaction([LOGS_OBJECT_STORE]); + const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); + const request = objectStore.get(resource.path); + request.onerror = () => e(request.error); + request.onsuccess = () => { + if (request.result) { + c(VSBuffer.fromString(request.result).buffer); + } else { + e(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound)); + } + }; + }); + } + + writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { + return new Promise(async (c, e) => { + const db = await this.database; + const transaction = db.transaction([LOGS_OBJECT_STORE], 'readwrite'); + const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); + const request = objectStore.put(VSBuffer.wrap(content).toString(), resource.path); + request.onerror = () => e(request.error); + request.onsuccess = () => { + this.versions.set(resource.toString(), (this.versions.get(resource.toString()) || 0) + 1); + this._onDidChangeFile.fire([{ resource, type: FileChangeType.UPDATED }]); + c(); + }; + }); + } + +} From ada79d72be36ecbfed6f88bb16edcc5cc3f5c2a8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Aug 2019 14:58:38 +0200 Subject: [PATCH 445/613] tests - disable opener tests --- src/vs/editor/test/browser/services/openerService.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index 3e1ef9e80cbe3..5c2cffcb053d9 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -12,7 +12,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProductService } from 'vs/platform/product/common/product'; import { IStorageService } from 'vs/platform/storage/common/storage'; -suite('OpenerService', function () { +suite.skip('OpenerService', function () { const editorService = new TestCodeEditorService(); From df95a55a05d7cd0bc64770238d5971302e8a8c21 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Aug 2019 15:20:49 +0200 Subject: [PATCH 446/613] builds - prevent window.open() in tests --- .../test/browser/services/openerService.test.ts | 3 ++- test/electron/renderer.html | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index 5c2cffcb053d9..3ac05b71d2fc5 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -12,7 +12,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProductService } from 'vs/platform/product/common/product'; import { IStorageService } from 'vs/platform/storage/common/storage'; -suite.skip('OpenerService', function () { +suite('OpenerService', function () { const editorService = new TestCodeEditorService(); @@ -79,6 +79,7 @@ suite.skip('OpenerService', function () { }); test('delegate to editorService, scheme:///fff', function () { + const openerService = new OpenerService( editorService, NullCommandService, diff --git a/test/electron/renderer.html b/test/electron/renderer.html index 724543895977f..a2d4bcc5ad9fb 100644 --- a/test/electron/renderer.html +++ b/test/electron/renderer.html @@ -11,6 +11,18 @@ + - + diff --git a/src/vs/code/browser/workbench/workbench.js b/src/vs/code/browser/workbench/workbench.js index 240ca34df05ed..5050cb4e5b42f 100644 --- a/src/vs/code/browser/workbench/workbench.js +++ b/src/vs/code/browser/workbench/workbench.js @@ -8,15 +8,15 @@ (function () { require.config({ - baseUrl: `${window.location.origin}/out`, + baseUrl: `${window.location.origin}/static/out`, paths: { - 'vscode-textmate': `${window.location.origin}/node_modules/vscode-textmate/release/main`, - 'onigasm-umd': `${window.location.origin}/node_modules/onigasm-umd/release/main`, - 'xterm': `${window.location.origin}/node_modules/xterm/lib/xterm.js`, - 'xterm-addon-search': `${window.location.origin}/node_modules/xterm-addon-search/lib/xterm-addon-search.js`, - 'xterm-addon-web-links': `${window.location.origin}/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`, - 'semver-umd': `${window.location.origin}/node_modules/semver-umd/lib/semver-umd.js`, - '@microsoft/applicationinsights-web': `${window.location.origin}/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js`, + 'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`, + 'onigasm-umd': `${window.location.origin}/static/node_modules/onigasm-umd/release/main`, + 'xterm': `${window.location.origin}/static/node_modules/xterm/lib/xterm.js`, + 'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`, + 'xterm-addon-web-links': `${window.location.origin}/static/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`, + 'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`, + '@microsoft/applicationinsights-web': `${window.location.origin}/static/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js`, } }); diff --git a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts index 5db4ac5683e0b..3f8958b723579 100644 --- a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { RemoteAuthorities } from 'vs/base/common/network'; export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverService { @@ -15,13 +16,14 @@ export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverS resolveAuthority(authority: string): Promise { if (authority.indexOf(':') >= 0) { const pieces = authority.split(':'); - return Promise.resolve({ - authority: { authority, host: pieces[0], port: parseInt(pieces[1], 10) } - }); + return Promise.resolve(this._createResolvedAuthority(authority, pieces[0], parseInt(pieces[1], 10))); } - return Promise.resolve({ - authority: { authority, host: authority, port: 80 } - }); + return Promise.resolve(this._createResolvedAuthority(authority, authority, 80)); + } + + private _createResolvedAuthority(authority: string, host: string, port: number): ResolverResult { + RemoteAuthorities.set(authority, host, port); + return { authority: { authority, host, port } }; } clearResolvedAuthority(authority: string): void { diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index a40ce65b7bf26..4fcb6db76f824 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -8,7 +8,6 @@ import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHost import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { endsWith, startsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; import { joinPath } from 'vs/base/common/resources'; import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor'; @@ -128,7 +127,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { const next = joinPath(parent, '..', ensureSuffix(mod, '.js')); moduleStack.push(next); const trap = ExportsTrap.Instance.add(next.toString()); - importScripts(asDomUri(next).toString(true)); + importScripts(next.toString(true)); moduleStack.pop(); return trap.claim(); @@ -139,7 +138,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { module = module.with({ path: ensureSuffix(module.path, '.js') }); moduleStack.push(module); const trap = ExportsTrap.Instance.add(module.toString()); - importScripts(asDomUri(module).toString(true)); + importScripts(module.toString(true)); moduleStack.pop(); return Promise.resolve(trap.claim()); @@ -153,16 +152,6 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { } } -// todo@joh this is a copy of `dom.ts#asDomUri` -function asDomUri(uri: URI): URI { - if (Schemas.vscodeRemote === uri.scheme) { - // rewrite vscode-remote-uris to uris of the window location - // so that they can be intercepted by the service worker - return URI.parse(window.location.href).with({ path: '/vscode-remote', query: JSON.stringify(uri) }); - } - return uri; -} - function ensureSuffix(path: string, suffix: string): string { return endsWith(path, suffix) ? path : path + suffix; } diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index df37618b47194..78d9281c0101f 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -31,7 +31,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { toStoreData, restoreRecentlyOpened } from 'vs/platform/history/common/historyStorage'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProductService } from 'vs/platform/product/common/product'; import Severity from 'vs/base/common/severity'; @@ -176,7 +175,6 @@ export class SimpleWindowService extends Disposable implements IWindowService { @IStorageService private readonly storageService: IStorageService, @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, @ILogService private readonly logService: ILogService, - @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService ) { super(); @@ -372,7 +370,7 @@ export class SimpleWindowService extends Disposable implements IWindowService { for (let i = 0; i < _uris.length; i++) { const uri = _uris[i]; if ('folderUri' in uri) { - const newAddress = `${document.location.origin}/?folder=${uri.folderUri.path}${this.workbenchEnvironmentService.configuration.connectionToken ? `&tkn=${this.workbenchEnvironmentService.configuration.connectionToken}` : ''}`; + const newAddress = `${document.location.origin}/?folder=${uri.folderUri.path}`; if (openFolderInNewWindow) { window.open(newAddress); } else { @@ -459,7 +457,6 @@ export class SimpleWindowsService implements IWindowsService { readonly onRecentlyOpenedChange: Event = Event.None; constructor( - @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, @IDialogService private readonly dialogService: IDialogService, @IProductService private readonly productService: IProductService, @IClipboardService private readonly clipboardService: IClipboardService @@ -651,11 +648,6 @@ export class SimpleWindowsService implements IWindowsService { addQueryParameter('ibe', ibe); } - // add connection token - if (this.workbenchEnvironmentService.configuration.connectionToken) { - addQueryParameter('tkn', this.workbenchEnvironmentService.configuration.connectionToken); - } - window.open(newAddress); return Promise.resolve(); diff --git a/src/vs/workbench/contrib/resources/browser/resourceServiceWorker.ts b/src/vs/workbench/contrib/resources/browser/resourceServiceWorker.ts index 48b238d10f338..3534ef147dfd5 100644 --- a/src/vs/workbench/contrib/resources/browser/resourceServiceWorker.ts +++ b/src/vs/workbench/contrib/resources/browser/resourceServiceWorker.ts @@ -34,7 +34,7 @@ self.addEventListener('activate', event => { //#region --- fetching/caching const _cacheName = 'vscode-extension-resources'; -const _resourcePrefix = '/vscode-remote'; +const _resourcePrefix = '/vscode-remote-resource'; const _pendingFetch = new Map(); self.addEventListener('message', event => { diff --git a/src/vs/workbench/contrib/resources/browser/resourceServiceWorkerMain.ts b/src/vs/workbench/contrib/resources/browser/resourceServiceWorkerMain.ts index 0dc883addcccf..89e7d32eae6d6 100644 --- a/src/vs/workbench/contrib/resources/browser/resourceServiceWorkerMain.ts +++ b/src/vs/workbench/contrib/resources/browser/resourceServiceWorkerMain.ts @@ -10,7 +10,7 @@ // statement. // trigger service worker updates -const _tag = '52278406-3ca9-48af-a8fb-8495add5bb4e'; +const _tag = '23549971-9b8d-41bb-92ae-d7f6a68c9702'; // loader world const baseUrl = '../../../../../'; diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index bdac4d340ef37..ec7417e7e7b9b 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -88,7 +88,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment this.localeResource = joinPath(this.userRoamingDataHome, 'locale.json'); this.backupHome = joinPath(this.userRoamingDataHome, BACKUPS); this.configuration.backupWorkspaceResource = joinPath(this.backupHome, options.workspaceId); - this.configuration.connectionToken = options.connectionToken || this.getConnectionTokenFromLocation(); + this.configuration.connectionToken = options.connectionToken || getCookieValue('vscode-tkn'); this.logsPath = '/web/logs'; @@ -191,21 +191,12 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment get webviewCspSource(): string { return this.webviewEndpoint ? this.webviewEndpoint : 'vscode-resource:'; } +} - private getConnectionTokenFromLocation(): string | undefined { - // TODO: Check with @alexd where the token will be: search or hash? - let connectionToken: string | undefined = undefined; - if (document.location.search) { - connectionToken = this.getConnectionToken(document.location.search); - } - if (!connectionToken && document.location.hash) { - connectionToken = this.getConnectionToken(document.location.hash); - } - return connectionToken; - } - - private getConnectionToken(str: string): string | undefined { - const m = str.match(/[#&?]tkn=([^&]+)/); - return m ? m[1] : undefined; - } +/** + * See https://stackoverflow.com/a/25490531 + */ +function getCookieValue(name: string): string | undefined { + const m = document.cookie.match('(^|[^;]+)\\s*' + name + '\\s*=\\s*([^;]+)'); + return m ? m.pop() : undefined; } From 1a4a1c707947c77c05a40b9015411a2afa1d58da Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 21 Aug 2019 16:12:42 +0200 Subject: [PATCH 449/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 559c11a1763d9..bc1cdb1d27ae8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "2f7403275ca8931a7265b58ac35e33096ce0d074", + "distro": "95e5ec943d6b4bf2ed6160c8fbcca30d8fb880dd", "author": { "name": "Microsoft Corporation" }, From 74b1f4de92a4931119b73fc3462e48e732d69716 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 21 Aug 2019 16:16:21 +0200 Subject: [PATCH 450/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bc1cdb1d27ae8..5342efb46a01e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "95e5ec943d6b4bf2ed6160c8fbcca30d8fb880dd", + "distro": "03312cde017811bad062ca8566a7f327f8fdcc34", "author": { "name": "Microsoft Corporation" }, From d54f3ae2bd092c663dda33ec61755eb32ff8685d Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 21 Aug 2019 07:33:26 -0700 Subject: [PATCH 451/613] Revert "Just simple object, no need for Map" This reverts commit 9ecd1be8c14091832fae31fc1f7e7a9d4ee5fd1f. --- src/vs/editor/browser/services/openerService.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 8668c51a26c99..1a57b3731f5f9 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -21,7 +21,7 @@ export class OpenerService extends Disposable implements IOpenerService { _serviceBrand!: ServiceIdentifier; private readonly _opener = new LinkedList(); - private readonly _validatorMap: { [k: string]: LinkedList } = {}; + private readonly _validatorMap = new Map>(); constructor( @ICodeEditorService private readonly _editorService: ICodeEditorService, @@ -36,10 +36,10 @@ export class OpenerService extends Disposable implements IOpenerService { } registerValidator(uriScheme: string, validator: IValidator): IDisposable { - if (!this._validatorMap[uriScheme]) { - this._validatorMap[uriScheme] = new LinkedList(); + if (!this._validatorMap.has(uriScheme)) { + this._validatorMap.set(uriScheme, new LinkedList()); } - const remove = this._validatorMap[uriScheme].push(validator); + const remove = this._validatorMap.get(uriScheme)!.push(validator); return { dispose: remove }; } @@ -50,8 +50,8 @@ export class OpenerService extends Disposable implements IOpenerService { } // check with contributed validators - if (this._validatorMap[resource.scheme]) { - const validators = this._validatorMap[resource.scheme].toArray(); + if (this._validatorMap.has(resource.scheme)) { + const validators = this._validatorMap.get(resource.scheme)!.toArray(); for (const validator of validators) { if (!(await validator.shouldOpen(resource))) { return false; @@ -132,8 +132,6 @@ export class OpenerService extends Disposable implements IOpenerService { } dispose() { - for (let key in this._validatorMap) { - delete this._validatorMap[key]; - } + this._validatorMap.clear(); } } From eb5c8a0208301cba37bf9498d5b2f4df4c8a94e9 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 21 Aug 2019 16:37:57 +0200 Subject: [PATCH 452/613] fixes #78510 --- .../browser/views/explorerDecorationsProvider.ts | 4 ++++ .../contrib/files/browser/views/explorerViewer.ts | 5 ++++- .../workbench/contrib/files/common/explorerModel.ts | 11 ++++++++--- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts b/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts index f1708ff4d4969..eb1f7705faf9f 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerDecorationsProvider.ts @@ -11,6 +11,7 @@ import { IDecorationsProvider, IDecorationData } from 'vs/workbench/services/dec import { listInvalidItemForeground } from 'vs/platform/theme/common/colorRegistry'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; +import { explorerRootErrorEmitter } from 'vs/workbench/contrib/files/browser/views/explorerViewer'; export class ExplorerDecorationsProvider implements IDecorationsProvider { readonly label: string = localize('label', "Explorer"); @@ -30,6 +31,9 @@ export class ExplorerDecorationsProvider implements IDecorationsProvider { this._onDidChange.fire([change.item.resource]); } })); + this.toDispose.add(explorerRootErrorEmitter.event((resource => { + this._onDidChange.fire([resource]); + }))); } get onDidChange(): Event { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index d8d9f52f32014..016249db38c95 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -46,6 +46,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { findValidPasteFileTarget } from 'vs/workbench/contrib/files/browser/fileActions'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; +import { Emitter } from 'vs/base/common/event'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -60,6 +61,7 @@ export class ExplorerDelegate implements IListVirtualDelegate { } } +export const explorerRootErrorEmitter = new Emitter(); export class ExplorerDataSource implements IAsyncDataSource { constructor( @@ -87,8 +89,9 @@ export class ExplorerDataSource implements IAsyncDataSource Date: Wed, 21 Aug 2019 16:40:50 +0200 Subject: [PATCH 453/613] clean up --- .../workbench/browser/web.simpleservices.ts | 40 ------------------- .../browser/extensions.web.contribution.ts | 11 ----- 2 files changed, 51 deletions(-) delete mode 100644 src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 78d9281c0101f..73f112884c860 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -8,7 +8,6 @@ import * as browser from 'vs/base/browser/browser'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionTipsService, ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IURLHandler, IURLService } from 'vs/platform/url/common/url'; import { ILogService } from 'vs/platform/log/common/log'; @@ -39,45 +38,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService // tslint:disable-next-line: import-patterns import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats'; -//#region Extension Tips - -export class SimpleExtensionTipsService implements IExtensionTipsService { - _serviceBrand: any; - - onRecommendationChange = Event.None; - - getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason; reasonText: string; }; } { - return Object.create(null); - } - - getFileBasedRecommendations(): IExtensionRecommendation[] { - return []; - } - - getOtherRecommendations(): Promise { - return Promise.resolve([]); - } - - getWorkspaceRecommendations(): Promise { - return Promise.resolve([]); - } - - getKeymapRecommendations(): IExtensionRecommendation[] { - return []; - } - - toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void { - } - - getAllIgnoredRecommendations(): { global: string[]; workspace: string[]; } { - return { global: [], workspace: [] }; - } -} - -registerSingleton(IExtensionTipsService, SimpleExtensionTipsService, true); - -//#endregion - //#region Extension URL Handler export const IExtensionUrlHandler = createDecorator('inactiveExtensionUrlHandler'); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts deleted file mode 100644 index 439f218cfb3d9..0000000000000 --- a/src/vs/workbench/contrib/extensions/browser/extensions.web.contribution.ts +++ /dev/null @@ -1,11 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; - -// Singletons -registerSingleton(IExtensionTipsService, ExtensionTipsService); From 3a3671964ac844b176634c36bdcaaca1eb9e6494 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 21 Aug 2019 07:47:56 -0700 Subject: [PATCH 454/613] Do not scope validators to schemes --- .../editor/browser/services/openerService.ts | 26 ++-- .../browser/services/openerService.test.ts | 124 ++++++++++++------ src/vs/platform/opener/common/opener.ts | 2 +- .../contrib/url/common/url.contribution.ts | 5 +- 4 files changed, 99 insertions(+), 58 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 1a57b3731f5f9..07056377d38c9 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -20,8 +20,8 @@ export class OpenerService extends Disposable implements IOpenerService { _serviceBrand!: ServiceIdentifier; - private readonly _opener = new LinkedList(); - private readonly _validatorMap = new Map>(); + private readonly _openers = new LinkedList(); + private readonly _validators = new LinkedList(); constructor( @ICodeEditorService private readonly _editorService: ICodeEditorService, @@ -31,15 +31,12 @@ export class OpenerService extends Disposable implements IOpenerService { } registerOpener(opener: IOpener): IDisposable { - const remove = this._opener.push(opener); + const remove = this._openers.push(opener); return { dispose: remove }; } - registerValidator(uriScheme: string, validator: IValidator): IDisposable { - if (!this._validatorMap.has(uriScheme)) { - this._validatorMap.set(uriScheme, new LinkedList()); - } - const remove = this._validatorMap.get(uriScheme)!.push(validator); + registerValidator(validator: IValidator): IDisposable { + const remove = this._validators.push(validator); return { dispose: remove }; } @@ -50,17 +47,14 @@ export class OpenerService extends Disposable implements IOpenerService { } // check with contributed validators - if (this._validatorMap.has(resource.scheme)) { - const validators = this._validatorMap.get(resource.scheme)!.toArray(); - for (const validator of validators) { - if (!(await validator.shouldOpen(resource))) { - return false; - } + for (const validator of this._validators.toArray()) { + if (!(await validator.shouldOpen(resource))) { + return false; } } // check with contributed openers - for (const opener of this._opener.toArray()) { + for (const opener of this._openers.toArray()) { const handled = await opener.open(resource, options); if (handled) { return true; @@ -132,6 +126,6 @@ export class OpenerService extends Disposable implements IOpenerService { } dispose() { - this._validatorMap.clear(); + this._validators.clear(); } } diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index c771eb2f3e6d0..6824acf4d84ed 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -9,12 +9,11 @@ import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands'; suite('OpenerService', function () { - const editorService = new TestCodeEditorService(); - let lastCommand: { id: string, args: any[] } | undefined; + let lastCommand: { id: string; args: any[] } | undefined; - const commandService = new class implements ICommandService { + const commandService = new (class implements ICommandService { _serviceBrand: any; onWillExecuteCommand = () => ({ dispose: () => { } }); onDidExecuteCommand = () => ({ dispose: () => { } }); @@ -22,29 +21,20 @@ suite('OpenerService', function () { lastCommand = { id, args }; return Promise.resolve(undefined); } - }; - + })(); setup(function () { lastCommand = undefined; }); test('delegate to editorService, scheme:///fff', function () { - - const openerService = new OpenerService( - editorService, - NullCommandService - ); + const openerService = new OpenerService(editorService, NullCommandService); openerService.open(URI.parse('another:///somepath')); assert.equal(editorService.lastInput!.options!.selection, undefined); }); test('delegate to editorService, scheme:///fff#L123', function () { - - const openerService = new OpenerService( - editorService, - NullCommandService - ); + const openerService = new OpenerService(editorService, NullCommandService); openerService.open(URI.parse('file:///somepath#L23')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); @@ -66,11 +56,7 @@ suite('OpenerService', function () { }); test('delegate to editorService, scheme:///fff#123,123', function () { - - const openerService = new OpenerService( - editorService, - NullCommandService - ); + const openerService = new OpenerService(editorService, NullCommandService); openerService.open(URI.parse('file:///somepath#23')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); @@ -88,11 +74,7 @@ suite('OpenerService', function () { }); test('delegate to commandsService, command:someid', function () { - - const openerService = new OpenerService( - editorService, - commandService - ); + const openerService = new OpenerService(editorService, commandService); const id = `aCommand${Math.random()}`; CommandsRegistry.registerCommand(id, function () { }); @@ -114,28 +96,20 @@ suite('OpenerService', function () { }); test('links are protected by validators', async function () { - const openerService = new OpenerService( - editorService, - commandService - ); + const openerService = new OpenerService(editorService, commandService); - openerService.registerValidator('http', { shouldOpen: () => Promise.resolve(false) }); - openerService.registerValidator('https', { shouldOpen: () => Promise.resolve(false) }); + openerService.registerValidator({ shouldOpen: () => Promise.resolve(false) }); const httpResult = await openerService.open(URI.parse('https://www.microsoft.com')); - assert.equal(httpResult, false); const httpsResult = await openerService.open(URI.parse('https://www.microsoft.com')); + assert.equal(httpResult, false); assert.equal(httpsResult, false); }); test('links validated by validators go to openers', async function () { - const openerService = new OpenerService( - editorService, - commandService - ); + const openerService = new OpenerService(editorService, commandService); - openerService.registerValidator('http', { shouldOpen: () => Promise.resolve(true) }); - openerService.registerValidator('https', { shouldOpen: () => Promise.resolve(true) }); + openerService.registerValidator({ shouldOpen: () => Promise.resolve(true) }); let openCount = 0; openerService.registerOpener({ @@ -150,4 +124,78 @@ suite('OpenerService', function () { await openerService.open(URI.parse('https://microsoft.com')); assert.equal(openCount, 2); }); + + test('links validated by multiple validators', async function () { + const openerService = new OpenerService(editorService, commandService); + + let v1 = 0; + openerService.registerValidator({ + shouldOpen: () => { + v1++; + return Promise.resolve(true); + } + }); + + let v2 = 0; + openerService.registerValidator({ + shouldOpen: () => { + v2++; + return Promise.resolve(true); + } + }); + + let openCount = 0; + openerService.registerOpener({ + open: (resource: URI) => { + openCount++; + return Promise.resolve(true); + } + }); + + await openerService.open(URI.parse('http://microsoft.com')); + assert.equal(openCount, 1); + assert.equal(v1, 1); + assert.equal(v2, 1); + await openerService.open(URI.parse('https://microsoft.com')); + assert.equal(openCount, 2); + assert.equal(v1, 2); + assert.equal(v2, 2); + }); + + test('links invalidated by first validator do not continue validating', async function () { + const openerService = new OpenerService(editorService, commandService); + + let v1 = 0; + openerService.registerValidator({ + shouldOpen: () => { + v1++; + return Promise.resolve(false); + } + }); + + let v2 = 0; + openerService.registerValidator({ + shouldOpen: () => { + v2++; + return Promise.resolve(true); + } + }); + + let openCount = 0; + openerService.registerOpener({ + open: (resource: URI) => { + openCount++; + return Promise.resolve(true); + } + }); + + await openerService.open(URI.parse('http://microsoft.com')); + assert.equal(openCount, 0); + assert.equal(v1, 1); + assert.equal(v2, 0); + await openerService.open(URI.parse('https://microsoft.com')); + assert.equal(openCount, 0); + assert.equal(v1, 2); + assert.equal(v2, 0); + }); }); diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index d77caa7852d9d..8c92cb2d1bdd2 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -31,7 +31,7 @@ export interface IOpenerService { * Register a participant that can validate if the URI resource be opened. * validators are run before openers. */ - registerValidator(uriScheme: string, validator: IValidator): IDisposable; + registerValidator(validator: IValidator): IDisposable; /** * Opens a resource, like a webaddress, a document uri, or executes command. diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 02fae3d3fc9ff..bead1482b7594 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -150,15 +150,14 @@ class OpenerValidatorContributions implements IWorkbenchContribution { @IProductService private readonly _productService: IProductService, @IQuickInputService private readonly _quickInputService: IQuickInputService ) { - this._openerService.registerValidator('http', { shouldOpen: r => this.validateLink(r) }); - this._openerService.registerValidator('https', { shouldOpen: r => this.validateLink(r) }); + this._openerService.registerValidator({ shouldOpen: r => this.validateLink(r) }); } validateLink(resource: URI): Promise { const { scheme, authority } = resource; if (!equalsIgnoreCase(scheme, Schemas.http) && !equalsIgnoreCase(scheme, Schemas.https)) { - return Promise.resolve(false); + return Promise.resolve(true); } let trustedDomains: string[] = [VSCODE_DOMAIN]; From 4a6f60ba0e7f0903bd2daa389bbc0a0fd2580d3f Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 21 Aug 2019 16:52:39 +0200 Subject: [PATCH 455/613] dynamic schemas not updated. Fixes #79363 --- .../client/src/jsonMain.ts | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index 6f1ca02d58041..52441738fef19 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -145,11 +145,14 @@ export function activate(context: ExtensionContext) { } }); + const schemaDocuments: { [uri: string]: boolean } = {}; + // handle content request client.onRequest(VSCodeContentRequest.type, (uriPath: string) => { let uri = Uri.parse(uriPath); if (uri.scheme !== 'http' && uri.scheme !== 'https') { return workspace.openTextDocument(uri).then(doc => { + schemaDocuments[uri.toString()] = true; return doc.getText(); }, error => { return Promise.reject(error); @@ -164,10 +167,12 @@ export function activate(context: ExtensionContext) { } }); - let handleContentChange = (uri: Uri) => { - if (uri.scheme === 'vscode' && uri.authority === 'schemas') { - client.sendNotification(SchemaContentChangeNotification.type, uri.toString()); + let handleContentChange = (uriString: string) => { + if (schemaDocuments[uriString]) { + client.sendNotification(SchemaContentChangeNotification.type, uriString); + return true; } + return false; }; let handleActiveEditorChange = (activeEditor?: TextEditor) => { @@ -184,10 +189,13 @@ export function activate(context: ExtensionContext) { } }; - toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri))); + toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString()))); toDispose.push(workspace.onDidCloseTextDocument(d => { - handleContentChange(d.uri); - fileSchemaErrors.delete(d.uri.toString()); + const uriString = d.uri.toString(); + if (handleContentChange(uriString)) { + delete schemaDocuments[uriString]; + } + fileSchemaErrors.delete(uriString); })); toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange)); From be8f289fa9e38b1c79f300a5347d813ef5dccef4 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 21 Aug 2019 16:55:59 +0200 Subject: [PATCH 456/613] update jsonc-parser --- extensions/configuration-editing/package.json | 2 +- extensions/extension-editing/package.json | 2 +- extensions/json-language-features/server/package.json | 2 +- extensions/npm/package.json | 2 +- extensions/typescript-language-features/package.json | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index e555d61457e95..3d1049af6b4d6 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -18,7 +18,7 @@ "watch": "gulp watch-extension:configuration-editing" }, "dependencies": { - "jsonc-parser": "2.0.2", + "jsonc-parser": "^2.1.1", "vscode-nls": "^4.0.0" }, "contributes": { diff --git a/extensions/extension-editing/package.json b/extensions/extension-editing/package.json index 9c07a2568aa94..50c97fb26f41b 100644 --- a/extensions/extension-editing/package.json +++ b/extensions/extension-editing/package.json @@ -19,7 +19,7 @@ "watch": "gulp watch-extension:extension-editing" }, "dependencies": { - "jsonc-parser": "^2.0.2", + "jsonc-parser": "^2.1.1", "markdown-it": "^8.3.1", "parse5": "^3.0.2", "vscode-nls": "^4.0.0" diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index bf5e3c8b09a79..bf76da32dba26 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -12,7 +12,7 @@ }, "main": "./out/jsonServerMain", "dependencies": { - "jsonc-parser": "^2.1.0", + "jsonc-parser": "^2.1.1", "request-light": "^0.2.4", "vscode-json-languageservice": "^3.3.1", "vscode-languageserver": "^5.3.0-next.8", diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 843255c2141ad..a987d6082b46b 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -18,7 +18,7 @@ "watch": "gulp watch-extension:npm" }, "dependencies": { - "jsonc-parser": "^2.0.2", + "jsonc-parser": "^2.1.1", "minimatch": "^3.0.4", "request-light": "^0.2.4", "vscode-nls": "^4.0.0" diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index fc003585c9bb0..3b9917c412cf1 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -16,7 +16,7 @@ "Programming Languages" ], "dependencies": { - "jsonc-parser": "^2.0.1", + "jsonc-parser": "^2.1.1", "rimraf": "^2.6.3", "semver": "5.5.1", "vscode-extension-telemetry": "0.1.1", @@ -758,4 +758,4 @@ } ] } -} \ No newline at end of file +} From 2694b373272504fec70669bcc104117138392593 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Aug 2019 17:02:32 +0200 Subject: [PATCH 457/613] Fix microsoft/vscode-remote-release/issues/1213 --- .../contrib/extensions/browser/extensionsActions.ts | 12 ++---------- .../contrib/extensions/browser/extensionsViews.ts | 2 +- .../extensions/browser/remoteExtensionsInstaller.ts | 2 +- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index ec19e774ab1f6..53632cb694afa 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -3025,7 +3025,6 @@ export class InstallLocalExtensionsInRemoteAction extends Action { private extensions: IExtension[] | undefined = undefined; constructor( - private readonly selectAndInstall: boolean, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @@ -3047,9 +3046,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action { get label(): string { if (this.extensionManagementServerService.remoteExtensionManagementServer) { - return this.selectAndInstall ? - localize('select and install local extensions', "Install Local Extensions in {0}...", this.extensionManagementServerService.remoteExtensionManagementServer.label) - : localize('install local extensions', "Install Local Extensions in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label); + return localize('select and install local extensions', "Install Local Extensions in {0}...", this.extensionManagementServerService.remoteExtensionManagementServer.label); } return ''; } @@ -3065,12 +3062,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action { } async run(): Promise { - if (this.selectAndInstall) { - return this.selectAndInstallLocalExtensions(); - } else { - const extensionsToInstall = await this.queryExtensionsToInstall(); - return this.installLocalExtensions(extensionsToInstall); - } + return this.selectAndInstallLocalExtensions(); } private async queryExtensionsToInstall(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 733aa5fa5b1a8..9eabbf3220f07 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -876,7 +876,7 @@ export class ServerExtensionsView extends ExtensionsListView { getActions(): IAction[] { if (this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === this.server) { - const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, false)); + const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction)); installLocalExtensionsInRemoteAction.class = 'octicon octicon-cloud-download'; return [installLocalExtensionsInRemoteAction]; } diff --git a/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts b/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts index b140853a8207e..84c47be30646f 100644 --- a/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts +++ b/src/vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller.ts @@ -22,7 +22,7 @@ export class RemoteExtensionsInstaller extends Disposable implements IWorkbenchC ) { super(); if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { - const installLocalExtensionsInRemoteAction = instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, true); + const installLocalExtensionsInRemoteAction = instantiationService.createInstance(InstallLocalExtensionsInRemoteAction); CommandsRegistry.registerCommand('workbench.extensions.installLocalExtensions', () => installLocalExtensionsInRemoteAction.run()); let disposable = Disposable.None; const appendMenuItem = () => { From fab2803943dd7999a676857f57345ca188caa20a Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 21 Aug 2019 17:13:33 +0200 Subject: [PATCH 458/613] list expose scrollLeft --- src/vs/base/browser/ui/list/listView.ts | 16 ++++++++++++++++ src/vs/base/browser/ui/list/listWidget.ts | 8 ++++++++ src/vs/base/browser/ui/tree/abstractTree.ts | 8 ++++++++ src/vs/base/browser/ui/tree/asyncDataTree.ts | 8 ++++++++ 4 files changed, 40 insertions(+) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 4f5394f773d14..8ae8867bf645b 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -683,6 +683,22 @@ export class ListView implements ISpliceable, IDisposable { this.scrollableElement.setScrollPosition({ scrollTop }); } + getScrollLeft(): number { + const scrollPosition = this.scrollableElement.getScrollPosition(); + return scrollPosition.scrollLeft; + } + + setScrollLeftt(scrollLeft: number): void { + if (this.scrollableElementUpdateDisposable) { + this.scrollableElementUpdateDisposable.dispose(); + this.scrollableElementUpdateDisposable = null; + this.scrollableElement.setScrollDimensions({ scrollWidth: this.scrollWidth }); + } + + this.scrollableElement.setScrollPosition({ scrollLeft }); + } + + get scrollTop(): number { return this.getScrollTop(); } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 6e82c6de867f1..15b21e3b9b0ff 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1307,6 +1307,14 @@ export class List implements ISpliceable, IDisposable { this.view.setScrollTop(scrollTop); } + get scrollLeft(): number { + return this.view.getScrollLeft(); + } + + set scrollLeft(scrollLeft: number) { + this.view.setScrollLeftt(scrollLeft); + } + get scrollHeight(): number { return this.view.scrollHeight; } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 7402d493a98c0..1aa9c5363e29f 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1319,6 +1319,14 @@ export abstract class AbstractTree implements IDisposable this.view.scrollTop = scrollTop; } + get scrollLeft(): number { + return this.view.scrollTop; + } + + set scrollLeft(scrollLeft: number) { + this.view.scrollLeft = scrollLeft; + } + get scrollHeight(): number { return this.view.scrollHeight; } diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index b26b80d2c4995..9c6bea61a4156 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -400,6 +400,14 @@ export class AsyncDataTree implements IDisposable this.tree.scrollTop = scrollTop; } + get scrollLeft(): number { + return this.tree.scrollLeft; + } + + set scrollLeft(scrollLeft: number) { + this.tree.scrollLeft = scrollLeft; + } + get scrollHeight(): number { return this.tree.scrollHeight; } From 793c790a6ab24cb413aa45b2173af56d8a9b604f Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 21 Aug 2019 17:13:52 +0200 Subject: [PATCH 459/613] debugHover: restart scroll position when shown again --- src/vs/workbench/contrib/debug/browser/debugHover.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index f06919be1d914..b7f66a4a69bf4 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -245,6 +245,9 @@ export class DebugHoverWidget implements IContentWidget { this.layoutTreeAndContainer(); this.editor.layoutContentWidget(this); this.scrollbar.scanDomNode(); + this.tree.scrollTop = 0; + this.tree.scrollLeft = 0; + if (focus) { this.editor.render(); this.tree.domFocus(); From da25e0802a1057404b98dfcb564a460a160144d2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Aug 2019 17:19:28 +0200 Subject: [PATCH 460/613] tslint - polish rule (support multiple declarations) --- build/lib/tslint/abstractGlobalsRule.js | 23 ++++++++++++++--------- build/lib/tslint/abstractGlobalsRule.ts | 24 +++++++++++++++--------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/build/lib/tslint/abstractGlobalsRule.js b/build/lib/tslint/abstractGlobalsRule.js index 2aadf2c3bd965..0691ce05608f5 100644 --- a/build/lib/tslint/abstractGlobalsRule.js +++ b/build/lib/tslint/abstractGlobalsRule.js @@ -19,18 +19,23 @@ class AbstractGlobalsRuleWalker extends Lint.RuleWalker { const checker = this.program.getTypeChecker(); const symbol = checker.getSymbolAtLocation(node); if (symbol) { - const valueDeclaration = symbol.valueDeclaration; - if (valueDeclaration) { - const parent = valueDeclaration.parent; - if (parent) { - const sourceFile = parent.getSourceFile(); - if (sourceFile) { - const fileName = sourceFile.fileName; - if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) { - this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`); + const declarations = symbol.declarations; + if (Array.isArray(declarations) && symbol.declarations.some(declaration => { + if (declaration) { + const parent = declaration.parent; + if (parent) { + const sourceFile = parent.getSourceFile(); + if (sourceFile) { + const fileName = sourceFile.fileName; + if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) { + return true; + } } } } + return false; + })) { + this.addFailureAtNode(node, `!!Cannot use global '${node.text}' in '${this._config.target}'`); } } } diff --git a/build/lib/tslint/abstractGlobalsRule.ts b/build/lib/tslint/abstractGlobalsRule.ts index 8c80b3e0cfd76..543720455c3b7 100644 --- a/build/lib/tslint/abstractGlobalsRule.ts +++ b/build/lib/tslint/abstractGlobalsRule.ts @@ -30,18 +30,24 @@ export abstract class AbstractGlobalsRuleWalker extends Lint.RuleWalker { const checker = this.program.getTypeChecker(); const symbol = checker.getSymbolAtLocation(node); if (symbol) { - const valueDeclaration = symbol.valueDeclaration; - if (valueDeclaration) { - const parent = valueDeclaration.parent; - if (parent) { - const sourceFile = parent.getSourceFile(); - if (sourceFile) { - const fileName = sourceFile.fileName; - if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) { - this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`); + const declarations = symbol.declarations; + if (Array.isArray(declarations) && symbol.declarations.some(declaration => { + if (declaration) { + const parent = declaration.parent; + if (parent) { + const sourceFile = parent.getSourceFile(); + if (sourceFile) { + const fileName = sourceFile.fileName; + if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) { + return true; + } } } } + + return false; + })) { + this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`); } } } From ad827cd3e83449359a62794ecadcb6c6f20cab8d Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 21 Aug 2019 17:22:07 +0200 Subject: [PATCH 461/613] editorService: be a bit smarter with revealIfOpened Give priority to a group if the Editor is already active --- src/vs/workbench/services/editor/browser/editorService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index bf36c64c500b6..6c49e86ed1934 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -282,10 +282,13 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Respect option to reveal an editor if it is open (not necessarily visible) if ((options && options.revealIfOpened) || this.configurationService.getValue('workbench.editor.revealIfOpen')) { for (const group of groupsByLastActive) { - if (group.isOpened(input)) { + if (group.isOpened(input) && group.isActive(input)) { targetGroup = group; break; } + if (group.isOpened(input)) { + targetGroup = group; + } } } } From 010a45ccc9a8f9cbf39f955ac98a3feab6c417e3 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 21 Aug 2019 17:24:53 +0200 Subject: [PATCH 462/613] editorService: do not take priority to later groups --- src/vs/workbench/services/editor/browser/editorService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 6c49e86ed1934..02bd141ac06be 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -286,7 +286,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { targetGroup = group; break; } - if (group.isOpened(input)) { + if (group.isOpened(input) && !targetGroup) { targetGroup = group; } } From 43aa378a3c6b5d1efe1334b20ec29d01956ddda0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 21 Aug 2019 17:42:21 +0200 Subject: [PATCH 463/613] better extension host logging --- src/vs/platform/log/common/fileLogService.ts | 4 ++ .../api/browser/mainThreadLogService.ts | 46 ++++++++++++++---- .../workbench/api/common/extHost.protocol.ts | 5 ++ .../workbench/api/worker/extHostLogService.ts | 47 ++++++++----------- .../extensions/browser/extensionService.ts | 3 +- 5 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/vs/platform/log/common/fileLogService.ts b/src/vs/platform/log/common/fileLogService.ts index 60b904a865504..01859d4f7b24b 100644 --- a/src/vs/platform/log/common/fileLogService.ts +++ b/src/vs/platform/log/common/fileLogService.ts @@ -74,6 +74,10 @@ export class FileLogService extends AbstractLogService implements ILogService { return this.queue.queue(() => Promise.resolve()); } + log(level: LogLevel, args: any[]): void { + this._log(level, this.format(args)); + } + private _log(level: LogLevel, message: string): void { this.queue.queue(async () => { let content = await this.loadContent(); diff --git a/src/vs/workbench/api/browser/mainThreadLogService.ts b/src/vs/workbench/api/browser/mainThreadLogService.ts index b19ef2e78b12a..f8eef4dd91cc0 100644 --- a/src/vs/workbench/api/browser/mainThreadLogService.ts +++ b/src/vs/workbench/api/browser/mainThreadLogService.ts @@ -3,20 +3,46 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { ILogService } from 'vs/platform/log/common/log'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IExtHostContext, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; +import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IExtHostContext, ExtHostContext, MainThreadLogShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; +import { UriComponents, URI } from 'vs/base/common/uri'; +import { FileLogService } from 'vs/platform/log/common/fileLogService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { basename } from 'vs/base/common/path'; -@extHostCustomer -export class MainThreadLogService extends Disposable { +@extHostNamedCustomer(MainContext.MainThreadLog) +export class MainThreadLogService implements MainThreadLogShape { + + private readonly _loggers = new Map(); + private readonly _logListener: IDisposable; constructor( extHostContext: IExtHostContext, - @ILogService logService: ILogService, + @ILogService private readonly _logService: ILogService, + @IInstantiationService private readonly _instaService: IInstantiationService, ) { - super(); - this._register(logService.onDidChangeLogLevel(level => extHostContext.getProxy(ExtHostContext.ExtHostLogService).$setLevel(level))); + const proxy = extHostContext.getProxy(ExtHostContext.ExtHostLogService); + this._logListener = _logService.onDidChangeLogLevel(level => { + proxy.$setLevel(level); + this._loggers.forEach(value => value.setLevel(level)); + }); + } + + dispose(): void { + this._logListener.dispose(); + this._loggers.forEach(value => value.dispose()); + this._loggers.clear(); } -} \ No newline at end of file + $log(file: UriComponents, level: LogLevel, message: any[]): void { + const uri = URI.revive(file); + let logger = this._loggers.get(uri.toString()); + if (!logger) { + logger = this._instaService.createInstance(FileLogService, basename(file.path), URI.revive(file), this._logService.getLevel()); + this._loggers.set(uri.toString(), logger); + } + logger.log(level, message); + } +} diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index f433ba27025a1..f7f61cf6255b8 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1284,6 +1284,10 @@ export interface ExtHostLogServiceShape { $setLevel(level: LogLevel): void; } +export interface MainThreadLogShape { + $log(file: UriComponents, level: LogLevel, args: any[]): void; +} + export interface ExtHostOutputServiceShape { $setVisibleChannel(channelId: string | null): void; } @@ -1326,6 +1330,7 @@ export const MainContext = { MainThreadKeytar: createMainId('MainThreadKeytar'), MainThreadLanguageFeatures: createMainId('MainThreadLanguageFeatures'), MainThreadLanguages: createMainId('MainThreadLanguages'), + MainThreadLog: createMainId('MainThread'), MainThreadMessageService: createMainId('MainThreadMessageService'), MainThreadOutputService: createMainId('MainThreadOutputService'), MainThreadProgress: createMainId('MainThreadProgress'), diff --git a/src/vs/workbench/api/worker/extHostLogService.ts b/src/vs/workbench/api/worker/extHostLogService.ts index 79211c61945eb..d29f41ec939e2 100644 --- a/src/vs/workbench/api/worker/extHostLogService.ts +++ b/src/vs/workbench/api/worker/extHostLogService.ts @@ -4,24 +4,33 @@ *--------------------------------------------------------------------------------------------*/ import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log'; -import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostLogServiceShape, MainThreadLogShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput'; -import * as vscode from 'vscode'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { joinPath } from 'vs/base/common/resources'; +import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; +import { UriComponents } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; export class ExtHostLogService extends AbstractLogService implements ILogService, ExtHostLogServiceShape { _serviceBrand: any; - private readonly _logChannel: vscode.OutputChannel; + private readonly _proxy: MainThreadLogShape; + private readonly _logFile: UriComponents; constructor( + @IExtHostRpcService rpc: IExtHostRpcService, @IExtHostInitDataService initData: IExtHostInitDataService, @IExtHostOutputService extHostOutputService: IExtHostOutputService ) { super(); + const logFile = joinPath(initData.logsLocation, `${ExtensionHostLogFileName}.log`); + this._proxy = rpc.getProxy(MainContext.MainThreadLog); + this._logFile = logFile.toJSON(); this.setLevel(initData.logLevel); - this._logChannel = extHostOutputService.createOutputChannel('Log (Worker Extension Host)'); + extHostOutputService.createOutputChannelFromLogFile(localize('name', "Worker Extension Host"), logFile); } $setLevel(level: LogLevel): void { @@ -30,55 +39,37 @@ export class ExtHostLogService extends AbstractLogService implements ILogService trace(_message: string, ..._args: any[]): void { if (this.getLevel() <= LogLevel.Trace) { - this._logChannel.appendLine(this._format(arguments)); + this._proxy.$log(this._logFile, LogLevel.Trace, Array.from(arguments)); } } debug(_message: string, ..._args: any[]): void { if (this.getLevel() <= LogLevel.Debug) { - this._logChannel.appendLine(this._format(arguments)); + this._proxy.$log(this._logFile, LogLevel.Debug, Array.from(arguments)); } } info(_message: string, ..._args: any[]): void { if (this.getLevel() <= LogLevel.Info) { - this._logChannel.appendLine(this._format(arguments)); + this._proxy.$log(this._logFile, LogLevel.Info, Array.from(arguments)); } } warn(_message: string, ..._args: any[]): void { if (this.getLevel() <= LogLevel.Warning) { - this._logChannel.appendLine(this._format(arguments)); + this._proxy.$log(this._logFile, LogLevel.Warning, Array.from(arguments)); } } error(_message: string | Error, ..._args: any[]): void { if (this.getLevel() <= LogLevel.Error) { - this._logChannel.appendLine(this._format(arguments)); + this._proxy.$log(this._logFile, LogLevel.Error, Array.from(arguments)); } } critical(_message: string | Error, ..._args: any[]): void { if (this.getLevel() <= LogLevel.Critical) { - this._logChannel.appendLine(String(arguments)); + this._proxy.$log(this._logFile, LogLevel.Critical, Array.from(arguments)); } } - - private _format(args: any): string { - let result = ''; - - for (let i = 0; i < args.length; i++) { - let a = args[i]; - - if (typeof a === 'object') { - try { - a = JSON.stringify(a); - } catch (e) { } - } - - result += (i > 0 ? ' ' : '') + a; - } - - return result; - } } diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 78076e314808d..80dc149b2ac64 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -28,6 +28,7 @@ import { Schemas } from 'vs/base/common/network'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; import { DeltaExtensionsResult } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; +import { INMEMORY_LOG_SCHEME } from 'vs/workbench/services/log/common/inMemoryLogProvider'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -86,7 +87,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten const result: ExtensionHostProcessManager[] = []; const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => isWebExtension(ext, this._configService))); - const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.parse('empty:value')); //todo@joh + const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.from({ scheme: INMEMORY_LOG_SCHEME, path: '/' })); const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents); result.push(webHostProcessManager); From 379623e3691e3388a6ada975569b12daba833689 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 21 Aug 2019 18:04:51 +0200 Subject: [PATCH 464/613] update yarn.lock files --- extensions/configuration-editing/yarn.lock | 8 ++++---- extensions/extension-editing/yarn.lock | 8 ++++---- extensions/json-language-features/server/yarn.lock | 5 +++++ extensions/npm/yarn.lock | 8 ++++---- extensions/typescript-language-features/yarn.lock | 8 ++++---- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/extensions/configuration-editing/yarn.lock b/extensions/configuration-editing/yarn.lock index ce653f097a9e8..39ad23373148d 100644 --- a/extensions/configuration-editing/yarn.lock +++ b/extensions/configuration-editing/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== -jsonc-parser@2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.2.tgz#42fcf56d70852a043fadafde51ddb4a85649978d" - integrity sha512-TSU435K5tEKh3g7bam1AFf+uZrISheoDsLlpmAo6wWZYqjsnd09lHYK1Qo+moK4Ikifev1Gdpa69g4NELKnCrQ== +jsonc-parser@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e" + integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/extension-editing/yarn.lock b/extensions/extension-editing/yarn.lock index 971bf580ba7e8..fb98728f237d7 100644 --- a/extensions/extension-editing/yarn.lock +++ b/extensions/extension-editing/yarn.lock @@ -29,10 +29,10 @@ entities@~1.1.1: resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA= -jsonc-parser@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.2.tgz#42fcf56d70852a043fadafde51ddb4a85649978d" - integrity sha512-TSU435K5tEKh3g7bam1AFf+uZrISheoDsLlpmAo6wWZYqjsnd09lHYK1Qo+moK4Ikifev1Gdpa69g4NELKnCrQ== +jsonc-parser@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e" + integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g== linkify-it@^2.0.0: version "2.0.3" diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 3bfcb75ac7da7..3c176e126036b 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -59,6 +59,11 @@ jsonc-parser@^2.1.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.0.tgz#eb0d0c7a3c33048524ce3574c57c7278fb2f1bf3" integrity sha512-n9GrT8rrr2fhvBbANa1g+xFmgGK5X91KFeDwlKQ3+SJfmH5+tKv/M/kahx/TXOMflfWHKGKqKyfHQaLKTNzJ6w== +jsonc-parser@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e" + integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" diff --git a/extensions/npm/yarn.lock b/extensions/npm/yarn.lock index e80a4ca5a2daa..0f34421c9aaed 100644 --- a/extensions/npm/yarn.lock +++ b/extensions/npm/yarn.lock @@ -72,10 +72,10 @@ https-proxy-agent@^2.2.1: agent-base "^4.1.0" debug "^3.1.0" -jsonc-parser@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.2.tgz#42fcf56d70852a043fadafde51ddb4a85649978d" - integrity sha512-TSU435K5tEKh3g7bam1AFf+uZrISheoDsLlpmAo6wWZYqjsnd09lHYK1Qo+moK4Ikifev1Gdpa69g4NELKnCrQ== +jsonc-parser@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e" + integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g== minimatch@^3.0.4: version "3.0.4" diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index 683f002c7278d..682bc093c6c10 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -1027,10 +1027,10 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -jsonc-parser@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.1.tgz#9d23cd2709714fff508a1a6679d82135bee1ae60" - integrity sha512-9w/QyN9qF1dTlffzkmyITa6qAYt6sDArlVZqaP+CXnRh66V73wImQGG8GIBkuas0XLAxddWEWYQ8PPFoK5KNQA== +jsonc-parser@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e" + integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g== jsonify@~0.0.0: version "0.0.0" From 8d70c559e5f81f71be0dd418b3a27ddb35ef5fe4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 21 Aug 2019 18:05:18 +0200 Subject: [PATCH 465/613] npm installing --- build/lib/tslint/abstractGlobalsRule.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/lib/tslint/abstractGlobalsRule.js b/build/lib/tslint/abstractGlobalsRule.js index 0691ce05608f5..1566c0aa57605 100644 --- a/build/lib/tslint/abstractGlobalsRule.js +++ b/build/lib/tslint/abstractGlobalsRule.js @@ -35,7 +35,7 @@ class AbstractGlobalsRuleWalker extends Lint.RuleWalker { } return false; })) { - this.addFailureAtNode(node, `!!Cannot use global '${node.text}' in '${this._config.target}'`); + this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`); } } } From abff050f23abe11065f805a26b792cb71ef9a28c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Aug 2019 18:28:27 +0200 Subject: [PATCH 466/613] editor - :lipstick: --- .../services/editor/browser/editorService.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 02bd141ac06be..ca6f5cf469a36 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -272,7 +272,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Respect option to reveal an editor if it is already visible in any group if (options && options.revealIfVisible) { for (const group of groupsByLastActive) { - if (input.matches(group.activeEditor)) { + if (group.isActive(input)) { targetGroup = group; break; } @@ -280,14 +280,16 @@ export class EditorService extends Disposable implements EditorServiceImpl { } // Respect option to reveal an editor if it is open (not necessarily visible) - if ((options && options.revealIfOpened) || this.configurationService.getValue('workbench.editor.revealIfOpen')) { - for (const group of groupsByLastActive) { - if (group.isOpened(input) && group.isActive(input)) { - targetGroup = group; - break; - } - if (group.isOpened(input) && !targetGroup) { - targetGroup = group; + if (!targetGroup) { + if ((options && options.revealIfOpened) || this.configurationService.getValue('workbench.editor.revealIfOpen')) { + for (const group of groupsByLastActive) { + if (group.isOpened(input) && group.isActive(input)) { + targetGroup = group; + break; + } + if (group.isOpened(input) && !targetGroup) { + targetGroup = group; + } } } } From ec067d3b5544a14a2b25d42f97b92ebe0ca1a354 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Aug 2019 18:40:18 +0200 Subject: [PATCH 467/613] builds - add build to merge exploration --- build/azure-pipelines/exploration-build.yml | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 build/azure-pipelines/exploration-build.yml diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml new file mode 100644 index 0000000000000..1fbc9eaa9cc4d --- /dev/null +++ b/build/azure-pipelines/exploration-build.yml @@ -0,0 +1,37 @@ +trigger: + branches: + include: ['master'] +pr: + branches: + include: ['master'] + +steps: +- task: NodeTool@0 + inputs: + versionSpec: "10.15.1" + +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + +- script: | + set -e + + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF + + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + + git checkout origin/ben/electron-test + git merge origin/master + + # Push master branch into exploration branch + git push origin HEAD:ben/electron-test + + displayName: Sync & Merge Exploration From 08c77578726bfa102e390221fe63a594932af71e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 21 Aug 2019 18:53:05 +0200 Subject: [PATCH 468/613] internally, add CompletionItemKindModifier, #50972 --- src/vs/editor/common/modes.ts | 11 +++++++--- .../common/standalone/standaloneEnums.ts | 4 ++++ .../editor/contrib/suggest/suggestWidget.ts | 20 ++++++++----------- .../standalone/browser/standaloneLanguages.ts | 1 + src/vs/monaco.d.ts | 9 +++++++-- .../api/browser/mainThreadLanguageFeatures.ts | 4 ++-- 6 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index f0b9934fcff7d..4689f47200137 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -367,6 +367,10 @@ export let completionKindFromString: { }; })(); +export const enum CompletionItemKindModifier { + Deprecated = 1 +} + export const enum CompletionItemInsertTextRule { /** * Adjust whitespace/indentation of multiline insert texts to @@ -397,9 +401,10 @@ export interface CompletionItem { */ kind: CompletionItemKind; /** - * Indicates if this item is deprecated. + * A modifier to the `kind` which affect how the item + * is rendered, e.g. Deprecated is rendered with a strikeout */ - deprecated?: boolean; + kindModifier?: CompletionItemKindModifier; /** * A human-readable string with additional information * about this item, like type or symbol information. @@ -468,7 +473,7 @@ export interface CompletionItem { /** * @internal */ - [key: string]: any; + _id?: [number, number]; } export interface CompletionList { diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 46d295d2398c3..76ee0d96933f2 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -581,6 +581,10 @@ export enum CompletionItemKind { Snippet = 25 } +export enum CompletionItemKindModifier { + Deprecated = 1 +} + export enum CompletionItemInsertTextRule { /** * Adjust whitespace/indentation of multiline insert texts to diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 04ed87429eadf..b5731357b0e04 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -30,7 +30,7 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; -import { CompletionItemKind, completionKindToCssClass } from 'vs/editor/common/modes'; +import { CompletionItemKind, completionKindToCssClass, CompletionItemKindModifier } from 'vs/editor/common/modes'; import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -38,6 +38,7 @@ import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { FileKind } from 'vs/platform/files/common/files'; import { MarkdownString } from 'vs/base/common/htmlContent'; +import { flatten } from 'vs/base/common/arrays'; const expandSuggestionDocsByDefault = false; @@ -60,11 +61,6 @@ export const editorSuggestWidgetForeground = registerColor('editorSuggestWidget. export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: listFocusBackground, light: listFocusBackground, hc: listFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.')); export const editorSuggestWidgetHighlightForeground = registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.')); -/** - * Suggest widget styles - */ -const editorSuggestWidgetDeprecatedClassName = 'suggest-widget-deprecated'; - const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i; function extractColor(item: CompletionItem, out: string[]): boolean { if (item.completion.label.match(colorRegExp)) { @@ -177,18 +173,18 @@ class Renderer implements IListRenderer } else if (suggestion.kind === CompletionItemKind.File && this._themeService.getIconTheme().hasFileIcons) { // special logic for 'file' completion items data.icon.className = 'icon hide'; - labelOptions.extraClasses = ([] as string[]).concat( + labelOptions.extraClasses = flatten([ getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FILE), getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FILE) - ); + ]); } else if (suggestion.kind === CompletionItemKind.Folder && this._themeService.getIconTheme().hasFolderIcons) { // special logic for 'folder' completion items data.icon.className = 'icon hide'; - labelOptions.extraClasses = ([] as string[]).concat( + labelOptions.extraClasses = flatten([ getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FOLDER), getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FOLDER) - ); + ]); } else { // normal icon data.icon.className = 'icon hide'; @@ -197,8 +193,8 @@ class Renderer implements IListRenderer ]; } - if (suggestion.label && suggestion.deprecated) { - labelOptions.extraClasses = (labelOptions.extraClasses || []).concat([editorSuggestWidgetDeprecatedClassName]); + if (suggestion.kindModifier && suggestion.kindModifier & CompletionItemKindModifier.Deprecated) { + labelOptions.extraClasses = (labelOptions.extraClasses || []).concat(['suggest-widget-deprecated']); } data.iconLabel.setLabel(suggestion.label, undefined, labelOptions); diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index e8e9dffbe8403..30209af005c55 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -562,6 +562,7 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages { // enums DocumentHighlightKind: standaloneEnums.DocumentHighlightKind, CompletionItemKind: standaloneEnums.CompletionItemKind, + CompletionItemKindModifier: standaloneEnums.CompletionItemKindModifier, CompletionItemInsertTextRule: standaloneEnums.CompletionItemInsertTextRule, SymbolKind: standaloneEnums.SymbolKind, IndentAction: standaloneEnums.IndentAction, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index a1cf394ed3956..545a3d799c3f3 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4786,6 +4786,10 @@ declare namespace monaco.languages { Snippet = 25 } + export enum CompletionItemKindModifier { + Deprecated = 1 + } + export enum CompletionItemInsertTextRule { /** * Adjust whitespace/indentation of multiline insert texts to @@ -4815,9 +4819,10 @@ declare namespace monaco.languages { */ kind: CompletionItemKind; /** - * Indicates if this item is deprecated. + * A modifier to the `kind` which affect how the item + * is rendered, e.g. Deprecated is rendered with a strikeout */ - deprecated?: boolean; + kindModifier?: CompletionItemKindModifier; /** * A human-readable string with additional information * about this item, like type or symbol information. diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 2093d2e34ce46..769df9337d9c2 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -330,6 +330,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha return { label: data.a, kind: data.b, + kindModifier: data.n ? modes.CompletionItemKindModifier.Deprecated : undefined, detail: data.c, documentation: data.d, sortText: data.e, @@ -341,7 +342,6 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha commitCharacters: data.k, additionalTextEdits: data.l, command: data.m, - deprecated: data.n, // not-standard _id: data.x, }; @@ -366,7 +366,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha }; if (supportsResolveDetails) { provider.resolveCompletionItem = (model, position, suggestion, token) => { - return this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion._id, token).then(result => { + return this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion._id!, token).then(result => { if (!result) { return suggestion; } From 4e7348b81a4a7a3b8212ac9a7fd26ede6cde1907 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 21 Aug 2019 18:53:20 +0200 Subject: [PATCH 469/613] tslint fix --- src/vs/workbench/contrib/terminal/browser/terminalService.ts | 1 + src/vs/workbench/contrib/terminal/common/terminalService.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index bfa25469cea38..daed808061268 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -26,6 +26,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur export class TerminalService extends CommonTerminalService implements ITerminalService { private _configHelper: IBrowserTerminalConfigHelper; + private _terminalContainer: HTMLElement | undefined; public get configHelper(): ITerminalConfigHelper { return this._configHelper; } diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index d2dc8ec3c59df..1fbdcb6237b84 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -35,7 +35,6 @@ export abstract class TerminalService implements ITerminalService { protected _isShuttingDown: boolean; protected _terminalFocusContextKey: IContextKey; protected _findWidgetVisible: IContextKey; - protected _terminalContainer: HTMLElement | undefined; protected _terminalTabs: ITerminalTab[] = []; protected _backgroundedTerminalInstances: ITerminalInstance[] = []; protected get _terminalInstances(): ITerminalInstance[] { From 77b57600df49cbbae6e00e9e270d06a74ef04819 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 21 Aug 2019 10:18:18 -0700 Subject: [PATCH 470/613] then -> async --- .../contrib/url/common/url.contribution.ts | 103 ++++++++---------- 1 file changed, 48 insertions(+), 55 deletions(-) diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index bead1482b7594..7d8a5f9e8fcf0 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -52,7 +52,7 @@ Registry.as(ActionExtensions.WorkbenchActions).registe const VSCODE_DOMAIN = 'https://code.visualstudio.com'; -const configureTrustedDomainsHandler = ( +const configureTrustedDomainsHandler = async ( quickInputService: IQuickInputService, storageService: IStorageService, domainToConfigure?: string @@ -103,21 +103,19 @@ const configureTrustedDomainsHandler = ( ? specialQuickPickItems : [...specialQuickPickItems, { type: 'separator' }, ...domainQuickPickItems]; - return quickInputService - .pick(quickPickItems, { - canPickMany: true, - activeItem: domainToConfigureItem - }) - .then(result => { - if (result) { - const pickedDomains: string[] = result.map(r => r.id!); - storageService.store('http.trustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL); - - return pickedDomains; - } + const pickedResult = await quickInputService.pick(quickPickItems, { + canPickMany: true, + activeItem: domainToConfigureItem + }); - return []; - }); + if (pickedResult) { + const pickedDomains: string[] = pickedResult.map(r => r.id!); + storageService.store('http.trustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL); + + return pickedDomains; + } + + return []; }; const configureTrustedDomainCommand = { @@ -153,11 +151,11 @@ class OpenerValidatorContributions implements IWorkbenchContribution { this._openerService.registerValidator({ shouldOpen: r => this.validateLink(r) }); } - validateLink(resource: URI): Promise { + async validateLink(resource: URI): Promise { const { scheme, authority } = resource; if (!equalsIgnoreCase(scheme, Schemas.http) && !equalsIgnoreCase(scheme, Schemas.https)) { - return Promise.resolve(true); + return true; } let trustedDomains: string[] = [VSCODE_DOMAIN]; @@ -171,45 +169,40 @@ class OpenerValidatorContributions implements IWorkbenchContribution { const domainToOpen = `${scheme}://${authority}`; if (isDomainTrusted(domainToOpen, trustedDomains)) { - return Promise.resolve(true); + return true; } else { - return this._dialogService - .show( - Severity.Info, - localize( - 'openExternalLinkAt', - 'Do you want {0} to open the external website?\n{1}', - this._productService.nameShort, - resource.toString(true) - ), - [ - localize('openLink', 'Open Link'), - localize('cancel', 'Cancel'), - localize('configureTrustedDomains', 'Configure Trusted Domains') - ], - { - cancelId: 1 - } - ) - .then(choice => { - // Open Link - if (choice === 0) { - return true; - } - // Configure Trusted Domains - else if (choice === 2) { - return configureTrustedDomainsHandler(this._quickInputService, this._storageService, domainToOpen).then( - (pickedDomains: string[]) => { - if (pickedDomains.indexOf(domainToOpen) !== -1) { - return true; - } - return false; - } - ); - } - - return false; - }); + const choice = await this._dialogService.show( + Severity.Info, + localize( + 'openExternalLinkAt', + 'Do you want {0} to open the external website?\n{1}', + this._productService.nameShort, + resource.toString(true) + ), + [ + localize('openLink', 'Open Link'), + localize('cancel', 'Cancel'), + localize('configureTrustedDomains', 'Configure Trusted Domains') + ], + { + cancelId: 1 + } + ); + + // Open Link + if (choice === 0) { + return true; + } + // Configure Trusted Domains + else if (choice === 2) { + const pickedDomains = await configureTrustedDomainsHandler(this._quickInputService, this._storageService, domainToOpen); + if (pickedDomains.indexOf(domainToOpen) !== -1) { + return true; + } + return false; + } + + return false; } } } From 3b911340064291fabfec152f13a3a30f292656da Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 21 Aug 2019 10:29:21 -0700 Subject: [PATCH 471/613] select keybinding entry but not focus when editing keybindings. --- .../contrib/preferences/browser/keybindingsEditor.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index a45a2680f325d..eb7ff5278a611 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -109,7 +109,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor ) { super(KeybindingsEditor.ID, telemetryService, themeService, storageService); this.delayedFiltering = new Delayer(300); - this._register(keybindingsService.onDidUpdateKeybindings(() => this.render(true))); + this._register(keybindingsService.onDidUpdateKeybindings(() => this.render(!!this.keybindingFocusContextKey.get()))); this.keybindingsEditorContextKey = CONTEXT_KEYBINDINGS_EDITOR.bindTo(this.contextKeyService); this.searchFocusContextKey = CONTEXT_KEYBINDINGS_SEARCH_FOCUS.bindTo(this.contextKeyService); @@ -537,7 +537,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor } this.unAssignedKeybindingItemToRevealAndFocus = null; } else if (currentSelectedIndex !== -1 && currentSelectedIndex < this.listEntries.length) { - this.selectEntry(currentSelectedIndex); + this.selectEntry(currentSelectedIndex, preserveFocus); } else if (this.editorService.activeControl === this && !preserveFocus) { this.focus(); } @@ -597,11 +597,13 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor return -1; } - private selectEntry(keybindingItemEntry: IKeybindingItemEntry | number): void { + private selectEntry(keybindingItemEntry: IKeybindingItemEntry | number, focus: boolean = true): void { const index = typeof keybindingItemEntry === 'number' ? keybindingItemEntry : this.getIndexOf(keybindingItemEntry); if (index !== -1) { - this.keybindingsList.getHTMLElement().focus(); - this.keybindingsList.setFocus([index]); + if (focus) { + this.keybindingsList.getHTMLElement().focus(); + this.keybindingsList.setFocus([index]); + } this.keybindingsList.setSelection([index]); } } From 6e43f06929b810ab02fecc4d68d9f533271d7027 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Aug 2019 10:45:35 -0700 Subject: [PATCH 472/613] Fix code highlighting in hovers Fixes #79589 Add a few more tags to allowed attributes --- src/vs/base/browser/markdownRenderer.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 5d94200e15acd..fa6956fdac07d 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -183,9 +183,11 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende allowedAttributes: { 'a': ['href', 'name', 'target', 'data-href'], 'iframe': ['allowfullscreen', 'frameborder', 'src'], - 'img': ['src', 'title', 'alt', 'width', 'height'] + 'img': ['src', 'title', 'alt', 'width', 'height'], + 'div': ['class', 'data-code'] } }); + signalInnerHTML!(); return element; From 79e52cb3e4818c58147ff7d324af63c8be0462e5 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 21 Aug 2019 11:47:34 -0700 Subject: [PATCH 473/613] Move Enter handler to commands from input keydown handler. --- .../browser/find/simpleFindWidget.ts | 19 +---- .../extensions/browser/extensionEditor.ts | 70 +++++++++++++++---- .../terminal/browser/terminal.contribution.ts | 4 +- .../terminal/browser/terminalFindWidget.ts | 2 +- .../browser/dynamicWebviewEditorOverlay.ts | 1 + .../webview/browser/webview.contribution.ts | 26 ++++++- .../contrib/webview/browser/webview.ts | 2 + .../webview/browser/webviewCommands.ts | 23 +++++- .../contrib/webview/browser/webviewEditor.ts | 7 +- .../contrib/webview/browser/webviewElement.ts | 4 ++ .../webview/browser/webviewFindWidget.ts | 13 +++- .../electron-browser/webviewElement.ts | 6 ++ 12 files changed, 137 insertions(+), 40 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index 8ba218619a29d..f036bfb8c58c5 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -9,7 +9,7 @@ import * as dom from 'vs/base/browser/dom'; import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput'; import { Widget } from 'vs/base/browser/ui/widget'; import { Delayer } from 'vs/base/common/async'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { KeyCode } from 'vs/base/common/keyCodes'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; import { SimpleButton } from 'vs/editor/contrib/find/findWidget'; @@ -42,8 +42,7 @@ export abstract class SimpleFindWidget extends Widget { @IContextViewService private readonly _contextViewService: IContextViewService, @IContextKeyService contextKeyService: IContextKeyService, private readonly _state: FindReplaceState = new FindReplaceState(), - showOptionButtons?: boolean, - private readonly _invertDefaultDirection: boolean = false + showOptionButtons?: boolean ) { super(); @@ -94,20 +93,6 @@ export abstract class SimpleFindWidget extends Widget { this.findFirst(); })); - this._register(this._findInput.onKeyDown((e) => { - if (e.equals(KeyCode.Enter)) { - this.find(this._invertDefaultDirection); - e.preventDefault(); - return; - } - - if (e.equals(KeyMod.Shift | KeyCode.Enter)) { - this.find(!this._invertDefaultDirection); - e.preventDefault(); - return; - } - })); - this.prevBtn = this._register(new SimpleButton({ label: NLS_PREVIOUS_MATCH_BTN_LABEL, className: 'previous', diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 272fa9d0591b4..17e83bed31931 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -50,7 +50,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry'; import { isUndefined } from 'vs/base/common/types'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWebviewService, Webview, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { generateUuid } from 'vs/base/common/uuid'; import { platform } from 'vs/base/common/process'; @@ -525,6 +525,12 @@ export class ExtensionEditor extends BaseEditor { } } + runFindAction(previous: boolean): void { + if (this.activeElement && (this.activeElement).runFindAction) { + (this.activeElement).runFindAction(previous); + } + } + private onNavbarChange(extension: IExtension, { id, focus }: { id: string | null, focus: boolean }, template: IExtensionEditorTemplate): void { if (this.editorLoadComplete) { /* __GDPR__ @@ -1314,28 +1320,66 @@ export class ExtensionEditor extends BaseEditor { } } +const contextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', ExtensionEditor.ID), ContextKeyExpr.not('editorFocus')); class ShowExtensionEditorFindCommand extends Command { public runCommand(accessor: ServicesAccessor, args: any): void { - const extensionEditor = this.getExtensionEditor(accessor); + const extensionEditor = getExtensionEditor(accessor); if (extensionEditor) { extensionEditor.showFind(); } } +} +(new ShowExtensionEditorFindCommand({ + id: 'editor.action.extensioneditor.showfind', + precondition: contextKeyExpr, + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_F, + weight: KeybindingWeight.EditorContrib + } +})).register(); - private getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor | null { - const activeControl = accessor.get(IEditorService).activeControl as ExtensionEditor; - if (activeControl instanceof ExtensionEditor) { - return activeControl; +class StartExtensionEditorFindNextCommand extends Command { + public runCommand(accessor: ServicesAccessor, args: any): void { + const extensionEditor = getExtensionEditor(accessor); + if (extensionEditor) { + extensionEditor.runFindAction(false); } - return null; } } -const showCommand = new ShowExtensionEditorFindCommand({ - id: 'editor.action.extensioneditor.showfind', - precondition: ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', ExtensionEditor.ID), ContextKeyExpr.not('editorFocus')), +(new StartExtensionEditorFindNextCommand({ + id: 'editor.action.extensioneditor.findNext', + precondition: ContextKeyExpr.and( + contextKeyExpr, + KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED), kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_F, + primary: KeyCode.Enter, weight: KeybindingWeight.EditorContrib } -}); -showCommand.register(); +})).register(); + +class StartExtensionEditorFindPreviousCommand extends Command { + public runCommand(accessor: ServicesAccessor, args: any): void { + const extensionEditor = getExtensionEditor(accessor); + if (extensionEditor) { + extensionEditor.runFindAction(true); + } + } +} +(new StartExtensionEditorFindPreviousCommand({ + id: 'editor.action.extensioneditor.findPrevious', + precondition: ContextKeyExpr.and( + contextKeyExpr, + KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED), + kbOpts: { + primary: KeyMod.Shift | KeyCode.Enter, + weight: KeybindingWeight.EditorContrib + } +})).register(); + +function getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor | null { + const activeControl = accessor.get(IEditorService).activeControl as ExtensionEditor; + if (activeControl instanceof ExtensionEditor) { + return activeControl; + } + return null; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 99c526eedcac0..b47f264dcb903 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -530,7 +530,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNe }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find next', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID, FindNext.LABEL, { primary: KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3, KeyMod.Shift | KeyCode.Enter] } }, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find next'); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID_TERMINAL_FOCUS, FindPrevious.LABEL, { primary: KeyMod.Shift | KeyCode.F3, @@ -538,7 +538,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, Fi }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find previous', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, { primary: KeyMod.Shift | KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3, KeyCode.Enter] }, }, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find previous'); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts index 87ca61f2d5517..a3d01c1c6e4fe 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts @@ -19,7 +19,7 @@ export class TerminalFindWidget extends SimpleFindWidget { @IContextKeyService private readonly _contextKeyService: IContextKeyService, @ITerminalService private readonly _terminalService: ITerminalService ) { - super(_contextViewService, _contextKeyService, findState, true, true); + super(_contextViewService, _contextKeyService, findState, true); this._register(findState.onFindReplaceStateChange(() => { this.show(); })); diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index 94d8bfa1f36fe..ead4c72d910d5 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -153,6 +153,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewEd reload(): void { this.withWebview(webview => webview.reload()); } showFind(): void { this.withWebview(webview => webview.showFind()); } hideFind(): void { this.withWebview(webview => webview.hideFind()); } + runFindAction(previous: boolean): void { this.withWebview(webview => webview.runFindAction(previous)); } public getInnerWebview() { return this._webview.value; diff --git a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts index 56017d4348f19..e6bf13abce6ae 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts @@ -15,8 +15,8 @@ import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } fro import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory'; -import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview'; -import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand } from '../browser/webviewCommands'; +import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview'; +import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand, WebViewEditorFindNextCommand, WebViewEditorFindPreviousCommand } from '../browser/webviewCommands'; import { WebviewEditor } from '../browser/webviewEditor'; import { WebviewEditorInput } from '../browser/webviewEditorInput'; import { IWebviewEditorService, WebviewEditorService } from '../browser/webviewEditorService'; @@ -58,6 +58,28 @@ function registerWebViewCommands(editorId: string): void { weight: KeybindingWeight.EditorContrib } })).register(); + + (new WebViewEditorFindNextCommand({ + id: WebViewEditorFindNextCommand.ID, + precondition: ContextKeyExpr.and( + contextKeyExpr, + KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED), + kbOpts: { + primary: KeyCode.Enter, + weight: KeybindingWeight.EditorContrib + } + })).register(); + + (new WebViewEditorFindPreviousCommand({ + id: WebViewEditorFindPreviousCommand.ID, + precondition: ContextKeyExpr.and( + contextKeyExpr, + KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED), + kbOpts: { + primary: KeyMod.Shift | KeyCode.Enter, + weight: KeybindingWeight.EditorContrib + } + })).register(); } registerWebViewCommands(WebviewEditor.ID); diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index ea7ecb5a7209f..e7ef6086eaf83 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -16,6 +16,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' * Set when the find widget in a webview is visible. */ export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE = new RawContextKey('webviewFindWidgetVisible', false); +export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED = new RawContextKey('webviewFindWidgetFocused', false); export const IWebviewService = createDecorator('webviewService'); @@ -83,6 +84,7 @@ export interface Webview extends IDisposable { showFind(): void; hideFind(): void; + runFindAction(previous: boolean): void; } export interface WebviewElement extends Webview { diff --git a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts index a5888e3e6680f..62a3eecece587 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts @@ -32,6 +32,27 @@ export class HideWebViewEditorFindCommand extends Command { } } +export class WebViewEditorFindNextCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.findNext'; + + public runCommand(accessor: ServicesAccessor, args: any): void { + const webViewEditor = getActiveWebviewEditor(accessor); + if (webViewEditor) { + webViewEditor.find(false); + } + } +} + +export class WebViewEditorFindPreviousCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.findPrevious'; + + public runCommand(accessor: ServicesAccessor, args: any): void { + const webViewEditor = getActiveWebviewEditor(accessor); + if (webViewEditor) { + webViewEditor.find(true); + } + } +} export class ReloadWebviewAction extends Action { static readonly ID = 'workbench.action.webview.reloadWebviewAction'; static readonly LABEL = nls.localize('refreshWebviewLabel', "Reload Webviews"); @@ -62,4 +83,4 @@ function getActiveWebviewEditor(accessor: ServicesAccessor): WebviewEditor | nul const editorService = accessor.get(IEditorService); const activeControl = editorService.activeControl as WebviewEditor; return activeControl.isWebviewEditor ? activeControl : null; -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index b8d7c6dd48e26..33d33303076bf 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -25,7 +25,6 @@ export class WebviewEditor extends BaseEditor { private readonly _scopedContextKeyService = this._register(new MutableDisposable()); private _findWidgetVisible: IContextKey; - private _editorFrame?: HTMLElement; private _content?: HTMLElement; @@ -79,6 +78,12 @@ export class WebviewEditor extends BaseEditor { this.withWebview(webview => webview.hideFind()); } + public find(previous: boolean) { + this.withWebview(webview => { + webview.runFindAction(previous); + }); + } + public reload() { this.withWebview(webview => webview.reload()); } diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 7a0015be053cc..b18f47f0f9690 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -268,6 +268,10 @@ export class IFrameWebview extends Disposable implements Webview { throw new Error('Method not implemented.'); } + runFindAction(previous: boolean): void { + throw new Error('Method not implemented.'); + } + public set state(state: string | undefined) { this.content = { html: this.content.html, diff --git a/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts b/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts index 0d8e6804bc067..b6c68c732bb67 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { SimpleFindWidget } from 'vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview'; export interface WebviewFindDelegate { find(value: string, previous: boolean): void; @@ -15,6 +16,7 @@ export interface WebviewFindDelegate { } export class WebviewFindWidget extends SimpleFindWidget { + protected _findWidgetFocused: IContextKey; constructor( private readonly _delegate: WebviewFindDelegate, @@ -22,6 +24,7 @@ export class WebviewFindWidget extends SimpleFindWidget { @IContextKeyService contextKeyService: IContextKeyService ) { super(contextViewService, contextKeyService); + this._findWidgetFocused = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED.bindTo(contextKeyService); } public find(previous: boolean) { @@ -47,9 +50,13 @@ export class WebviewFindWidget extends SimpleFindWidget { return false; } - protected onFocusTrackerFocus() { } + protected onFocusTrackerFocus() { + this._findWidgetFocused.set(true); + } - protected onFocusTrackerBlur() { } + protected onFocusTrackerBlur() { + this._findWidgetFocused.reset(); + } protected onFindInputFocusTrackerFocus() { } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index bdd1c7e395680..1fd54f62df64b 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -640,6 +640,12 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { } } + public runFindAction(previous: boolean) { + if (this._webviewFindWidget) { + this._webviewFindWidget.find(previous); + } + } + public reload() { this.doUpdateContent(); } From 026b470f49bc27ace5b270521c837657d0794970 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 21 Aug 2019 11:54:32 -0700 Subject: [PATCH 474/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5342efb46a01e..196cd2d33a65a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "03312cde017811bad062ca8566a7f327f8fdcc34", + "distro": "116231da62de1019aab221a7b307f1d41edb5774", "author": { "name": "Microsoft Corporation" }, From c812d6a99527e13922067aaf999d934d3eaee1f6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Aug 2019 22:37:15 +0200 Subject: [PATCH 475/613] support directories in indexed db fsp --- .../log/browser/indexedDBLogProvider.ts | 116 ++++++++++++++---- 1 file changed, 93 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts index 31c389f4e503f..064e6bd67730a 100644 --- a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts +++ b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts @@ -9,6 +9,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { VSBuffer } from 'vs/base/common/buffer'; import { FileSystemError } from 'vs/workbench/api/common/extHostTypes'; +import { isEqualOrParent, joinPath } from 'vs/base/common/resources'; const LOGS_OBJECT_STORE = 'logs'; export const INDEXEDDB_LOG_SCHEME = 'vscode-logs-indexedbd'; @@ -41,7 +42,7 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi c(db); } }; - request.onupgradeneeded = (e) => { + request.onupgradeneeded = () => { const db = request.result; if (!db.objectStoreNames.contains(LOGS_OBJECT_STORE)) { db.createObjectStore(LOGS_OBJECT_STORE); @@ -55,20 +56,7 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi return Disposable.None; } - mkdir(resource: URI): Promise { - return Promise.reject(new Error('Not Supported')); - } - - rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { - return Promise.reject(new Error('Not Supported')); - } - - readdir(resource: URI): Promise<[string, FileType][]> { - return Promise.reject(new Error('Not Supported')); - } - - delete(resource: URI, opts: FileDeleteOptions): Promise { - return Promise.reject(new Error('Not Supported')); + async mkdir(resource: URI): Promise { } async stat(resource: URI): Promise { @@ -81,33 +69,67 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi size: content.byteLength }; } catch (e) { + } + const files = await this.readdir(resource); + if (files.length) { return { - type: FileType.File, + type: FileType.Directory, ctime: 0, mtime: 0, size: 0 }; } + return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound)); } - async readFile(resource: URI): Promise { + async readdir(resource: URI): Promise<[string, FileType][]> { + const hasKey = await this.hasKey(resource.path); + if (hasKey) { + return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotADirectory)); + } return new Promise(async (c, e) => { const db = await this.database; const transaction = db.transaction([LOGS_OBJECT_STORE]); const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); - const request = objectStore.get(resource.path); + const request = objectStore.getAllKeys(); request.onerror = () => e(request.error); request.onsuccess = () => { - if (request.result) { - c(VSBuffer.fromString(request.result).buffer); - } else { - e(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound)); + const files: [string, FileType][] = []; + const resourceSegments = resource.path.split('/'); + for (const key of request.result) { + if (isEqualOrParent(URI.file(key).with({ scheme: INDEXEDDB_LOG_SCHEME }), resource, false)) { + const keySegments = key.split('/'); + files.push([keySegments[resourceSegments.length], resourceSegments.length + 1 === keySegments.length ? FileType.File : FileType.Directory]); + } } + c(files); }; }); } - writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { + async readFile(resource: URI): Promise { + const hasKey = await this.hasKey(resource.path); + if (!hasKey) { + return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound)); + } + return new Promise(async (c, e) => { + const db = await this.database; + const transaction = db.transaction([LOGS_OBJECT_STORE]); + const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); + const request = objectStore.get(resource.path); + request.onerror = () => e(request.error); + request.onsuccess = () => c(VSBuffer.fromString(request.result || '').buffer); + }); + } + + async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { + const hasKey = await this.hasKey(resource.path); + if (!hasKey) { + const files = await this.readdir(resource); + if (files.length) { + return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileIsADirectory)); + } + } return new Promise(async (c, e) => { const db = await this.database; const transaction = db.transaction([LOGS_OBJECT_STORE], 'readwrite'); @@ -122,4 +144,52 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi }); } + async delete(resource: URI, opts: FileDeleteOptions): Promise { + const hasKey = await this.hasKey(resource.path); + if (hasKey) { + await this.deleteKey(resource.path); + return; + } + + if (opts.recursive) { + const files = await this.readdir(resource); + await Promise.all(files.map(([key]) => this.delete(joinPath(resource, key), opts))); + } + } + + rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + return Promise.reject(new Error('Not Supported')); + } + + private hasKey(key: string): Promise { + return new Promise(async (c, e) => { + const db = await this.database; + const transaction = db.transaction([LOGS_OBJECT_STORE]); + const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); + const request = objectStore.getKey(key); + request.onerror = () => e(request.error); + request.onsuccess = () => { + c(!!request.result); + }; + }); + } + + private deleteKey(key: string): Promise { + return new Promise(async (c, e) => { + const db = await this.database; + const transaction = db.transaction([LOGS_OBJECT_STORE], 'readwrite'); + const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); + const request = objectStore.delete(key); + request.onerror = () => e(request.error); + request.onsuccess = () => { + this.versions.delete(key); + this._onDidChangeFile.fire([{ resource: this.toResource(key), type: FileChangeType.DELETED }]); + c(); + }; + }); + } + + private toResource(key: string): URI { + return URI.file(key).with({ scheme: INDEXEDDB_LOG_SCHEME }); + } } From 6a1798e39a319573de1d2a3f1779de834bb2e2e3 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Aug 2019 22:49:29 +0200 Subject: [PATCH 476/613] pass logs path instead of log file --- src/vs/workbench/browser/web.main.ts | 7 ++++--- .../services/environment/browser/environmentService.ts | 7 +++---- .../test/electron-browser/fileUserDataProvider.test.ts | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 3ea46019652b5..ce1979379268a 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -44,6 +44,7 @@ import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/ import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { INMEMORY_LOG_SCHEME, InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider'; import { FileLogService } from 'vs/platform/log/common/fileLogService'; +import { toLocalISOString } from 'vs/base/common/date'; class CodeRendererMain extends Disposable { @@ -119,14 +120,14 @@ class CodeRendererMain extends Disposable { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Log - const logFile = URI.file(`window.log`).with({ scheme: INMEMORY_LOG_SCHEME }); + const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: INMEMORY_LOG_SCHEME }); const logService = new BufferLogService(); serviceCollection.set(ILogService, logService); const payload = await this.resolveWorkspaceInitializationPayload(); // Environment - const environmentService = new BrowserWorkbenchEnvironmentService({ workspaceId: payload.id, logFile, ...this.configuration }); + const environmentService = new BrowserWorkbenchEnvironmentService({ workspaceId: payload.id, logsPath, ...this.configuration }); serviceCollection.set(IWorkbenchEnvironmentService, environmentService); // Product @@ -151,7 +152,7 @@ class CodeRendererMain extends Disposable { // InMemory Log fileService.registerProvider(INMEMORY_LOG_SCHEME, new InMemoryLogProvider()); - logService.logger = new FileLogService('window', logFile, logService.getLevel(), fileService); + logService.logger = new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService); // Static Extensions const staticExtensions = new StaticExtensionsService(this.configuration.staticExtensions || []); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index bdac4d340ef37..9ee69bc41986b 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -64,7 +64,7 @@ export class BrowserWindowConfiguration implements IWindowConfiguration { interface IBrowserWorkbenchEnvironemntConstructionOptions extends IWorkbenchConstructionOptions { workspaceId: string; - logFile: URI; + logsPath: URI; } export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironmentService { @@ -75,7 +75,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment constructor(readonly options: IBrowserWorkbenchEnvironemntConstructionOptions) { this.args = { _: [] }; - this.logFile = options.logFile; + this.logsPath = options.logsPath.path; + this.logFile = joinPath(options.logsPath, `window-${generateUuid()}`); this.appRoot = '/web/'; this.appNameLong = 'Visual Studio Code - Web'; @@ -90,8 +91,6 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment this.configuration.backupWorkspaceResource = joinPath(this.backupHome, options.workspaceId); this.configuration.connectionToken = options.connectionToken || this.getConnectionTokenFromLocation(); - this.logsPath = '/web/logs'; - this.debugExtensionHost = { port: null, break: false diff --git a/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts b/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts index 777fcedf37b74..e927331f93bf8 100644 --- a/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts +++ b/src/vs/workbench/services/userData/test/electron-browser/fileUserDataProvider.test.ts @@ -47,7 +47,7 @@ suite('FileUserDataProvider', () => { userDataResource = URI.file(userDataPath).with({ scheme: Schemas.userData }); await Promise.all([pfs.mkdirp(userDataPath), pfs.mkdirp(backupsPath)]); - const environmentService = new BrowserWorkbenchEnvironmentService({ remoteAuthority: 'remote', workspaceId: 'workspaceId', logFile: URI.file('logFile') }); + const environmentService = new BrowserWorkbenchEnvironmentService({ remoteAuthority: 'remote', workspaceId: 'workspaceId', logsPath: URI.file('logFile') }); environmentService.userRoamingDataHome = userDataResource; const userDataFileSystemProvider = new FileUserDataProvider(URI.file(userDataPath), URI.file(backupsPath), diskFileSystemProvider, environmentService); @@ -321,7 +321,7 @@ suite('FileUserDataProvider - Watching', () => { localUserDataResource = URI.file(userDataPath); userDataResource = localUserDataResource.with({ scheme: Schemas.userData }); - const environmentService = new BrowserWorkbenchEnvironmentService({ remoteAuthority: 'remote', workspaceId: 'workspaceId', logFile: URI.file('logFile') }); + const environmentService = new BrowserWorkbenchEnvironmentService({ remoteAuthority: 'remote', workspaceId: 'workspaceId', logsPath: URI.file('logFile') }); environmentService.userRoamingDataHome = userDataResource; const userDataFileSystemProvider = new FileUserDataProvider(localUserDataResource, localBackupsResource, new TestFileSystemProvider(fileEventEmitter.event), environmentService); From 9466ecef44aed3befa198ef4715cb67639127e06 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Aug 2019 22:53:21 +0200 Subject: [PATCH 477/613] register indexed db fsp --- src/vs/workbench/browser/web.main.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index ce1979379268a..0b2d4438b1fa5 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -45,6 +45,7 @@ import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { INMEMORY_LOG_SCHEME, InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider'; import { FileLogService } from 'vs/platform/log/common/fileLogService'; import { toLocalISOString } from 'vs/base/common/date'; +import { INDEXEDDB_LOG_SCHEME, IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedDBLogProvider'; class CodeRendererMain extends Disposable { @@ -150,8 +151,9 @@ class CodeRendererMain extends Disposable { const fileService = this._register(new FileService(logService)); serviceCollection.set(IFileService, fileService); - // InMemory Log + // Logger fileService.registerProvider(INMEMORY_LOG_SCHEME, new InMemoryLogProvider()); + fileService.registerProvider(INDEXEDDB_LOG_SCHEME, new IndexedDBLogProvider()); logService.logger = new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService); // Static Extensions From 77c1e7fa7f1e234a12fd908f6fc1ab096f4f0060 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 21 Aug 2019 23:01:49 +0200 Subject: [PATCH 478/613] use indexed db for logging --- src/vs/workbench/browser/web.main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 0b2d4438b1fa5..1b43d77d1182f 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -121,7 +121,7 @@ class CodeRendererMain extends Disposable { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Log - const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: INMEMORY_LOG_SCHEME }); + const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: INDEXEDDB_LOG_SCHEME }); const logService = new BufferLogService(); serviceCollection.set(ILogService, logService); From 558d23bd26a528274fbbc4227f54de69c624ac42 Mon Sep 17 00:00:00 2001 From: Mickael Istria Date: Wed, 21 Aug 2019 22:19:20 +0200 Subject: [PATCH 479/613] Issue #79599 - CSS LS fails if InitializationOptions.dataPaths not set Signed-off-by: Mickael Istria --- extensions/css-language-features/server/src/cssServerMain.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts index 0bfd886c31059..a5c50d515c7e4 100644 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ b/extensions/css-language-features/server/src/cssServerMain.ts @@ -103,7 +103,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { } } - const dataPaths: string[] = params.initializationOptions.dataPaths; + const dataPaths: string[] = params.initializationOptions.dataPaths || []; const customDataProviders = getDataProviders(dataPaths); function getClientCapability(name: string, def: T) { From 6c92c45dec338a92871d40a1ad8c673593542c3d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Aug 2019 11:56:20 -0700 Subject: [PATCH 480/613] Remove extra conditional This value should always be a number --- src/vs/workbench/api/browser/mainThreadWebview.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 4c5b97e060de9..eb727602706dd 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -292,7 +292,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews viewStates[handle] = { visible: input === group.activeEditor, active: input === activeInput, - position: editorGroupToViewColumn(this._editorGroupService, group.id || 0), + position: editorGroupToViewColumn(this._editorGroupService, group.id), }; } } From 2637bf2bf5df83afb48f278885d7712a0686ff22 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Aug 2019 11:57:42 -0700 Subject: [PATCH 481/613] Remove extra conditional guard strict null checks should prevent calling this function with undefined --- src/vs/workbench/api/browser/mainThreadWebview.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index eb727602706dd..d657e9d0d96fd 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -304,10 +304,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews } private onDidClickLink(handle: WebviewPanelHandle, link: URI): void { - if (!link) { - return; - } - const webview = this.getWebviewEditorInput(handle); if (this.isSupportedLink(webview, link)) { this._openerService.open(link); From 5a43543d3ae871d0e290840bab47d471251877bc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Aug 2019 11:58:51 -0700 Subject: [PATCH 482/613] :lipstick: --- .../contrib/webview/electron-browser/webviewElement.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 1fd54f62df64b..ac65e4df36a3b 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -319,7 +319,7 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { return; case 'did-click-link': - let [uri] = event.args; + const [uri] = event.args; this._onDidClickLink.fire(URI.parse(uri)); return; @@ -334,12 +334,10 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview { clientY: rawEvent.clientY + bounds.top, })); return; - } - catch (TypeError) { + } catch { // CustomEvent was treated as MouseEvent so don't do anything - https://github.com/microsoft/vscode/issues/78915 return; } - } case 'did-set-content': From 9ec76f9c7002bd32d22707b90e4c35ce9b1c00da Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 00:01:49 +0200 Subject: [PATCH 483/613] logs data clean up --- .../contrib/logs/common/logs.contribution.ts | 6 ++- .../contrib/logs/common/logsDataCleaner.ts | 44 +++++++++++++++++++ .../environment/browser/environmentService.ts | 2 +- .../log/browser/indexedDBLogProvider.ts | 13 +++--- 4 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 src/vs/workbench/contrib/logs/common/logsDataCleaner.ts diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 7030e38f8ee96..582f1b4efda0b 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -20,6 +20,8 @@ import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { dirname } from 'vs/base/common/resources'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { LogsDataCleaner } from 'vs/workbench/contrib/logs/common/logsDataCleaner'; const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); const devCategory = nls.localize('developer', "Developer"); @@ -30,7 +32,8 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { constructor( @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @ILogService private readonly logService: ILogService, - @IFileService private readonly fileService: IFileService + @IFileService private readonly fileService: IFileService, + @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); if (isWeb) { @@ -42,6 +45,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { private registerWebContributions(): void { Registry.as(OutputExt.OutputChannels).registerChannel({ id: Constants.rendererLogChannelId, label: nls.localize('rendererLog', "Window"), file: this.environmentService.logFile, log: true }); + this.instantiationService.createInstance(LogsDataCleaner); } private registerNativeContributions(): void { diff --git a/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts b/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts new file mode 100644 index 0000000000000..90d328690edc1 --- /dev/null +++ b/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IFileService } from 'vs/platform/files/common/files'; +import { basename, dirname } from 'vs/base/common/resources'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { URI } from 'vs/base/common/uri'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; + +export class LogsDataCleaner extends Disposable { + + constructor( + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IFileService private readonly fileService: IFileService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + ) { + super(); + this.cleanUpOldLogsSoon(); + } + + private cleanUpOldLogsSoon(): void { + let handle: NodeJS.Timeout | undefined = setTimeout(async () => { + handle = undefined; + const logsPath = URI.file(this.environmentService.logsPath).with({ scheme: this.environmentService.logFile.scheme }); + const stat = await this.fileService.resolve(dirname(logsPath)); + if (stat.children) { + const currentLog = basename(logsPath); + const allSessions = stat.children.filter(stat => stat.isDirectory && /^\d{8}T\d{6}$/.test(stat.name)); + const oldSessions = allSessions.sort().filter((d, i) => d.name !== currentLog); + const toDelete = oldSessions.slice(0, Math.max(0, oldSessions.length - 49)); + Promise.all(toDelete.map(stat => this.fileService.del(stat.resource, { recursive: true }))); + } + }, 10 * 1000); + this.lifecycleService.onWillShutdown(() => { + if (handle) { + clearTimeout(handle); + handle = undefined; + } + }); + } +} diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 9ee69bc41986b..821ba14cb2676 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -76,7 +76,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment constructor(readonly options: IBrowserWorkbenchEnvironemntConstructionOptions) { this.args = { _: [] }; this.logsPath = options.logsPath.path; - this.logFile = joinPath(options.logsPath, `window-${generateUuid()}`); + this.logFile = joinPath(options.logsPath, `window-${generateUuid()}.log`); this.appRoot = '/web/'; this.appNameLong = 'Visual Studio Code - Web'; diff --git a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts index 064e6bd67730a..da2859fc2061d 100644 --- a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts +++ b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts @@ -9,7 +9,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { VSBuffer } from 'vs/base/common/buffer'; import { FileSystemError } from 'vs/workbench/api/common/extHostTypes'; -import { isEqualOrParent, joinPath } from 'vs/base/common/resources'; +import { isEqualOrParent, joinPath, relativePath } from 'vs/base/common/resources'; const LOGS_OBJECT_STORE = 'logs'; export const INDEXEDDB_LOG_SCHEME = 'vscode-logs-indexedbd'; @@ -95,11 +95,14 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi request.onerror = () => e(request.error); request.onsuccess = () => { const files: [string, FileType][] = []; - const resourceSegments = resource.path.split('/'); for (const key of request.result) { - if (isEqualOrParent(URI.file(key).with({ scheme: INDEXEDDB_LOG_SCHEME }), resource, false)) { - const keySegments = key.split('/'); - files.push([keySegments[resourceSegments.length], resourceSegments.length + 1 === keySegments.length ? FileType.File : FileType.Directory]); + const keyResource = this.toResource(key); + if (isEqualOrParent(keyResource, resource, false)) { + const path = relativePath(resource, keyResource, false); + if (path) { + const keySegments = path.split('/'); + files.push([keySegments[0], keySegments.length === 1 ? FileType.File : FileType.Directory]); + } } } c(files); From df8220e0321d225e118bbbe77f7fa5ca17ace104 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 00:07:12 +0200 Subject: [PATCH 484/613] :lipstick: --- .../log/browser/indexedDBLogProvider.ts | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts index da2859fc2061d..1882c49a77b84 100644 --- a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts +++ b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts @@ -11,8 +11,9 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { FileSystemError } from 'vs/workbench/api/common/extHostTypes'; import { isEqualOrParent, joinPath, relativePath } from 'vs/base/common/resources'; -const LOGS_OBJECT_STORE = 'logs'; export const INDEXEDDB_LOG_SCHEME = 'vscode-logs-indexedbd'; +export const INDEXEDDB_LOGS_DB = 'vscode-logs-db'; +export const INDEXEDDB_LOGS_OBJECT_STORE = 'vscode-logs-store'; export class IndexedDBLogProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { @@ -29,23 +30,23 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi constructor( ) { super(); - this.database = this.openDatabase(2); + this.database = this.openDatabase(1); } private openDatabase(version: number): Promise { return new Promise((c, e) => { - const request = window.indexedDB.open('LoggingDatabase', version); + const request = window.indexedDB.open(INDEXEDDB_LOGS_DB, version); request.onerror = (err) => e(request.error); request.onsuccess = () => { const db = request.result; - if (db.objectStoreNames.contains(LOGS_OBJECT_STORE)) { + if (db.objectStoreNames.contains(INDEXEDDB_LOGS_OBJECT_STORE)) { c(db); } }; request.onupgradeneeded = () => { const db = request.result; - if (!db.objectStoreNames.contains(LOGS_OBJECT_STORE)) { - db.createObjectStore(LOGS_OBJECT_STORE); + if (!db.objectStoreNames.contains(INDEXEDDB_LOGS_OBJECT_STORE)) { + db.createObjectStore(INDEXEDDB_LOGS_OBJECT_STORE); } c(db); }; @@ -89,8 +90,8 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi } return new Promise(async (c, e) => { const db = await this.database; - const transaction = db.transaction([LOGS_OBJECT_STORE]); - const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); + const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]); + const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); const request = objectStore.getAllKeys(); request.onerror = () => e(request.error); request.onsuccess = () => { @@ -117,8 +118,8 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi } return new Promise(async (c, e) => { const db = await this.database; - const transaction = db.transaction([LOGS_OBJECT_STORE]); - const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); + const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]); + const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); const request = objectStore.get(resource.path); request.onerror = () => e(request.error); request.onsuccess = () => c(VSBuffer.fromString(request.result || '').buffer); @@ -135,8 +136,8 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi } return new Promise(async (c, e) => { const db = await this.database; - const transaction = db.transaction([LOGS_OBJECT_STORE], 'readwrite'); - const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); + const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE], 'readwrite'); + const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); const request = objectStore.put(VSBuffer.wrap(content).toString(), resource.path); request.onerror = () => e(request.error); request.onsuccess = () => { @@ -167,8 +168,8 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi private hasKey(key: string): Promise { return new Promise(async (c, e) => { const db = await this.database; - const transaction = db.transaction([LOGS_OBJECT_STORE]); - const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); + const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]); + const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); const request = objectStore.getKey(key); request.onerror = () => e(request.error); request.onsuccess = () => { @@ -180,8 +181,8 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi private deleteKey(key: string): Promise { return new Promise(async (c, e) => { const db = await this.database; - const transaction = db.transaction([LOGS_OBJECT_STORE], 'readwrite'); - const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); + const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE], 'readwrite'); + const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); const request = objectStore.delete(key); request.onerror = () => e(request.error); request.onsuccess = () => { From 0b0577ea5de0b56a05f8cf7e03048536e6412aad Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 01:09:14 +0200 Subject: [PATCH 485/613] browse session logs --- .../contrib/logs/common/logs.contribution.ts | 6 +- .../contrib/logs/common/logsActions.ts | 72 ++++++++++++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 582f1b4efda0b..b2ad741a15ea7 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -8,7 +8,7 @@ import { join } from 'vs/base/common/path'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { SetLogLevelAction, OpenLogsFolderAction } from 'vs/workbench/contrib/logs/common/logsActions'; +import { SetLogLevelAction, OpenLogsFolderAction, OpenSessionLogFileAction } from 'vs/workbench/contrib/logs/common/logsActions'; import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -46,6 +46,10 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { private registerWebContributions(): void { Registry.as(OutputExt.OutputChannels).registerChannel({ id: Constants.rendererLogChannelId, label: nls.localize('rendererLog', "Window"), file: this.environmentService.logFile, log: true }); this.instantiationService.createInstance(LogsDataCleaner); + + const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); + const devCategory = nls.localize('developer', "Developer"); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenSessionLogFileAction, OpenSessionLogFileAction.ID, OpenSessionLogFileAction.LABEL), 'Developer: Open Log File (Session)...', devCategory); } private registerNativeContributions(): void { diff --git a/src/vs/workbench/contrib/logs/common/logsActions.ts b/src/vs/workbench/contrib/logs/common/logsActions.ts index 56b54a4c89cb7..abaf79a640c4c 100644 --- a/src/vs/workbench/contrib/logs/common/logsActions.ts +++ b/src/vs/workbench/contrib/logs/common/logsActions.ts @@ -9,8 +9,13 @@ import { join } from 'vs/base/common/path'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { ILogService, LogLevel, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { URI } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { dirname, basename, isEqual } from 'vs/base/common/resources'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { startsWith } from 'vs/base/common/strings'; export class OpenLogsFolderAction extends Action { @@ -73,3 +78,68 @@ export class SetLogLevelAction extends Action { return undefined; } } + +export class OpenSessionLogFileAction extends Action { + + static ID = 'workbench.action.openSessionLogFile'; + static LABEL = nls.localize('openSessionLogFile', "Open Log File (Session)..."); + + constructor(id: string, label: string, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IFileService private readonly fileService: IFileService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @IEditorService private readonly editorService: IEditorService, + ) { + super(id, label); + } + + async run(): Promise { + const sessionResult = await this.quickInputService.pick( + this.getSessions().then(sessions => sessions.map((s, index) => ({ + id: s.toString(), + label: basename(s), + description: index === 0 ? nls.localize('current', "Current") : undefined + }))), + { + canPickMany: false, + placeHolder: nls.localize('sessions placeholder', "Select Session") + }); + if (sessionResult) { + const logFileResult = await this.quickInputService.pick( + this.getLogFiles(URI.parse(sessionResult.id!)).then(logFiles => logFiles.map(s => ({ + id: s.toString(), + label: startsWith(basename(s), 'window') ? nls.localize('window', "Window") : basename(s) + }))), + { + canPickMany: false, + placeHolder: nls.localize('log placeholder', "Select Log file") + }); + if (logFileResult) { + return this.editorService.openEditor({ resource: URI.parse(logFileResult.id!) }).then(() => undefined); + } + } + } + + private async getSessions(): Promise { + const logsPath = URI.file(this.environmentService.logsPath).with({ scheme: this.environmentService.logFile.scheme }); + const result: URI[] = [logsPath]; + const stat = await this.fileService.resolve(dirname(logsPath)); + if (stat.children) { + result.push(...stat.children + .filter(stat => !isEqual(stat.resource, logsPath) && stat.isDirectory && /^\d{8}T\d{6}$/.test(stat.name)) + .sort() + .reverse() + .map(d => d.resource)); + } + return result; + } + + private async getLogFiles(session: URI): Promise { + const stat = await this.fileService.resolve(session); + if (stat.children) { + return stat.children.filter(stat => !stat.isDirectory).map(stat => stat.resource); + } + return []; + } +} + From 8b61c15d3d0a23198e4b8dd7007460a747f2ad9c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Aug 2019 16:22:47 -0700 Subject: [PATCH 486/613] Use per-resource settings for js/ts validate.enable For #78549 but requires more work --- .../src/features/bufferSyncSupport.ts | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index f662cb21af638..c99e7b777f946 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -310,8 +310,6 @@ export default class BufferSyncSupport extends Disposable { private readonly client: ITypeScriptServiceClient; - private _validateJavaScript: boolean = true; - private _validateTypeScript: boolean = true; private readonly modeIds: Set; private readonly syncedBuffers: SyncedBufferMap; private readonly pendingDiagnostics: PendingDiagnostics; @@ -334,9 +332,6 @@ export default class BufferSyncSupport extends Disposable { this.syncedBuffers = new SyncedBufferMap(pathNormalizer); this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer); this.synchronizer = new BufferSynchronizer(client); - - this.updateConfiguration(); - vscode.workspace.onDidChangeConfiguration(this.updateConfiguration, this, this._disposables); } private readonly _onDelete = this._register(new vscode.EventEmitter()); @@ -511,22 +506,20 @@ export default class BufferSyncSupport extends Disposable { this.pendingDiagnostics.clear(); } - private updateConfiguration() { - const jsConfig = vscode.workspace.getConfiguration('javascript', null); - const tsConfig = vscode.workspace.getConfiguration('typescript', null); - - this._validateJavaScript = jsConfig.get('validate.enable', true); - this._validateTypeScript = tsConfig.get('validate.enable', true); - } - private shouldValidate(buffer: SyncedBuffer) { switch (buffer.kind) { case BufferKind.JavaScript: - return this._validateJavaScript; + { + const config = vscode.workspace.getConfiguration('javascript', buffer.resource); + return config.get('validate.enable', true); + } case BufferKind.TypeScript: default: - return this._validateTypeScript; + { + const config = vscode.workspace.getConfiguration('typescript', buffer.resource); + return config.get('validate.enable', true); + } } } } From 6443e8a06ea9f0d57f7f15918c669a72a306e44e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 01:39:48 +0200 Subject: [PATCH 487/613] Extract and create key value log provider --- .../log/browser/indexedDBLogProvider.ts | 145 +++--------------- .../log/common/keyValueLogProvider.ts | 128 ++++++++++++++++ 2 files changed, 150 insertions(+), 123 deletions(-) create mode 100644 src/vs/workbench/services/log/common/keyValueLogProvider.ts diff --git a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts index 1882c49a77b84..3a662c08f53bb 100644 --- a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts +++ b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts @@ -3,33 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; -import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions, FileChangeType, FileSystemProviderErrorCode } from 'vs/platform/files/common/files'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { FileSystemError } from 'vs/workbench/api/common/extHostTypes'; -import { isEqualOrParent, joinPath, relativePath } from 'vs/base/common/resources'; +import { KeyValueLogProvider } from 'vs/workbench/services/log/common/keyValueLogProvider'; export const INDEXEDDB_LOG_SCHEME = 'vscode-logs-indexedbd'; export const INDEXEDDB_LOGS_DB = 'vscode-logs-db'; export const INDEXEDDB_LOGS_OBJECT_STORE = 'vscode-logs-store'; -export class IndexedDBLogProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { - - readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; - readonly onDidChangeCapabilities: Event = Event.None; - - private readonly _onDidChangeFile: Emitter = this._register(new Emitter()); - readonly onDidChangeFile: Event = this._onDidChangeFile.event; - - private readonly versions: Map = new Map(); +export class IndexedDBLogProvider extends KeyValueLogProvider { private readonly database: Promise; constructor( ) { - super(); + super(INDEXEDDB_LOG_SCHEME); this.database = this.openDatabase(1); } @@ -53,147 +39,60 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi }); } - watch(resource: URI, opts: IWatchOptions): IDisposable { - return Disposable.None; - } - - async mkdir(resource: URI): Promise { - } - - async stat(resource: URI): Promise { - try { - const content = await this.readFile(resource); - return { - type: FileType.File, - ctime: 0, - mtime: this.versions.get(resource.toString()) || 0, - size: content.byteLength - }; - } catch (e) { - } - const files = await this.readdir(resource); - if (files.length) { - return { - type: FileType.Directory, - ctime: 0, - mtime: 0, - size: 0 - }; - } - return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound)); - } - - async readdir(resource: URI): Promise<[string, FileType][]> { - const hasKey = await this.hasKey(resource.path); - if (hasKey) { - return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotADirectory)); - } + protected async getAllKeys(): Promise { return new Promise(async (c, e) => { const db = await this.database; const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]); const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); const request = objectStore.getAllKeys(); request.onerror = () => e(request.error); - request.onsuccess = () => { - const files: [string, FileType][] = []; - for (const key of request.result) { - const keyResource = this.toResource(key); - if (isEqualOrParent(keyResource, resource, false)) { - const path = relativePath(resource, keyResource, false); - if (path) { - const keySegments = path.split('/'); - files.push([keySegments[0], keySegments.length === 1 ? FileType.File : FileType.Directory]); - } - } - } - c(files); - }; + request.onsuccess = () => c(request.result); }); } - async readFile(resource: URI): Promise { - const hasKey = await this.hasKey(resource.path); - if (!hasKey) { - return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound)); - } - return new Promise(async (c, e) => { + protected hasKey(key: string): Promise { + return new Promise(async (c, e) => { const db = await this.database; const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]); const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); - const request = objectStore.get(resource.path); + const request = objectStore.getKey(key); request.onerror = () => e(request.error); - request.onsuccess = () => c(VSBuffer.fromString(request.result || '').buffer); + request.onsuccess = () => { + c(!!request.result); + }; }); } - async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { - const hasKey = await this.hasKey(resource.path); - if (!hasKey) { - const files = await this.readdir(resource); - if (files.length) { - return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileIsADirectory)); - } - } + protected getValue(key: string): Promise { return new Promise(async (c, e) => { const db = await this.database; - const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE], 'readwrite'); + const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]); const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); - const request = objectStore.put(VSBuffer.wrap(content).toString(), resource.path); + const request = objectStore.get(key); request.onerror = () => e(request.error); - request.onsuccess = () => { - this.versions.set(resource.toString(), (this.versions.get(resource.toString()) || 0) + 1); - this._onDidChangeFile.fire([{ resource, type: FileChangeType.UPDATED }]); - c(); - }; + request.onsuccess = () => c(request.result || ''); }); } - async delete(resource: URI, opts: FileDeleteOptions): Promise { - const hasKey = await this.hasKey(resource.path); - if (hasKey) { - await this.deleteKey(resource.path); - return; - } - - if (opts.recursive) { - const files = await this.readdir(resource); - await Promise.all(files.map(([key]) => this.delete(joinPath(resource, key), opts))); - } - } - - rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { - return Promise.reject(new Error('Not Supported')); - } - - private hasKey(key: string): Promise { - return new Promise(async (c, e) => { + protected setValue(key: string, value: string): Promise { + return new Promise(async (c, e) => { const db = await this.database; - const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]); + const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE], 'readwrite'); const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); - const request = objectStore.getKey(key); + const request = objectStore.put(value, key); request.onerror = () => e(request.error); - request.onsuccess = () => { - c(!!request.result); - }; + request.onsuccess = () => c(); }); } - private deleteKey(key: string): Promise { + protected deleteKey(key: string): Promise { return new Promise(async (c, e) => { const db = await this.database; const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE], 'readwrite'); const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE); const request = objectStore.delete(key); request.onerror = () => e(request.error); - request.onsuccess = () => { - this.versions.delete(key); - this._onDidChangeFile.fire([{ resource: this.toResource(key), type: FileChangeType.DELETED }]); - c(); - }; + request.onsuccess = () => c(); }); } - - private toResource(key: string): URI { - return URI.file(key).with({ scheme: INDEXEDDB_LOG_SCHEME }); - } } diff --git a/src/vs/workbench/services/log/common/keyValueLogProvider.ts b/src/vs/workbench/services/log/common/keyValueLogProvider.ts new file mode 100644 index 0000000000000..738e35f354da9 --- /dev/null +++ b/src/vs/workbench/services/log/common/keyValueLogProvider.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; +import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions, FileChangeType, FileSystemProviderErrorCode } from 'vs/platform/files/common/files'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { FileSystemError } from 'vs/workbench/api/common/extHostTypes'; +import { isEqualOrParent, joinPath, relativePath } from 'vs/base/common/resources'; + +export abstract class KeyValueLogProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { + + readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; + readonly onDidChangeCapabilities: Event = Event.None; + + private readonly _onDidChangeFile: Emitter = this._register(new Emitter()); + readonly onDidChangeFile: Event = this._onDidChangeFile.event; + + private readonly versions: Map = new Map(); + + constructor(private readonly scheme: string) { + super(); + } + + watch(resource: URI, opts: IWatchOptions): IDisposable { + return Disposable.None; + } + + async mkdir(resource: URI): Promise { + } + + async stat(resource: URI): Promise { + try { + const content = await this.readFile(resource); + return { + type: FileType.File, + ctime: 0, + mtime: this.versions.get(resource.toString()) || 0, + size: content.byteLength + }; + } catch (e) { + } + const files = await this.readdir(resource); + if (files.length) { + return { + type: FileType.Directory, + ctime: 0, + mtime: 0, + size: 0 + }; + } + return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound)); + } + + async readdir(resource: URI): Promise<[string, FileType][]> { + const hasKey = await this.hasKey(resource.path); + if (hasKey) { + return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotADirectory)); + } + const keys = await this.getAllKeys(); + const files: [string, FileType][] = []; + for (const key of keys) { + const keyResource = this.toResource(key); + if (isEqualOrParent(keyResource, resource, false)) { + const path = relativePath(resource, keyResource, false); + if (path) { + const keySegments = path.split('/'); + files.push([keySegments[0], keySegments.length === 1 ? FileType.File : FileType.Directory]); + } + } + } + return files; + } + + async readFile(resource: URI): Promise { + const hasKey = await this.hasKey(resource.path); + if (!hasKey) { + return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound)); + } + const value = await this.getValue(resource.path); + return VSBuffer.fromString(value).buffer; + } + + async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { + const hasKey = await this.hasKey(resource.path); + if (!hasKey) { + const files = await this.readdir(resource); + if (files.length) { + return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileIsADirectory)); + } + } + await this.setValue(resource.path, VSBuffer.wrap(content).toString()); + this.versions.set(resource.toString(), (this.versions.get(resource.toString()) || 0) + 1); + this._onDidChangeFile.fire([{ resource, type: FileChangeType.UPDATED }]); + } + + async delete(resource: URI, opts: FileDeleteOptions): Promise { + const hasKey = await this.hasKey(resource.path); + if (hasKey) { + await this.deleteKey(resource.path); + this.versions.delete(resource.path); + this._onDidChangeFile.fire([{ resource, type: FileChangeType.DELETED }]); + return; + } + + if (opts.recursive) { + const files = await this.readdir(resource); + await Promise.all(files.map(([key]) => this.delete(joinPath(resource, key), opts))); + } + } + + rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + return Promise.reject(new Error('Not Supported')); + } + + private toResource(key: string): URI { + return URI.file(key).with({ scheme: this.scheme }); + } + + protected abstract getAllKeys(): Promise; + protected abstract hasKey(key: string): Promise; + protected abstract getValue(key: string): Promise; + protected abstract setValue(key: string, value: string): Promise; + protected abstract deleteKey(key: string): Promise; +} From a98a127c0bedf805cfb3825f2b6e4ee372675f7e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 01:40:30 +0200 Subject: [PATCH 488/613] use logs path --- .../workbench/services/extensions/browser/extensionService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 80dc149b2ac64..0f09ebb5b6277 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -28,7 +28,6 @@ import { Schemas } from 'vs/base/common/network'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; import { DeltaExtensionsResult } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; -import { INMEMORY_LOG_SCHEME } from 'vs/workbench/services/log/common/inMemoryLogProvider'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -87,7 +86,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten const result: ExtensionHostProcessManager[] = []; const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => isWebExtension(ext, this._configService))); - const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.from({ scheme: INMEMORY_LOG_SCHEME, path: '/' })); + const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.file(this._environmentService.logsPath).with({ scheme: this._environmentService.logFile.scheme })); const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents); result.push(webHostProcessManager); From cfe4c9590d78b2363ea93fb026746960a748b8e5 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 01:49:19 +0200 Subject: [PATCH 489/613] fix naming --- src/vs/workbench/contrib/logs/common/logsActions.ts | 3 +-- .../services/environment/browser/environmentService.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/logs/common/logsActions.ts b/src/vs/workbench/contrib/logs/common/logsActions.ts index abaf79a640c4c..7bf78df5a3b0b 100644 --- a/src/vs/workbench/contrib/logs/common/logsActions.ts +++ b/src/vs/workbench/contrib/logs/common/logsActions.ts @@ -15,7 +15,6 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { dirname, basename, isEqual } from 'vs/base/common/resources'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { startsWith } from 'vs/base/common/strings'; export class OpenLogsFolderAction extends Action { @@ -108,7 +107,7 @@ export class OpenSessionLogFileAction extends Action { const logFileResult = await this.quickInputService.pick( this.getLogFiles(URI.parse(sessionResult.id!)).then(logFiles => logFiles.map(s => ({ id: s.toString(), - label: startsWith(basename(s), 'window') ? nls.localize('window', "Window") : basename(s) + label: basename(s) }))), { canPickMany: false, diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 7b06d5b845d3c..c61e1974692e4 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -76,7 +76,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment constructor(readonly options: IBrowserWorkbenchEnvironemntConstructionOptions) { this.args = { _: [] }; this.logsPath = options.logsPath.path; - this.logFile = joinPath(options.logsPath, `window-${generateUuid()}.log`); + this.logFile = joinPath(options.logsPath, 'window.log'); this.appRoot = '/web/'; this.appNameLong = 'Visual Studio Code - Web'; From 9701ca88544e3658dc1557992a5e7f24f7544892 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 01:55:49 +0200 Subject: [PATCH 490/613] adopt in memory log provider to extend key value provider --- .../log/common/inMemoryLogProvider.ts | 67 +++++-------------- 1 file changed, 15 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/services/log/common/inMemoryLogProvider.ts b/src/vs/workbench/services/log/common/inMemoryLogProvider.ts index 6a01a1dbe1bc3..16208cbae0975 100644 --- a/src/vs/workbench/services/log/common/inMemoryLogProvider.ts +++ b/src/vs/workbench/services/log/common/inMemoryLogProvider.ts @@ -3,75 +3,38 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; -import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions, FileChangeType } from 'vs/platform/files/common/files'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -import { VSBuffer } from 'vs/base/common/buffer'; +import { KeyValueLogProvider } from 'vs/workbench/services/log/common/keyValueLogProvider'; +import { keys } from 'vs/base/common/map'; export const INMEMORY_LOG_SCHEME = 'vscode-logs-inmemory'; -interface ILog { - content: string; - version: number; -} - -export class InMemoryLogProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { - - readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; - readonly onDidChangeCapabilities: Event = Event.None; +export class InMemoryLogProvider extends KeyValueLogProvider { - private readonly _onDidChangeFile: Emitter = this._register(new Emitter()); - readonly onDidChangeFile: Event = this._onDidChangeFile.event; - - private readonly logs: Map = new Map(); + private readonly logs: Map = new Map(); constructor( ) { - super(); - } - - watch(resource: URI, opts: IWatchOptions): IDisposable { - return Disposable.None; - } - - mkdir(resource: URI): Promise { - return Promise.reject(new Error('Not Supported')); - } - - rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { - return Promise.reject(new Error('Not Supported')); + super(INMEMORY_LOG_SCHEME); } - readdir(resource: URI): Promise<[string, FileType][]> { - return Promise.reject(new Error('Not Supported')); + protected async getAllKeys(): Promise { + return keys(this.logs); } - delete(resource: URI, opts: FileDeleteOptions): Promise { - return Promise.reject(new Error('Not Supported')); + protected async hasKey(key: string): Promise { + return this.logs.has(key); } - async stat(resource: URI): Promise { - const log = this.logs.get(resource.toString()); - return { - ctime: 0, - mtime: log ? log.version : 0, - size: log ? log.content.length : 0, - type: FileType.File - }; + protected async getValue(key: string): Promise { + return this.logs.get(key) || ''; } - async readFile(resource: URI): Promise { - const log = this.logs.get(resource.toString()); - return VSBuffer.fromString(log ? log.content : '').buffer; + protected async setValue(key: string, value: string): Promise { + this.logs.set(key, value); } - async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { - const log = this.logs.get(resource.toString()) || { content: '', version: 0 }; - log.content = VSBuffer.wrap(content).toString(); - log.version = log.version + 1; - this.logs.set(resource.toString(), log); - this._onDidChangeFile.fire([{ resource, type: FileChangeType.UPDATED }]); + protected async deleteKey(key: string): Promise { + this.logs.delete(key); } } From 538ce431560e3f7d91657ca523f25c21e7d69f41 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 01:57:02 +0200 Subject: [PATCH 491/613] disable indexed db log provider --- src/vs/workbench/browser/web.main.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 1b43d77d1182f..6e805aa0a00e8 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -45,7 +45,6 @@ import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { INMEMORY_LOG_SCHEME, InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider'; import { FileLogService } from 'vs/platform/log/common/fileLogService'; import { toLocalISOString } from 'vs/base/common/date'; -import { INDEXEDDB_LOG_SCHEME, IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedDBLogProvider'; class CodeRendererMain extends Disposable { @@ -121,7 +120,7 @@ class CodeRendererMain extends Disposable { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Log - const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: INDEXEDDB_LOG_SCHEME }); + const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: INMEMORY_LOG_SCHEME }); const logService = new BufferLogService(); serviceCollection.set(ILogService, logService); @@ -153,7 +152,6 @@ class CodeRendererMain extends Disposable { // Logger fileService.registerProvider(INMEMORY_LOG_SCHEME, new InMemoryLogProvider()); - fileService.registerProvider(INDEXEDDB_LOG_SCHEME, new IndexedDBLogProvider()); logService.logger = new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService); // Static Extensions From 2d45c514b0b3072bea27319789d821a2b774c864 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 02:01:07 +0200 Subject: [PATCH 492/613] retain last 50 sessions --- .../electron-browser/sharedProcess/contrib/logsDataCleaner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts index 589b87f5131fe..4fb49920390e6 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts @@ -29,7 +29,7 @@ export class LogsDataCleaner extends Disposable { readdir(logsRoot).then(children => { const allSessions = children.filter(name => /^\d{8}T\d{6}$/.test(name)); const oldSessions = allSessions.sort().filter((d, i) => d !== currentLog); - const toDelete = oldSessions.slice(0, Math.max(0, oldSessions.length - 9)); + const toDelete = oldSessions.slice(0, Math.max(0, oldSessions.length - 49)); return Promise.all(toDelete.map(name => rimraf(join(logsRoot, name)))); }).then(null, onUnexpectedError); From f10d3b2776ad7651433173322aecb61f1c2eb851 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 02:08:05 +0200 Subject: [PATCH 493/613] undo change in shared process --- .../electron-browser/sharedProcess/contrib/logsDataCleaner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts index 4fb49920390e6..589b87f5131fe 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner.ts @@ -29,7 +29,7 @@ export class LogsDataCleaner extends Disposable { readdir(logsRoot).then(children => { const allSessions = children.filter(name => /^\d{8}T\d{6}$/.test(name)); const oldSessions = allSessions.sort().filter((d, i) => d !== currentLog); - const toDelete = oldSessions.slice(0, Math.max(0, oldSessions.length - 49)); + const toDelete = oldSessions.slice(0, Math.max(0, oldSessions.length - 9)); return Promise.all(toDelete.map(name => rimraf(join(logsRoot, name)))); }).then(null, onUnexpectedError); From 6f9d54b2bfccf468ff5c47a683155de0cf427a01 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 02:13:35 +0200 Subject: [PATCH 494/613] de dup keys --- .../workbench/services/log/common/keyValueLogProvider.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/log/common/keyValueLogProvider.ts b/src/vs/workbench/services/log/common/keyValueLogProvider.ts index 738e35f354da9..8bcdb579c758b 100644 --- a/src/vs/workbench/services/log/common/keyValueLogProvider.ts +++ b/src/vs/workbench/services/log/common/keyValueLogProvider.ts @@ -10,6 +10,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { VSBuffer } from 'vs/base/common/buffer'; import { FileSystemError } from 'vs/workbench/api/common/extHostTypes'; import { isEqualOrParent, joinPath, relativePath } from 'vs/base/common/resources'; +import { values } from 'vs/base/common/map'; export abstract class KeyValueLogProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { @@ -61,18 +62,18 @@ export abstract class KeyValueLogProvider extends Disposable implements IFileSys return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotADirectory)); } const keys = await this.getAllKeys(); - const files: [string, FileType][] = []; + const files: Map = new Map(); for (const key of keys) { const keyResource = this.toResource(key); if (isEqualOrParent(keyResource, resource, false)) { const path = relativePath(resource, keyResource, false); if (path) { const keySegments = path.split('/'); - files.push([keySegments[0], keySegments.length === 1 ? FileType.File : FileType.Directory]); + files.set(keySegments[0], [keySegments[0], keySegments.length === 1 ? FileType.File : FileType.Directory]); } } } - return files; + return values(files); } async readFile(resource: URI): Promise { From c985e74f295f8980540a70620413922cf21e7ae3 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 02:14:35 +0200 Subject: [PATCH 495/613] enable indexed db log --- src/vs/workbench/browser/web.main.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 6e805aa0a00e8..a5ff2b965a61b 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -42,9 +42,9 @@ import { InMemoryUserDataProvider } from 'vs/workbench/services/userData/common/ import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; -import { INMEMORY_LOG_SCHEME, InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider'; import { FileLogService } from 'vs/platform/log/common/fileLogService'; import { toLocalISOString } from 'vs/base/common/date'; +import { INDEXEDDB_LOG_SCHEME, IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedDBLogProvider'; class CodeRendererMain extends Disposable { @@ -120,7 +120,7 @@ class CodeRendererMain extends Disposable { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Log - const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: INMEMORY_LOG_SCHEME }); + const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: INDEXEDDB_LOG_SCHEME }); const logService = new BufferLogService(); serviceCollection.set(ILogService, logService); @@ -151,7 +151,7 @@ class CodeRendererMain extends Disposable { serviceCollection.set(IFileService, fileService); // Logger - fileService.registerProvider(INMEMORY_LOG_SCHEME, new InMemoryLogProvider()); + fileService.registerProvider(INDEXEDDB_LOG_SCHEME, new IndexedDBLogProvider()); logService.logger = new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService); // Static Extensions From 908ea7786062ace87dd265b70af4864381b6b43f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Aug 2019 17:19:26 -0700 Subject: [PATCH 496/613] Revert "Use per-resource settings for js/ts validate.enable" This reverts commit 8b61c15d3d0a23198e4b8dd7007460a747f2ad9c. --- .../src/features/bufferSyncSupport.ts | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index c99e7b777f946..f662cb21af638 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -310,6 +310,8 @@ export default class BufferSyncSupport extends Disposable { private readonly client: ITypeScriptServiceClient; + private _validateJavaScript: boolean = true; + private _validateTypeScript: boolean = true; private readonly modeIds: Set; private readonly syncedBuffers: SyncedBufferMap; private readonly pendingDiagnostics: PendingDiagnostics; @@ -332,6 +334,9 @@ export default class BufferSyncSupport extends Disposable { this.syncedBuffers = new SyncedBufferMap(pathNormalizer); this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer); this.synchronizer = new BufferSynchronizer(client); + + this.updateConfiguration(); + vscode.workspace.onDidChangeConfiguration(this.updateConfiguration, this, this._disposables); } private readonly _onDelete = this._register(new vscode.EventEmitter()); @@ -506,20 +511,22 @@ export default class BufferSyncSupport extends Disposable { this.pendingDiagnostics.clear(); } + private updateConfiguration() { + const jsConfig = vscode.workspace.getConfiguration('javascript', null); + const tsConfig = vscode.workspace.getConfiguration('typescript', null); + + this._validateJavaScript = jsConfig.get('validate.enable', true); + this._validateTypeScript = tsConfig.get('validate.enable', true); + } + private shouldValidate(buffer: SyncedBuffer) { switch (buffer.kind) { case BufferKind.JavaScript: - { - const config = vscode.workspace.getConfiguration('javascript', buffer.resource); - return config.get('validate.enable', true); - } + return this._validateJavaScript; case BufferKind.TypeScript: default: - { - const config = vscode.workspace.getConfiguration('typescript', buffer.resource); - return config.get('validate.enable', true); - } + return this._validateTypeScript; } } } From 25b5aa72ccde985fec3436c31b269c5da5e9cee0 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 21 Aug 2019 17:21:59 -0700 Subject: [PATCH 497/613] No longer check if default is US. --- .../services/keybinding/test/browserKeyboardMapper.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/keybinding/test/browserKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/browserKeyboardMapper.test.ts index e6f8b9b4d9719..a01c00e3180e8 100644 --- a/src/vs/workbench/services/keybinding/test/browserKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/browserKeyboardMapper.test.ts @@ -40,12 +40,12 @@ suite('keyboard layout loader', () => { let commandService = instantiationService.stub(ICommandService, {}); let instance = new TestKeyboardMapperFactory(notitifcationService, storageService, commandService); - test.skip('load default US keyboard layout', () => { + test('load default US keyboard layout', () => { assert.notEqual(instance.activeKeyboardLayout, null); - assert.equal(instance.activeKeyboardLayout!.isUSStandard, true); }); - test.skip('isKeyMappingActive', () => { + test('isKeyMappingActive', () => { + instance.setUSKeyboardLayout(); assert.equal(instance.isKeyMappingActive({ KeyA: { value: 'a', From 0ec80b402ebe8206dfa03b11fc3661017f6804ad Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 21 Aug 2019 17:38:48 -0700 Subject: [PATCH 498/613] Add tooltip to settings GUI TOC - #79581 --- src/vs/workbench/contrib/preferences/browser/tocTree.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/preferences/browser/tocTree.ts b/src/vs/workbench/contrib/preferences/browser/tocTree.ts index 93a335ee145ec..5c057d629c463 100644 --- a/src/vs/workbench/contrib/preferences/browser/tocTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/tocTree.ts @@ -115,6 +115,7 @@ export class TOCRenderer implements ITreeRenderer Date: Wed, 21 Aug 2019 17:37:09 -0700 Subject: [PATCH 499/613] Ensure no more than MAX_FILES are read when collecting workspace stats, #79456 --- .../diagnostics/node/diagnosticsService.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index b94ff1f9145c5..3408c59c1d7be 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -76,16 +76,26 @@ export function collectWorkspaceStats(folder: string, filter: string[]): Promise return done(results); } + if (token.count > MAX_FILES) { + token.maxReached = true; + return done(results); + } + let pending = files.length; if (pending === 0) { return done(results); } - for (const file of files) { - if (token.maxReached) { - return done(results); - } + let filesToRead = files; + if (token.count + files.length > MAX_FILES) { + token.maxReached = true; + pending = MAX_FILES - token.count; + filesToRead = files.slice(0, pending); + } + + token.count += files.length; + for (const file of filesToRead) { stat(join(dir, file), (err, stats) => { // Ignore files that can't be read if (err) { @@ -108,11 +118,6 @@ export function collectWorkspaceStats(folder: string, filter: string[]): Promise } } } else { - if (token.count >= MAX_FILES) { - token.maxReached = true; - } - - token.count++; results.push(file); if (--pending === 0) { From a96e22dace230b37418503e41a013fa323a8797c Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 21 Aug 2019 17:45:51 -0700 Subject: [PATCH 500/613] More accurate file count for workspace stats --- src/vs/platform/diagnostics/node/diagnosticsService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index 3408c59c1d7be..1f5326fd136b5 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -77,6 +77,7 @@ export function collectWorkspaceStats(folder: string, filter: string[]): Promise } if (token.count > MAX_FILES) { + token.count += files.length; token.maxReached = true; return done(results); } From 4f5ef279908b2c6420bfd432893db0239656d734 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Aug 2019 17:42:39 -0700 Subject: [PATCH 501/613] Pull in latest markdown-it version --- extensions/markdown-language-features/package.json | 2 +- extensions/markdown-language-features/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 7bcf7ca8641ed..19bd9ef8b1a66 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -310,7 +310,7 @@ }, "dependencies": { "highlight.js": "9.15.8", - "markdown-it": "^8.4.2", + "markdown-it": "^9.1.0", "markdown-it-front-matter": "^0.1.2", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0" diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index a1b876e7dc8a8..ef0f5af89e620 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -3900,10 +3900,10 @@ markdown-it-front-matter@^0.1.2: resolved "https://registry.yarnpkg.com/markdown-it-front-matter/-/markdown-it-front-matter-0.1.2.tgz#e50bf56e77e6a4f5ac4ffa894d4d45ccd9896b20" integrity sha1-5Qv1bnfmpPWsT/qJTU1FzNmJayA= -markdown-it@^8.4.2: - version "8.4.2" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54" - integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ== +markdown-it@^9.1.2: + version "9.1.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-9.1.0.tgz#df9601c168568704d554b1fff9af0c5b561168d9" + integrity sha512-xHKG4C8iPriyfu/jc2hsCC045fKrMQ0VexX2F1FGYiRxDxqMB2aAhF8WauJ3fltn2kb90moGBkiiEdooGIg55w== dependencies: argparse "^1.0.7" entities "~1.1.1" From c09ef1a7ed06f3c321a0f43d367bd6079190327f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Aug 2019 17:59:59 -0700 Subject: [PATCH 502/613] Remove refactor/extract from interactive playground Fixes #51575 Don't highlight a feature in the interactive playground that does not work properly --- .../browser/editor/vs_code_editor_walkthrough.md | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md index ffd95c8971832..1f306f79ef216 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md @@ -5,7 +5,6 @@ The core editor in VS Code is packed with features. This page highlights a numb * [IntelliSense](#intellisense) - get code assistance and parameter suggestions for your code and external modules. * [Line Actions](#line-actions) - quickly move lines around to re-order your code. * [Rename Refactoring](#rename-refactoring) - quickly rename symbols across your code base. -* [Refactoring via Extraction](#refactoring-via-extraction) - quickly extract common code into a separate function or constant. * [Formatting](#formatting) - keep your code looking great with inbuilt document & selection formatting. * [Code Folding](#code-folding) - focus on the most relevant parts of your code by folding other areas. * [Errors and Warnings](#errors-and-warnings) - see errors and warning as you type. @@ -90,21 +89,6 @@ function Book(title, author) { > **JSDoc Tip:** VS Code's IntelliSense uses JSDoc comments to provide richer suggestions. The types and documentation from JSDoc comments show up when you hover over a reference to `Book` or in IntelliSense when you create a new instance of `Book`. -### Refactoring via Extraction -Sometimes you want to refactor already written code into a separate function or constant to reuse it later. Select the lines you want to refactor out and press kb(editor.action.quickFix) or click the little light bulb and choose one of the respective `Extract to...` options. Try it by selecting the code inside the `if`-clause on line 3 or any other common code you want to refactor out. - -```js -function findFirstEvenNumber(arr) { - for (const el of arr) { - if (typeof el === 'number' && el % 2 === 0) { - return el; - } - } - return null; -} -``` - - ### Formatting Keeping your code looking great is hard without a good formatter. Luckily it's easy to format content, either for the entire document with kb(editor.action.formatDocument) or for the current selection with kb(editor.action.formatSelection). Both of these options are also available through the right-click context menu. From 96fb1b87ea143cf8a1dc441a1f78d0ae8a951f25 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Aug 2019 18:15:27 -0700 Subject: [PATCH 503/613] Use more explicit variable name --- .../contrib/parameterHints/parameterHintsModel.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts b/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts index c34c68a84a21b..21cf0393170ea 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts @@ -52,7 +52,7 @@ export class ParameterHintsModel extends Disposable { public readonly onChangedHints = this._onChangedHints.event; private readonly editor: ICodeEditor; - private enabled: boolean; + private triggerOnType = false; private _state: ParameterHintState.State = ParameterHintState.Default; private readonly _lastSignatureHelpResult = this._register(new MutableDisposable()); private triggerChars = new CharacterSet(); @@ -68,7 +68,6 @@ export class ParameterHintsModel extends Disposable { super(); this.editor = editor; - this.enabled = false; this.throttledDelayer = new Delayer(delay); @@ -242,7 +241,7 @@ export class ParameterHintsModel extends Disposable { } private onDidType(text: string) { - if (!this.enabled) { + if (!this.triggerOnType) { return; } @@ -272,9 +271,9 @@ export class ParameterHintsModel extends Disposable { } private onEditorConfigurationChange(): void { - this.enabled = this.editor.getConfiguration().contribInfo.parameterHints.enabled; + this.triggerOnType = this.editor.getConfiguration().contribInfo.parameterHints.enabled; - if (!this.enabled) { + if (!this.triggerOnType) { this.cancel(); } } @@ -283,4 +282,4 @@ export class ParameterHintsModel extends Disposable { this.cancel(true); super.dispose(); } -} \ No newline at end of file +} From 962158e164dbd151345bb7fba1265a6818e4ae3b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Aug 2019 18:15:49 -0700 Subject: [PATCH 504/613] Fix yarn lock --- extensions/markdown-language-features/yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index ef0f5af89e620..a45f00885e8bd 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -3900,7 +3900,7 @@ markdown-it-front-matter@^0.1.2: resolved "https://registry.yarnpkg.com/markdown-it-front-matter/-/markdown-it-front-matter-0.1.2.tgz#e50bf56e77e6a4f5ac4ffa894d4d45ccd9896b20" integrity sha1-5Qv1bnfmpPWsT/qJTU1FzNmJayA= -markdown-it@^9.1.2: +markdown-it@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-9.1.0.tgz#df9601c168568704d554b1fff9af0c5b561168d9" integrity sha512-xHKG4C8iPriyfu/jc2hsCC045fKrMQ0VexX2F1FGYiRxDxqMB2aAhF8WauJ3fltn2kb90moGBkiiEdooGIg55w== From e558dc6ea73a75bd69d7a0b485f0e7e4194c66bf Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 21 Aug 2019 18:20:31 -0700 Subject: [PATCH 505/613] Support multiline find widget in monaco. --- src/vs/base/browser/ui/inputbox/inputBox.css | 2 ++ src/vs/editor/contrib/find/findWidget.css | 5 +++++ src/vs/editor/contrib/find/findWidget.ts | 13 ++++++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/inputbox/inputBox.css b/src/vs/base/browser/ui/inputbox/inputBox.css index dc1240dfbc57a..d13e1f2e53595 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.css +++ b/src/vs/base/browser/ui/inputbox/inputBox.css @@ -60,6 +60,8 @@ display: block; -ms-overflow-style: none; /* IE 10+ */ overflow: -moz-scrollbars-none; /* Firefox */ + scrollbar-width: none; /* Firefox ^64 */ + outline: none; } .monaco-inputbox > .wrapper > textarea.input::-webkit-scrollbar { diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index d6c6f602e484a..693157f51f60a 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -71,6 +71,11 @@ bottom: unset; } +.monaco-editor .find-widget .monaco-inputbox.synthetic-focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; +} + .monaco-editor .find-widget .monaco-inputbox .input { background-color: transparent; /* Style to compensate for //winjs */ diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 2ea8ba03dd73b..2bfbe8388d3e1 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -29,7 +29,7 @@ import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_REPLACE_INPUT_FOCUSED, FIND_IDS, MA import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry'; +import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; @@ -894,8 +894,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } private _buildDomNode(): void { - const flexibleHeight = platform.isNative; - const flexibleWidth = platform.isNative; + const flexibleHeight = true; + const flexibleWidth = true; // Find input this._findInput = this._register(new ContextScopedFindInput(null, this._contextViewProvider, { width: FIND_INPUT_AREA_WIDTH, @@ -1402,4 +1402,11 @@ registerThemingParticipant((theme, collector) => { if (inputActiveBackground) { collector.addRule(`.monaco-editor .find-widget .monaco-checkbox .checkbox:checked + .label { background-color: ${inputActiveBackground.toString()}; }`); } + + // This rule is used to override the outline color for synthetic-focus find input. + const focusOutline = theme.getColor(focusBorder); + if (focusOutline) { + collector.addRule(`.monaco-workbench .monaco-editor .find-widget .monaco-inputbox.synthetic-focus { outline-color: ${focusOutline}; }`); + + } }); From 9b4d07f36ecc6d36acd19b212a22cbd746d01925 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 21 Aug 2019 19:10:06 -0700 Subject: [PATCH 506/613] Update find keybindings for all platforms --- .../contrib/terminal/browser/terminal.contribution.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index b47f264dcb903..a953b60f18f9e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -530,7 +530,8 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNe }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find next', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID, FindNext.LABEL, { primary: KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3, KeyMod.Shift | KeyCode.Enter] } + secondary: [KeyMod.Shift | KeyCode.Enter], + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } }, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find next'); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID_TERMINAL_FOCUS, FindPrevious.LABEL, { primary: KeyMod.Shift | KeyCode.F3, @@ -538,7 +539,8 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, Fi }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find previous', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, { primary: KeyMod.Shift | KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3, KeyCode.Enter] }, + secondary: [KeyCode.Enter], + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, }, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find previous'); From 08d59c432609ae9306eb3889815977e93bb548de Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 21 Aug 2019 19:13:42 -0700 Subject: [PATCH 507/613] Platform specific kb overrides default primary and secondary. --- .../contrib/terminal/browser/terminal.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index a953b60f18f9e..73a8047c94026 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -531,7 +531,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNe actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID, FindNext.LABEL, { primary: KeyCode.F3, secondary: [KeyMod.Shift | KeyCode.Enter], - mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3, KeyMod.Shift | KeyCode.Enter] } }, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find next'); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID_TERMINAL_FOCUS, FindPrevious.LABEL, { primary: KeyMod.Shift | KeyCode.F3, @@ -540,7 +540,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, Fi actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, { primary: KeyMod.Shift | KeyCode.F3, secondary: [KeyCode.Enter], - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3, KeyCode.Enter] }, }, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find previous'); From 7d7f8128fdbf9a3cd4c06b2ecec60c881ceb458e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 07:46:10 +0200 Subject: [PATCH 508/613] Update exploration-build.yml for Azure Pipelines --- build/azure-pipelines/exploration-build.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index 1fbc9eaa9cc4d..0432f6a80d3a9 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -10,20 +10,10 @@ steps: inputs: versionSpec: "10.15.1" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode - - script: | set -e cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF git config user.email "vscode@microsoft.com" git config user.name "VSCode" From 7125817376817d861b719619920f3b4ba0d2dca0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 07:54:07 +0200 Subject: [PATCH 509/613] build - add pool --- build/azure-pipelines/distro-build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index 62ee67ad1c6d9..74fddcc55a84b 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -1,3 +1,6 @@ +pool: + vmImage: 'Ubuntu-16.04' + trigger: branches: include: ['master', 'release/*'] From 88e3bed9f082b5703e03d3af97a382691e24bb67 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 07:55:45 +0200 Subject: [PATCH 510/613] Update exploration-build.yml for Azure Pipelines --- build/azure-pipelines/exploration-build.yml | 32 ++++++++++++++++----- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index 0432f6a80d3a9..74fddcc55a84b 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -1,27 +1,45 @@ +pool: + vmImage: 'Ubuntu-16.04' + trigger: branches: - include: ['master'] + include: ['master', 'release/*'] pr: branches: - include: ['master'] + include: ['master', 'release/*'] steps: - task: NodeTool@0 inputs: versionSpec: "10.15.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - script: | set -e cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF git config user.email "vscode@microsoft.com" git config user.name "VSCode" - git checkout origin/ben/electron-test - git merge origin/master + git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git" + git fetch distro + + # Push master branch into oss/master + git push distro origin/master:refs/heads/oss/master + + # Push every release branch into oss/release + git for-each-ref --format="%(refname:short)" refs/remotes/origin/release/* | sed 's/^origin\/\(.*\)$/\0:refs\/heads\/oss\/\1/' | xargs git push distro - # Push master branch into exploration branch - git push origin HEAD:ben/electron-test + git merge $(node -p "require('./package.json').distro") - displayName: Sync & Merge Exploration + displayName: Sync & Merge Distro From b2332819670f001f6d5f0b291cba64443dfe3529 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 07:56:52 +0200 Subject: [PATCH 511/613] Revert "Update exploration-build.yml for Azure Pipelines" This reverts commit 88e3bed9f082b5703e03d3af97a382691e24bb67. --- build/azure-pipelines/exploration-build.yml | 32 +++++---------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index 74fddcc55a84b..0432f6a80d3a9 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -1,45 +1,27 @@ -pool: - vmImage: 'Ubuntu-16.04' - trigger: branches: - include: ['master', 'release/*'] + include: ['master'] pr: branches: - include: ['master', 'release/*'] + include: ['master'] steps: - task: NodeTool@0 inputs: versionSpec: "10.15.1" -- task: AzureKeyVault@1 - displayName: 'Azure Key Vault: Get Secrets' - inputs: - azureSubscription: 'vscode-builds-subscription' - KeyVaultName: vscode - - script: | set -e cat << EOF > ~/.netrc - machine github.com - login vscode - password $(github-distro-mixin-password) - EOF git config user.email "vscode@microsoft.com" git config user.name "VSCode" - git remote add distro "https://github.com/$VSCODE_MIXIN_REPO.git" - git fetch distro - - # Push master branch into oss/master - git push distro origin/master:refs/heads/oss/master - - # Push every release branch into oss/release - git for-each-ref --format="%(refname:short)" refs/remotes/origin/release/* | sed 's/^origin\/\(.*\)$/\0:refs\/heads\/oss\/\1/' | xargs git push distro + git checkout origin/ben/electron-test + git merge origin/master - git merge $(node -p "require('./package.json').distro") + # Push master branch into exploration branch + git push origin HEAD:ben/electron-test - displayName: Sync & Merge Distro + displayName: Sync & Merge Exploration From 70587062f690c2572e67394cf5689b7b941e13ae Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 07:57:23 +0200 Subject: [PATCH 512/613] jc --- build/azure-pipelines/exploration-build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index 0432f6a80d3a9..797c4b5fce028 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -1,3 +1,6 @@ +pool: + vmImage: 'Ubuntu-16.04' + trigger: branches: include: ['master'] From de04dc374fb4bd7ec3f8dda62de3b1a5faed4ba2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 08:05:22 +0200 Subject: [PATCH 513/613] tslint - move rules --- build/lib/tslint/{noDOMGlobalsRule.js => noDomGlobalsRule.js} | 0 build/lib/tslint/{noDOMGlobalsRule.ts => noDomGlobalsRule.ts} | 0 .../lib/tslint/{noNodeJSGlobalsRule.js => noNodejsGlobalsRule.js} | 0 .../lib/tslint/{noNodeJSGlobalsRule.ts => noNodejsGlobalsRule.ts} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename build/lib/tslint/{noDOMGlobalsRule.js => noDomGlobalsRule.js} (100%) rename build/lib/tslint/{noDOMGlobalsRule.ts => noDomGlobalsRule.ts} (100%) rename build/lib/tslint/{noNodeJSGlobalsRule.js => noNodejsGlobalsRule.js} (100%) rename build/lib/tslint/{noNodeJSGlobalsRule.ts => noNodejsGlobalsRule.ts} (100%) diff --git a/build/lib/tslint/noDOMGlobalsRule.js b/build/lib/tslint/noDomGlobalsRule.js similarity index 100% rename from build/lib/tslint/noDOMGlobalsRule.js rename to build/lib/tslint/noDomGlobalsRule.js diff --git a/build/lib/tslint/noDOMGlobalsRule.ts b/build/lib/tslint/noDomGlobalsRule.ts similarity index 100% rename from build/lib/tslint/noDOMGlobalsRule.ts rename to build/lib/tslint/noDomGlobalsRule.ts diff --git a/build/lib/tslint/noNodeJSGlobalsRule.js b/build/lib/tslint/noNodejsGlobalsRule.js similarity index 100% rename from build/lib/tslint/noNodeJSGlobalsRule.js rename to build/lib/tslint/noNodejsGlobalsRule.js diff --git a/build/lib/tslint/noNodeJSGlobalsRule.ts b/build/lib/tslint/noNodejsGlobalsRule.ts similarity index 100% rename from build/lib/tslint/noNodeJSGlobalsRule.ts rename to build/lib/tslint/noNodejsGlobalsRule.ts From d98eb32b6106caf7528d814084d715cab6a2cf9c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 08:13:42 +0200 Subject: [PATCH 514/613] build - touch scheduling rule in the hope it gets installed again --- build/azure-pipelines/product-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index f38f431182644..a2fdc9e4ec684 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -143,8 +143,8 @@ trigger: none pr: none schedules: -- cron: "0 5 * * Mon-Fri" - displayName: Mon-Fri at 7:00 +- cron: "10 5 * * Mon-Fri" + displayName: Mon-Fri at 7:10 branches: include: - master From fa1fb02b11e3b07c65ae86890e098d8e7f58ca0b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 08:27:05 +0200 Subject: [PATCH 515/613] fix #79454 --- build/gulpfile.hygiene.js | 2 +- src/vs/base/test/node/keytar.test.ts | 2 +- src/vs/workbench/browser/web.simpleservices.ts | 1 - src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts | 4 ++++ src/vs/workbench/contrib/tasks/common/tasks.ts | 1 + src/vs/workbench/contrib/terminal/common/terminal.ts | 4 ++++ src/vs/workbench/contrib/terminal/common/terminalService.ts | 2 ++ .../services/keybinding/browser/keybindingService.ts | 1 - 8 files changed, 13 insertions(+), 4 deletions(-) diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index c065556dc0920..aaedf0a7bc82d 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -199,7 +199,7 @@ gulp.task('tslint', () => { .pipe(filter(tslintExtensionsFilter)) .pipe(gulptslint.default({ rulesDirectory: 'build/lib/tslint' })) .pipe(gulptslint.default.report({ emitError: true })) - ]); + ]).pipe(es.through()); }); function hygiene(some) { diff --git a/src/vs/base/test/node/keytar.test.ts b/src/vs/base/test/node/keytar.test.ts index 141e6ab904fa7..96ca335e940c9 100644 --- a/src/vs/base/test/node/keytar.test.ts +++ b/src/vs/base/test/node/keytar.test.ts @@ -32,4 +32,4 @@ suite('Keytar', () => { } })().then(done, done); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 73f112884c860..93708d0bc405b 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -20,7 +20,6 @@ import { IRecentlyOpened, IRecent, isRecentFile, isRecentFolder } from 'vs/platf import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; -// tslint:disable-next-line: import-patterns import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { addDisposableListener, EventType, windowOpenNoOpener } from 'vs/base/browser/dom'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index fb838d2e49e8a..96148b4e58fca 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -339,6 +339,7 @@ export class TerminalTaskSystem implements ITaskSystem { private async executeTask(task: Task, resolver: ITaskResolver, trigger: string): Promise { let promises: Promise[] = []; if (task.configurationProperties.dependsOn) { + // tslint:disable-next-line: no-for-in-array for (let index in task.configurationProperties.dependsOn) { const dependency = task.configurationProperties.dependsOn[index]; let dependencyTask = resolver.resolve(dependency.workspaceFolder, dependency.task!); @@ -1375,6 +1376,7 @@ export class TerminalTaskSystem implements ITaskSystem { return command; } if (cwd === undefined) { + // tslint:disable-next-line: no-nodejs-globals cwd = process.cwd(); } const dir = path.dirname(command); @@ -1383,7 +1385,9 @@ export class TerminalTaskSystem implements ITaskSystem { // to the current working directory. return path.join(cwd, command); } + // tslint:disable-next-line: no-nodejs-globals if (paths === undefined && Types.isString(process.env.PATH)) { + // tslint:disable-next-line: no-nodejs-globals paths = process.env.PATH.split(path.delimiter); } // No PATH environment. Make path absolute to the cwd. diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index d1d4cc32eb494..49d370e71e023 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -985,6 +985,7 @@ export namespace KeyedTaskIdentifier { function sortedStringify(literal: any): string { const keys = Object.keys(literal).sort(); let result: string = ''; + // tslint:disable-next-line: no-for-in-array for (let position in keys) { let stringified = literal[keys[position]]; if (stringified instanceof Object) { diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 48698da20ee6e..9d582c1ad6782 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -248,6 +248,7 @@ export interface ITerminalService { /** * Creates a raw terminal instance, this should not be used outside of the terminal part. */ + // tslint:disable-next-line: no-dom-globals createInstance(container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance; getInstanceFromId(terminalId: number): ITerminalInstance | undefined; getInstanceFromIndex(terminalIndex: number): ITerminalInstance; @@ -279,6 +280,7 @@ export interface ITerminalService { selectDefaultWindowsShell(): Promise; + // tslint:disable-next-line: no-dom-globals setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; manageWorkspaceShellPermissions(): void; @@ -337,6 +339,7 @@ export interface ITerminalTab { focusNextPane(): void; resizePane(direction: Direction): void; setActiveInstanceByIndex(index: number): void; + // tslint:disable-next-line: no-dom-globals attachToElement(element: HTMLElement): void; setVisible(visible: boolean): void; layout(width: number, height: number): void; @@ -611,6 +614,7 @@ export interface ITerminalInstance { * * @param container The element to attach the terminal instance to. */ + // tslint:disable-next-line: no-dom-globals attachToElement(container: HTMLElement): void; /** diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index 1fbdcb6237b84..74d2b5728dc62 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -122,7 +122,9 @@ export abstract class TerminalService implements ITerminalService { protected abstract _showBackgroundTerminal(instance: ITerminalInstance): void; public abstract createTerminal(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance; + // tslint:disable-next-line: no-dom-globals public abstract createInstance(container: HTMLElement, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance; + // tslint:disable-next-line: no-dom-globals public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; public getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance { diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index b90651c4f3362..8fe901128a923 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -34,7 +34,6 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { MenuRegistry } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -// tslint:disable-next-line: import-patterns import { commandsExtensionPoint } from 'vs/workbench/api/common/menusExtensionPoint'; import { Disposable } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; From ee2b890bcc2a292c5c8b71842043add26f59c38e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 22 Aug 2019 09:35:41 +0200 Subject: [PATCH 516/613] kindModifier is a Set, #23927 --- src/vs/editor/common/modes.ts | 2 +- src/vs/editor/contrib/suggest/suggestWidget.ts | 2 +- src/vs/monaco.d.ts | 2 +- src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 4689f47200137..78610e6c0040e 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -404,7 +404,7 @@ export interface CompletionItem { * A modifier to the `kind` which affect how the item * is rendered, e.g. Deprecated is rendered with a strikeout */ - kindModifier?: CompletionItemKindModifier; + kindModifier?: Set; /** * A human-readable string with additional information * about this item, like type or symbol information. diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index b5731357b0e04..721239842565a 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -193,7 +193,7 @@ class Renderer implements IListRenderer ]; } - if (suggestion.kindModifier && suggestion.kindModifier & CompletionItemKindModifier.Deprecated) { + if (suggestion.kindModifier && suggestion.kindModifier.has(CompletionItemKindModifier.Deprecated)) { labelOptions.extraClasses = (labelOptions.extraClasses || []).concat(['suggest-widget-deprecated']); } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 545a3d799c3f3..5cab18f9cffd7 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4822,7 +4822,7 @@ declare namespace monaco.languages { * A modifier to the `kind` which affect how the item * is rendered, e.g. Deprecated is rendered with a strikeout */ - kindModifier?: CompletionItemKindModifier; + kindModifier?: Set; /** * A human-readable string with additional information * about this item, like type or symbol information. diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 769df9337d9c2..638616a3674d6 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -330,7 +330,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha return { label: data.a, kind: data.b, - kindModifier: data.n ? modes.CompletionItemKindModifier.Deprecated : undefined, + kindModifier: data.n ? new Set().add(modes.CompletionItemKindModifier.Deprecated) : undefined, detail: data.c, documentation: data.d, sortText: data.e, From 1848b163138015ef9e88999773f1fdc894549de2 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 22 Aug 2019 10:28:55 +0200 Subject: [PATCH 517/613] tweak UI for deprecated completions, update API proposal #23927 --- src/vs/base/common/map.ts | 9 +++++++++ .../editor/contrib/suggest/media/suggest.css | 19 +++++++++++-------- .../contrib/suggest/suggestController.ts | 5 +++-- .../editor/contrib/suggest/suggestWidget.ts | 3 ++- src/vs/vscode.proposed.d.ts | 11 ++++++++--- .../api/browser/mainThreadLanguageFeatures.ts | 3 ++- .../workbench/api/common/extHost.api.impl.ts | 1 + .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostLanguageFeatures.ts | 7 ++++++- .../api/common/extHostTypeConverters.ts | 15 +++++++++++++++ src/vs/workbench/api/common/extHostTypes.ts | 5 +++++ 11 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index f1cce9af4ceb1..eee70e8268410 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -7,6 +7,15 @@ import { URI } from 'vs/base/common/uri'; import { CharCode } from 'vs/base/common/charCode'; import { Iterator, IteratorResult, FIN } from './iterator'; + +export function fromArray(array: readonly T[]): Set { + const result = new Set(); + for (const element of array) { + result.add(element); + } + return result; +} + export function values(set: Set): V[]; export function values(map: Map): V[]; export function values(forEachable: { forEach(callback: (value: V, ...more: any[]) => any): void }): V[] { diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index d01d1b6bb5bcc..12cb09f4acdf8 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -97,10 +97,6 @@ font-weight: bold; } -.monaco-editor .suggest-widget-deprecated span { - text-decoration: line-through; -} - /** Icon styles **/ .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close, @@ -115,8 +111,8 @@ .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close { background-image: url('./close-light.svg'); position: absolute; - top: 0px; - right: 0px; + top: 0; + right: 0; margin-right: 5px; } @@ -159,9 +155,16 @@ } /** Styles for each row in the list **/ + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated { + opacity: 0.66; +} +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated > .monaco-icon-label-description-container { + text-decoration: line-through; +} + .monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label::before { height: 100%; - } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .icon { @@ -257,7 +260,7 @@ text-overflow: ellipsis; opacity: 0.7; word-break: break-all; - margin: 0px 24px 0 0; + margin: 0 24px 0 0; padding: 4px 0 12px 5px; } diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 958cff7b5340a..7fd1f0f84977f 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -34,6 +34,8 @@ import { IdleValue } from 'vs/base/common/async'; import { isObject } from 'vs/base/common/types'; import { CommitCharacterController } from './suggestCommitCharacters'; +const _sticky = false; // for development purposes only + export class SuggestController implements IEditorContribution { private static readonly ID: string = 'editor.contrib.suggestController'; @@ -47,7 +49,6 @@ export class SuggestController implements IEditorContribution { private readonly _alternatives: IdleValue; private readonly _toDispose = new DisposableStore(); - private readonly _sticky = false; // for development purposes only constructor( private _editor: ICodeEditor, @@ -127,7 +128,7 @@ export class SuggestController implements IEditorContribution { } })); this._toDispose.add(this._editor.onDidBlurEditorWidget(() => { - if (!this._sticky) { + if (!_sticky) { this._model.cancel(); this._model.clear(); } diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 721239842565a..cdad5fdb234b6 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -194,7 +194,8 @@ class Renderer implements IListRenderer } if (suggestion.kindModifier && suggestion.kindModifier.has(CompletionItemKindModifier.Deprecated)) { - labelOptions.extraClasses = (labelOptions.extraClasses || []).concat(['suggest-widget-deprecated']); + labelOptions.extraClasses = (labelOptions.extraClasses || []).concat(['deprecated']); + labelOptions.matches = []; } data.iconLabel.setLabel(suggestion.label, undefined, labelOptions); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 1628e28fb27a9..30f5775ce516f 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1142,13 +1142,18 @@ declare module 'vscode' { //#endregion - //#region Deprecated support + //#region Joh - CompletionItemKindModifier, https://github.com/microsoft/vscode/issues/23927 + + export enum CompletionItemKindModifier { + Deprecated = 1 + } export interface CompletionItem { + /** - * Indicates if this item is deprecated. + * */ - deprecated?: boolean; + kind2?: CompletionItemKind | { base: CompletionItemKind, modifier: ReadonlyArray }; } //#endregion diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 638616a3674d6..0feaf4b35993d 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -21,6 +21,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { mixin } from 'vs/base/common/objects'; +import { fromArray } from 'vs/base/common/map'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape { @@ -330,7 +331,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha return { label: data.a, kind: data.b, - kindModifier: data.n ? new Set().add(modes.CompletionItemKindModifier.Deprecated) : undefined, + kindModifier: data.n && fromArray(data.n), detail: data.c, documentation: data.d, sortText: data.e, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 5370f6a01babb..755d23b1d4f8e 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -803,6 +803,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I CommentMode: extHostTypes.CommentMode, CompletionItem: extHostTypes.CompletionItem, CompletionItemKind: extHostTypes.CompletionItemKind, + CompletionItemKindModifier: extHostTypes.CompletionItemKindModifier, CompletionList: extHostTypes.CompletionList, CompletionTriggerKind: extHostTypes.CompletionTriggerKind, ConfigurationTarget: extHostTypes.ConfigurationTarget, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index f7f61cf6255b8..9e9abe2e83435 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -934,7 +934,7 @@ export interface ISuggestDataDto { k/* commitCharacters */?: string[]; l/* additionalTextEdits */?: ISingleEditOperation[]; m/* command */?: modes.Command; - n/* deprecated */?: boolean; + n/* kindModifier */?: modes.CompletionItemKindModifier[]; // not-standard x?: ChainedCacheId; } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 0f0afd3b5874b..3954141c90aeb 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -736,9 +736,14 @@ class SuggestAdapter { k: item.commitCharacters, l: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from), m: this._commands.toInternal(item.command, disposables), - n: item.deprecated }; + // kind2 + if (typeof item.kind2 === 'object') { + result.b = typeConvert.CompletionItemKind.from(item.kind2.base); + result.n = item.kind2.modifier.map(typeConvert.CompletionItemKindModifier.from); + } + // 'insertText'-logic if (item.textEdit) { result.h = item.textEdit.newText; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 0fb461e8c977e..1fdc6fef86fa1 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -681,6 +681,21 @@ export namespace CompletionContext { } } +export namespace CompletionItemKindModifier { + + export function from(kind: types.CompletionItemKindModifier): modes.CompletionItemKindModifier { + switch (kind) { + case types.CompletionItemKindModifier.Deprecated: return modes.CompletionItemKindModifier.Deprecated; + } + } + + export function to(kind: modes.CompletionItemKindModifier): types.CompletionItemKindModifier { + switch (kind) { + case modes.CompletionItemKindModifier.Deprecated: return types.CompletionItemKindModifier.Deprecated; + } + } +} + export namespace CompletionItemKind { export function from(kind: types.CompletionItemKind | undefined): modes.CompletionItemKind { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 9084c6170571f..53031ca7ca117 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1308,11 +1308,16 @@ export enum CompletionItemKind { TypeParameter = 24 } +export enum CompletionItemKindModifier { + Deprecated = 1, +} + @es5ClassCompat export class CompletionItem implements vscode.CompletionItem { label: string; kind?: CompletionItemKind; + kind2?: CompletionItemKind | { base: CompletionItemKind, modifier: CompletionItemKindModifier[] }; detail?: string; documentation?: string | MarkdownString; sortText?: string; From df8499dfa16bc1d0cf38e872e663b90ba383c6b0 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 22 Aug 2019 10:29:11 +0200 Subject: [PATCH 518/613] Add @types/cookie --- package.json | 1 + yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/package.json b/package.json index 47d7fb2d519d1..678c8a3d60770 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ }, "devDependencies": { "7zip": "0.0.6", + "@types/cookie": "^0.3.3", "@types/keytar": "^4.4.0", "@types/mocha": "2.2.39", "@types/node": "^10.12.12", diff --git a/yarn.lock b/yarn.lock index a7c03232237c9..23c3a22021aab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -165,6 +165,11 @@ dependencies: commander "*" +"@types/cookie@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" + integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow== + "@types/fancy-log@1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@types/fancy-log/-/fancy-log-1.3.0.tgz#a61ab476e5e628cd07a846330df53b85e05c8ce0" From 728bf2e2bcf584e3b8bc4130abd3ad3ae2e77615 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 22 Aug 2019 10:41:57 +0200 Subject: [PATCH 519/613] fixes #79608 --- src/vs/workbench/browser/layout.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 357a3488de125..92ef4ff66f8c3 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -617,10 +617,13 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // To properly reset line numbers we need to read the configuration for each editor respecting it's uri. if (!lineNumbers && isCodeEditor(editor) && editor.hasModel()) { const model = editor.getModel(); - this.configurationService.getValue('editor.lineNumbers', { resource: model.uri }); - } else { - editor.updateOptions({ lineNumbers }); + lineNumbers = this.configurationService.getValue('editor.lineNumbers', { resource: model.uri }); + } + if (!lineNumbers) { + lineNumbers = this.configurationService.getValue('editor.lineNumbers'); } + + editor.updateOptions({ lineNumbers }); }); // Check if zen mode transitioned to full screen and if now we are out of zen mode @@ -1292,4 +1295,3 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.disposed = true; } } - From 284c04475e7dbad20d3bbfc6f96fead7f99564e0 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 22 Aug 2019 10:42:19 +0200 Subject: [PATCH 520/613] Revert "chore: upgrade native-watchdog@1.1.0" This reverts commit 74ddff2d2ee83044be42ec881ddcf3c0a9b1f3c3. --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 678c8a3d60770..9cf53d386d6f1 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "keytar": "^4.11.0", "native-is-elevated": "0.3.0", "native-keymap": "2.0.0", - "native-watchdog": "1.1.0", + "native-watchdog": "1.0.0", "node-pty": "0.9.0-beta19", "nsfw": "1.2.5", "onigasm-umd": "^2.2.2", diff --git a/yarn.lock b/yarn.lock index 23c3a22021aab..90baeda9809a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6065,10 +6065,10 @@ native-keymap@2.0.0: resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-2.0.0.tgz#7491ba8f9cc75bd6ada7e754dadb7716c793a3e3" integrity sha512-KIlDZp0yKaHaGIkEVdlYN3QIaZICXwG1qh/oeBeQdM8TwAi90IAZlAD57qsNDkEvIJIzerCzb5jYYQAdHGBgYg== -native-watchdog@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.1.0.tgz#aea2a4bc7ddd69b774682ae7705d91fa5ac0dcb6" - integrity sha512-cMJryYPR+8rtCqU3yAfGS3sCO6uA+fsrSv9MgBG8JE2cu4hwOWBhvR44IhUeJsGrwrNkgbeok9RYmyb/5LttSQ== +native-watchdog@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.0.0.tgz#97344e83cd6815a8c8e6c44a52e7be05832e65ca" + integrity sha512-HKQATz5KLUMPyQQ/QaalzgTXaGz2plYPBxjyalaR4ECIu/UznXY8YJD+a9SLkkcvtxnJ8/zHLY3xik06vUZ7uA== natural-compare@^1.4.0: version "1.4.0" From 719cdaa87cc477e9c4808583dce4849479814ccf Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 10:41:30 +0200 Subject: [PATCH 521/613] web - implement callback auth flow --- src/vs/code/electron-main/app.ts | 2 +- src/vs/platform/url/common/url.ts | 15 +- src/vs/platform/url/common/urlService.ts | 10 +- src/vs/platform/url/node/urlIpc.ts | 6 +- src/vs/platform/url/node/urlService.ts | 17 ++ src/vs/vscode.proposed.d.ts | 46 +++++ .../workbench/api/browser/mainThreadUrls.ts | 12 +- .../workbench/api/common/extHost.api.impl.ts | 4 + .../workbench/api/common/extHost.protocol.ts | 1 + src/vs/workbench/api/common/extHostUrls.ts | 4 + .../workbench/browser/web.simpleservices.ts | 46 +---- .../experimentService.test.ts | 2 +- .../extensionsActions.test.ts | 2 +- .../extensionsTipsService.test.ts | 2 +- .../electron-browser/extensionsViews.test.ts | 2 +- .../extensionsWorkbenchService.test.ts | 2 +- .../services/url/browser/urlService.ts | 177 ++++++++++++++++++ .../url/electron-browser/urlService.ts | 5 +- src/vs/workbench/workbench.common.main.ts | 1 + src/vs/workbench/workbench.desktop.main.ts | 5 +- src/vs/workbench/workbench.web.api.ts | 6 + src/vs/workbench/workbench.web.main.ts | 1 + 22 files changed, 303 insertions(+), 65 deletions(-) create mode 100644 src/vs/platform/url/node/urlService.ts create mode 100644 src/vs/workbench/services/url/browser/urlService.ts rename src/vs/{platform => workbench/services}/url/electron-browser/urlService.ts (89%) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 6d43b392dfb5c..66c0baf040184 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -70,7 +70,7 @@ import { startsWith } from 'vs/base/common/strings'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; import { IBackupMainService } from 'vs/platform/backup/common/backup'; import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { statSync } from 'fs'; import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc'; diff --git a/src/vs/platform/url/common/url.ts b/src/vs/platform/url/common/url.ts index f73971c4e00bc..87a5a8fbeecc2 100644 --- a/src/vs/platform/url/common/url.ts +++ b/src/vs/platform/url/common/url.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable } from 'vs/base/common/lifecycle'; export const IURLService = createDecorator('urlService'); @@ -14,8 +14,17 @@ export interface IURLHandler { } export interface IURLService { - _serviceBrand: any; + + _serviceBrand: ServiceIdentifier; + + /** + * Create a URL that can be called to trigger IURLhandlers. + * The URL that gets passed to the IURLHandlers carries over + * any of the provided IURLCreateOption values. + */ + create(options?: Partial): URI; open(url: URI): Promise; + registerHandler(handler: IURLHandler): IDisposable; } diff --git a/src/vs/platform/url/common/urlService.ts b/src/vs/platform/url/common/urlService.ts index 66d4abd6722a5..eb216a6b33555 100644 --- a/src/vs/platform/url/common/urlService.ts +++ b/src/vs/platform/url/common/urlService.ts @@ -4,18 +4,20 @@ *--------------------------------------------------------------------------------------------*/ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; -import { URI } from 'vs/base/common/uri'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { first } from 'vs/base/common/async'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { values } from 'vs/base/common/map'; +import { first } from 'vs/base/common/async'; +import { toDisposable, IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -export class URLService implements IURLService { +export abstract class AbstractURLService extends Disposable implements IURLService { _serviceBrand!: ServiceIdentifier; private handlers = new Set(); + abstract create(options?: Partial): URI; + open(uri: URI): Promise { const handlers = values(this.handlers); return first(handlers.map(h => () => h.handleURL(uri)), undefined, false).then(val => val || false); diff --git a/src/vs/platform/url/node/urlIpc.ts b/src/vs/platform/url/node/urlIpc.ts index 2ced486d02d08..0e70e9c6534a7 100644 --- a/src/vs/platform/url/node/urlIpc.ts +++ b/src/vs/platform/url/node/urlIpc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; @@ -39,6 +39,10 @@ export class URLServiceChannelClient implements IURLService { registerHandler(handler: IURLHandler): IDisposable { throw new Error('Not implemented.'); } + + create(_options?: Partial): URI { + throw new Error('Method not implemented.'); + } } export class URLHandlerChannel implements IServerChannel { diff --git a/src/vs/platform/url/node/urlService.ts b/src/vs/platform/url/node/urlService.ts new file mode 100644 index 0000000000000..adf857493e848 --- /dev/null +++ b/src/vs/platform/url/node/urlService.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI, UriComponents } from 'vs/base/common/uri'; +import product from 'vs/platform/product/node/product'; +import { AbstractURLService } from 'vs/platform/url/common/urlService'; + +export class URLService extends AbstractURLService { + + create(options?: Partial): URI { + const { authority, path, query, fragment } = options ? options : { authority: undefined, path: undefined, query: undefined, fragment: undefined }; + + return URI.from({ scheme: product.urlProtocol, authority, path, query, fragment }); + } +} diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 30f5775ce516f..654f61ec02ffa 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1157,4 +1157,50 @@ declare module 'vscode' { } //#endregion + + // #region Ben - extension auth flow (desktop+web) + + export namespace env { + + export interface AppUriOptions { + payload?: { + path?: string; + query?: string; + fragment?: string; + }; + } + + /** + * Creates a Uri that - if opened in a browser - will result in a + * registered [UriHandler](#UriHandler) to fire. The handler's + * Uri will be configured with the path, query and fragment of + * [AppUriOptions](#AppUriOptions) if provided, otherwise it will be empty. + * + * Extensions should not make any assumptions about the resulting + * Uri and should not alter it in anyway. Rather, extensions can e.g. + * use this Uri in an authentication flow, by adding the Uri as + * callback query argument to the server to authenticate to. + * + * Note: If the server decides to add additional query parameters to the Uri + * (e.g. a token or secret), it will appear in the Uri that is passed + * to the [UriHandler](#UriHandler). + * + * **Example** of an authentication flow: + * ```typescript + * vscode.window.registerUriHandler({ + * handleUri(uri: vscode.Uri): vscode.ProviderResult { + * if (uri.path === '/did-authenticate') { + * console.log(uri.toString()); + * } + * } + * }); + * + * const callableUri = await vscode.env.createAppUri({ payload: { path: '/did-authenticate' } }); + * await vscode.env.openExternal(callableUri); + * ``` + */ + export function createAppUri(options?: AppUriOptions): Thenable; + } + + //#endregion } diff --git a/src/vs/workbench/api/browser/mainThreadUrls.ts b/src/vs/workbench/api/browser/mainThreadUrls.ts index 224d51f978ccf..75fdde3e49530 100644 --- a/src/vs/workbench/api/browser/mainThreadUrls.ts +++ b/src/vs/workbench/api/browser/mainThreadUrls.ts @@ -6,7 +6,7 @@ import { ExtHostContext, IExtHostContext, MainContext, MainThreadUrlsShape, ExtHostUrlsShape } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from '../common/extHostCustomers'; import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IExtensionUrlHandler } from 'vs/workbench/services/extensions/common/inactiveExtensionUrlHandler'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @@ -68,6 +68,16 @@ export class MainThreadUrls implements MainThreadUrlsShape { return Promise.resolve(undefined); } + async $createAppUri(extensionId: ExtensionIdentifier, options?: { payload?: Partial }): Promise { + const payload: Partial = options && options.payload ? options.payload : Object.create(null); + + // we define the authority to be the extension ID to ensure + // that the Uri gets routed back to the extension properly. + payload.authority = extensionId.value; + + return this.urlService.create(payload); + } + dispose(): void { this.handlers.forEach(({ disposable }) => disposable.dispose()); this.handlers.clear(); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 755d23b1d4f8e..5b4a69da168d6 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -234,6 +234,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get appName() { return initData.environment.appName; }, get appRoot() { return initData.environment.appRoot!.fsPath; }, get uriScheme() { return initData.environment.appUriScheme; }, + createAppUri(options?) { + checkProposedApiEnabled(extension); + return extHostUrls.createAppUri(extension.identifier, options); + }, get logLevel() { checkProposedApiEnabled(extension); return typeConverters.LogLevel.to(extHostLogService.getLevel()); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9e9abe2e83435..d6ca16a738cb0 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -567,6 +567,7 @@ export interface ExtHostWebviewsShape { export interface MainThreadUrlsShape extends IDisposable { $registerUriHandler(handle: number, extensionId: ExtensionIdentifier): Promise; $unregisterUriHandler(handle: number): Promise; + $createAppUri(extensionId: ExtensionIdentifier, options?: { payload?: Partial }): Promise; } export interface ExtHostUrlsShape { diff --git a/src/vs/workbench/api/common/extHostUrls.ts b/src/vs/workbench/api/common/extHostUrls.ts index 778d957cbc775..2864722484810 100644 --- a/src/vs/workbench/api/common/extHostUrls.ts +++ b/src/vs/workbench/api/common/extHostUrls.ts @@ -55,4 +55,8 @@ export class ExtHostUrls implements ExtHostUrlsShape { return Promise.resolve(undefined); } + + async createAppUri(extensionId: ExtensionIdentifier, options?: vscode.env.AppUriOptions): Promise { + return URI.revive(await this._proxy.$createAppUri(extensionId, options)); + } } diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 93708d0bc405b..895a8a0393f14 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -7,11 +7,8 @@ import { URI } from 'vs/base/common/uri'; import * as browser from 'vs/base/browser/browser'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Event } from 'vs/base/common/event'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IURLHandler, IURLService } from 'vs/platform/url/common/url'; import { ILogService } from 'vs/platform/log/common/log'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IUpdateService, State } from 'vs/platform/update/common/update'; import { IWindowService, INativeOpenDialogOptions, IEnterWorkspaceResult, IURIToOpen, IMessageBoxResult, IWindowsService, IOpenSettings, IWindowSettings } from 'vs/platform/windows/common/windows'; @@ -37,29 +34,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService // tslint:disable-next-line: import-patterns import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats'; -//#region Extension URL Handler - -export const IExtensionUrlHandler = createDecorator('inactiveExtensionUrlHandler'); - -export interface IExtensionUrlHandler { - readonly _serviceBrand: any; - registerExtensionHandler(extensionId: ExtensionIdentifier, handler: IURLHandler): void; - unregisterExtensionHandler(extensionId: ExtensionIdentifier): void; -} - -export class SimpleExtensionURLHandler implements IExtensionUrlHandler { - - _serviceBrand: any; - - registerExtensionHandler(extensionId: ExtensionIdentifier, handler: IURLHandler): void { } - - unregisterExtensionHandler(extensionId: ExtensionIdentifier): void { } -} - -registerSingleton(IExtensionUrlHandler, SimpleExtensionURLHandler, true); - -//#endregion - //#region Update export class SimpleUpdateService implements IUpdateService { @@ -94,24 +68,6 @@ registerSingleton(IUpdateService, SimpleUpdateService); //#endregion -//#region URL - -export class SimpleURLService implements IURLService { - _serviceBrand: any; - - open(url: URI): Promise { - return Promise.resolve(false); - } - - registerHandler(handler: IURLHandler): IDisposable { - return Disposable.None; - } -} - -registerSingleton(IURLService, SimpleURLService); - -//#endregion - //#region Window export class SimpleWindowService extends Disposable implements IWindowService { diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts index 70851b908c950..ab7a75444b5b2 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts @@ -15,7 +15,7 @@ import { IExtensionEnablementService } from 'vs/workbench/services/extensionMana import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { Emitter } from 'vs/base/common/event'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { IURLService } from 'vs/platform/url/common/url'; import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 32c9e48f8aa1d..93e10545ebde7 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -31,7 +31,7 @@ import { TestContextService, TestWindowService, TestSharedProcessService } from import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IWindowService } from 'vs/platform/windows/common/windows'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { URI } from 'vs/base/common/uri'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts index 83231f515ea17..1ac40b499a25a 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -40,7 +40,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { INotificationService, Severity, IPromptChoice, IPromptOptions } from 'vs/platform/notification/common/notification'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index 318a7e17e450a..50dac5ecc8b2f 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -31,7 +31,7 @@ import { TestContextService, TestWindowService, TestSharedProcessService } from import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IWindowService } from 'vs/platform/windows/common/windows'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { URI } from 'vs/base/common/uri'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { SinonStub } from 'sinon'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index c87fed721f4bb..f6ca00e7b8897 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -34,7 +34,7 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { ProgressService } from 'vs/workbench/services/progress/browser/progressService'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; diff --git a/src/vs/workbench/services/url/browser/urlService.ts b/src/vs/workbench/services/url/browser/urlService.ts new file mode 100644 index 0000000000000..f69c231457d2d --- /dev/null +++ b/src/vs/workbench/services/url/browser/urlService.ts @@ -0,0 +1,177 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IURLService } from 'vs/platform/url/common/url'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { ServiceIdentifier, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { AbstractURLService } from 'vs/platform/url/common/urlService'; +import { Event, Emitter } from 'vs/base/common/event'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { streamToBuffer } from 'vs/base/common/buffer'; +import { ILogService } from 'vs/platform/log/common/log'; +import { generateUuid } from 'vs/base/common/uuid'; + +export interface IURLCallbackProvider { + + /** + * Indicates that a Uri has been opened outside of VSCode. The Uri + * will be forwarded to all installed Uri handlers in the system. + */ + readonly onCallback: Event; + + /** + * Creates a Uri that - if opened in a browser - must result in + * the `onCallback` to fire. + * + * The optional `Partial` must be properly restored for + * the Uri passed to the `onCallback` handler. + * + * For example: if a Uri is to be created with `scheme:"vscode"`, + * `authority:"foo"` and `path:"bar"` the `onCallback` should fire + * with a Uri `vscode://foo/bar`. + * + * If there are additional `query` values in the Uri, they should + * be added to the list of provided `query` arguments from the + * `Partial`. + */ + create(options?: Partial): URI; +} + +export class BrowserURLService extends AbstractURLService { + + _serviceBrand!: ServiceIdentifier; + + private provider: IURLCallbackProvider; + + constructor( + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(); + + this.provider = environmentService.options && environmentService.options.urlCallbackProvider ? environmentService.options.urlCallbackProvider : instantiationService.createInstance(SelfhostURLCallbackProvider); + + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.provider.onCallback(uri => this.open(uri))); + } + + create(options?: Partial): URI { + return this.provider.create(options); + } +} + +class SelfhostURLCallbackProvider extends Disposable implements IURLCallbackProvider { + + static FETCH_INTERVAL = 500; // fetch every 500ms + static FETCH_TIMEOUT = 5 * 60 * 1000; // ...but stop after 5min + + static QUERY_KEYS = { + REQUEST_ID: 'vscode-requestId', + SCHEME: 'vscode-scheme', + AUTHORITY: 'vscode-authority', + PATH: 'vscode-path', + QUERY: 'vscode-query', + FRAGMENT: 'vscode-fragment' + }; + + private readonly _onCallback: Emitter = this._register(new Emitter()); + readonly onCallback: Event = this._onCallback.event; + + constructor( + @IRequestService private readonly requestService: IRequestService, + @ILogService private readonly logService: ILogService + ) { + super(); + } + + create(options?: Partial): URI { + const queryValues: Map = new Map(); + + const requestId = generateUuid(); + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId); + + const { scheme, authority, path, query, fragment } = options ? options : { scheme: undefined, authority: undefined, path: undefined, query: undefined, fragment: undefined }; + + if (scheme) { + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.SCHEME, scheme); + } + + if (authority) { + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.AUTHORITY, authority); + } + + if (path) { + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.PATH, path); + } + + if (query) { + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.QUERY, query); + } + + if (fragment) { + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.FRAGMENT, fragment); + } + + // Start to poll on the callback being fired + this.periodicFetchCallback(requestId, Date.now()); + + return this.doCreateUri('callback', queryValues); + } + + private async periodicFetchCallback(requestId: string, startTime: number): Promise { + + // Ask server for callback results + const queryValues: Map = new Map(); + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId); + + const result = await this.requestService.request({ + url: this.doCreateUri('fetch-callback', queryValues).toString(true) + }, CancellationToken.None); + + // Check for callback results + const content = await streamToBuffer(result.stream); + if (content.byteLength > 0) { + try { + this._onCallback.fire(URI.revive(JSON.parse(content.toString()))); + } catch (error) { + this.logService.error(error); + } + + return; // done + } + + // Continue fetching unless we hit the timeout + if (Date.now() - startTime < SelfhostURLCallbackProvider.FETCH_TIMEOUT) { + setTimeout(() => this.periodicFetchCallback(requestId, startTime), SelfhostURLCallbackProvider.FETCH_INTERVAL); + } + } + + private doCreateUri(path: string, queryValues: Map): URI { + let query: string | undefined = undefined; + + if (queryValues) { + let index = 0; + queryValues.forEach((value, key) => { + if (!query) { + query = ''; + } + + const prefix = (index++ === 0) ? '' : '&'; + query += `${prefix}${key}=${encodeURIComponent(value)}`; + }); + } + + return URI.parse(window.location.href).with({ path, query }); + } +} + +registerSingleton(IURLService, BrowserURLService, true); diff --git a/src/vs/platform/url/electron-browser/urlService.ts b/src/vs/workbench/services/url/electron-browser/urlService.ts similarity index 89% rename from src/vs/platform/url/electron-browser/urlService.ts rename to src/vs/workbench/services/url/electron-browser/urlService.ts index 1b1c88721b75e..f1a73d383d954 100644 --- a/src/vs/platform/url/electron-browser/urlService.ts +++ b/src/vs/workbench/services/url/electron-browser/urlService.ts @@ -7,9 +7,10 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { URI } from 'vs/base/common/uri'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { URLServiceChannelClient, URLHandlerChannel } from 'vs/platform/url/node/urlIpc'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import product from 'vs/platform/product/node/product'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class RelayURLService extends URLService implements IURLHandler { @@ -43,3 +44,5 @@ export class RelayURLService extends URLService implements IURLHandler { return super.open(uri); } } + +registerSingleton(IURLService, RelayURLService); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 198e68e65bb2e..2fdb06fe05894 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -52,6 +52,7 @@ import 'vs/workbench/browser/parts/views/views'; //#region --- workbench services +import 'vs/workbench/services/extensions/common/inactiveExtensionUrlHandler'; import 'vs/workbench/services/bulkEdit/browser/bulkEditService'; import 'vs/workbench/services/keybinding/common/keybindingEditing'; import 'vs/workbench/services/decorations/browser/decorationsService'; diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index edfd8a273f3e8..a566d1d7900a3 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -30,7 +30,6 @@ import 'vs/workbench/electron-browser/desktop.main'; import 'vs/workbench/services/integrity/node/integrityService'; import 'vs/workbench/services/textMate/electron-browser/textMateService'; import 'vs/workbench/services/workspace/electron-browser/workspaceEditingService'; -import 'vs/workbench/services/extensions/common/inactiveExtensionUrlHandler'; import 'vs/workbench/services/search/node/searchService'; import 'vs/workbench/services/output/node/outputChannelModelService'; import 'vs/workbench/services/textfile/node/textFileService'; @@ -49,6 +48,7 @@ import 'vs/workbench/services/accessibility/node/accessibilityService'; import 'vs/workbench/services/remote/node/tunnelService'; import 'vs/workbench/services/backup/node/backupFileService'; import 'vs/workbench/services/credentials/node/credentialsService'; +import 'vs/workbench/services/url/electron-browser/urlService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -70,8 +70,6 @@ import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { WorkspacesService } from 'vs/platform/workspaces/electron-browser/workspacesService'; import { IMenubarService } from 'vs/platform/menubar/node/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; -import { IURLService } from 'vs/platform/url/common/url'; -import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; registerSingleton(IClipboardService, ClipboardService, true); @@ -84,7 +82,6 @@ registerSingleton(IUpdateService, UpdateService); registerSingleton(IIssueService, IssueService); registerSingleton(IWorkspacesService, WorkspacesService); registerSingleton(IMenubarService, MenubarService); -registerSingleton(IURLService, RelayURLService); registerSingleton(IStaticExtensionsService, class extends StaticExtensionsService { constructor() { super([]); } }); //#endregion diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index dd84853e87ab6..844ff1da56a73 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -10,6 +10,7 @@ import { IFileSystemProvider } from 'vs/platform/files/common/files'; import { IWebSocketFactory } from 'vs/platform/remote/browser/browserSocketFactory'; import { ICredentialsProvider } from 'vs/workbench/services/credentials/browser/credentialsService'; import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; +import { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService'; export interface IWorkbenchConstructionOptions { @@ -65,6 +66,11 @@ export interface IWorkbenchConstructionOptions { * Experimental: Add static extensions that cannot be uninstalled but only be disabled. */ staticExtensions?: { packageJSON: IExtensionManifest, extensionLocation: UriComponents }[]; + + /** + * Experimental: Support for URL callbacks. + */ + urlCallbackProvider?: IURLCallbackProvider; } /** diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 20db7d0737836..681fc606b6a86 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -37,6 +37,7 @@ import 'vs/workbench/services/extensionManagement/common/extensionManagementServ import 'vs/workbench/services/telemetry/browser/telemetryService'; import 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; import 'vs/workbench/services/credentials/browser/credentialsService'; +import 'vs/workbench/services/url/browser/urlService'; import 'vs/workbench/browser/web.simpleservices'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; From 232b6a35159bd49f0905fd8017878ee943c003dd Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 22 Aug 2019 10:52:50 +0200 Subject: [PATCH 522/613] Finalize multi-select support for custom tree view Fixes #76941 --- src/vs/vscode.d.ts | 7 +++++++ src/vs/vscode.proposed.d.ts | 9 --------- src/vs/workbench/api/common/extHostTreeViews.ts | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 3611c2746ab5d..e047c9c2d95fb 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -6882,6 +6882,13 @@ declare module 'vscode' { * Whether to show collapse all action or not. */ showCollapseAll?: boolean; + + /** + * 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; } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 654f61ec02ffa..665eb1130c1b7 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1030,15 +1030,6 @@ 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 diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 66c8e3ed7f7ef..23fdc7054b2c3 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -193,7 +193,7 @@ class ExtHostTreeView extends Disposable { private refreshPromise: Promise = Promise.resolve(); private refreshQueue: Promise = Promise.resolve(); - constructor(private viewId: string, options: vscode.TreeViewOptions2, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService, private extension: IExtensionDescription) { + constructor(private viewId: string, options: vscode.TreeViewOptions, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService, private extension: IExtensionDescription) { super(); this.dataProvider = options.treeDataProvider; this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany }); From 9c39bf2dc3b9ecaa0215efabd2625bf860fc2312 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 10:45:28 +0200 Subject: [PATCH 523/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9cf53d386d6f1..e6a1abf6fcb0c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "116231da62de1019aab221a7b307f1d41edb5774", + "distro": "97a3da8fc0166ace25ffb8f21585fd58ba3305c8", "author": { "name": "Microsoft Corporation" }, From d104c2c46d21461380a064143ef9c4ed2ed6bad1 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 22 Aug 2019 10:56:41 +0200 Subject: [PATCH 524/613] Fixes #79475: Protect against bad callers of asCSSUrl --- src/vs/base/browser/dom.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 1639bb949fe12..28fbfc82f45ec 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1202,5 +1202,8 @@ export function asDomUri(uri: URI): URI { * returns url('...') */ export function asCSSUrl(uri: URI): string { + if (!uri) { + return `url('')`; + } return `url('${asDomUri(uri).toString(true).replace(/'/g, '%27')}')`; } From 682f371512dc9c10b1b1929a8e8deacfc0fbff83 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 22 Aug 2019 11:00:53 +0200 Subject: [PATCH 525/613] call stack: debug session should not show an expando when it has no children fixes #79582 --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index c89c4b8324724..46e90870f22b6 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -590,7 +590,12 @@ class CallStackDataSource implements IAsyncDataSource 1) || (threads.length === 1 && threads[0].stopped) || (this.debugService.getModel().getSessions().filter(s => s.parentSession === element).length > 0); + } + + return isDebugModel(element) || (element instanceof Thread && element.stopped); } async getChildren(element: IDebugModel | CallStackItem): Promise { From 4655b60a448adfb0b827070008108d0ee2afc078 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 11:06:17 +0200 Subject: [PATCH 526/613] editor service :lipstick: --- .../services/editor/browser/editorService.ts | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index ca6f5cf469a36..22375af730ee0 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -280,17 +280,30 @@ export class EditorService extends Disposable implements EditorServiceImpl { } // Respect option to reveal an editor if it is open (not necessarily visible) + // Still prefer to reveal an editor in a group where the editor is active though. if (!targetGroup) { if ((options && options.revealIfOpened) || this.configurationService.getValue('workbench.editor.revealIfOpen')) { + let groupWithInputActive: IEditorGroup | undefined = undefined; + let groupWithInputOpened: IEditorGroup | undefined = undefined; + for (const group of groupsByLastActive) { - if (group.isOpened(input) && group.isActive(input)) { - targetGroup = group; - break; + if (group.isOpened(input)) { + if (!groupWithInputOpened) { + groupWithInputOpened = group; + } + + if (!groupWithInputActive && group.isActive(input)) { + groupWithInputActive = group; + } } - if (group.isOpened(input) && !targetGroup) { - targetGroup = group; + + if (groupWithInputOpened && groupWithInputActive) { + break; // we found all groups we wanted } } + + // Prefer a target group where the input is visible + targetGroup = groupWithInputActive || groupWithInputOpened; } } } From df2e0c71f70d98975b7feeb2c1c778bfe92f27c3 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 22 Aug 2019 11:39:05 +0200 Subject: [PATCH 527/613] fixes #79625 --- src/vs/workbench/contrib/debug/common/debugger.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/common/debugger.ts b/src/vs/workbench/contrib/debug/common/debugger.ts index 8f317ccc138f7..8559889910ed0 100644 --- a/src/vs/workbench/contrib/debug/common/debugger.ts +++ b/src/vs/workbench/contrib/debug/common/debugger.ts @@ -236,16 +236,18 @@ export class Debugger implements IDebugger { }; properties['preLaunchTask'] = { anyOf: [taskSchema, { - type: ['string', 'null'], + type: ['string'] }], default: '', + defaultSnippets: [{ body: { task: '', type: '' } }], description: nls.localize('debugPrelaunchTask', "Task to run before debug session starts.") }; properties['postDebugTask'] = { anyOf: [taskSchema, { - type: ['string', 'null'], + type: ['string'], }], default: '', + defaultSnippets: [{ body: { task: '', type: '' } }], description: nls.localize('debugPostDebugTask', "Task to run after debug session ends.") }; properties['internalConsoleOptions'] = INTERNAL_CONSOLE_OPTIONS_SCHEMA; From 9f38b4e8bdcf477b42af04617ce394128e342129 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 11:56:10 +0200 Subject: [PATCH 528/613] fallback in memory log provider and add console logging --- src/vs/workbench/browser/web.main.ts | 22 ++++++++++++++----- .../log/browser/indexedDBLogProvider.ts | 12 +++++----- .../log/common/inMemoryLogProvider.ts | 7 ------ 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index a5ff2b965a61b..22b6b35251a33 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -6,7 +6,7 @@ import { mark } from 'vs/base/common/performance'; import { domContentLoaded, addDisposableListener, EventType, addClass } from 'vs/base/browser/dom'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { ILogService } from 'vs/platform/log/common/log'; +import { ILogService, ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { Workbench } from 'vs/workbench/browser/workbench'; @@ -44,7 +44,8 @@ import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/ import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { FileLogService } from 'vs/platform/log/common/fileLogService'; import { toLocalISOString } from 'vs/base/common/date'; -import { INDEXEDDB_LOG_SCHEME, IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedDBLogProvider'; +import { IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedDBLogProvider'; +import { InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider'; class CodeRendererMain extends Disposable { @@ -120,7 +121,7 @@ class CodeRendererMain extends Disposable { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Log - const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: INDEXEDDB_LOG_SCHEME }); + const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: 'vscode-log' }); const logService = new BufferLogService(); serviceCollection.set(ILogService, logService); @@ -151,8 +152,19 @@ class CodeRendererMain extends Disposable { serviceCollection.set(IFileService, fileService); // Logger - fileService.registerProvider(INDEXEDDB_LOG_SCHEME, new IndexedDBLogProvider()); - logService.logger = new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService); + const indexedDBLogProvider = new IndexedDBLogProvider(logsPath.scheme); + indexedDBLogProvider.database.then( + () => fileService.registerProvider(logsPath.scheme, indexedDBLogProvider), + e => { + (logService).info('Error while creating indexedDB log provider. Falling back to in memory log provider.'); + (logService).error(e); + fileService.registerProvider(logsPath.scheme, new InMemoryLogProvider(logsPath.scheme)); + } + ).then(() => { + const consoleLogService = new ConsoleLogService(logService.getLevel()); + const fileLogService = new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService); + logService.logger = new MultiplexLogService([consoleLogService, fileLogService]); + }); // Static Extensions const staticExtensions = new StaticExtensionsService(this.configuration.staticExtensions || []); diff --git a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts index 3a662c08f53bb..16264dd585165 100644 --- a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts +++ b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts @@ -5,23 +5,21 @@ import { KeyValueLogProvider } from 'vs/workbench/services/log/common/keyValueLogProvider'; -export const INDEXEDDB_LOG_SCHEME = 'vscode-logs-indexedbd'; -export const INDEXEDDB_LOGS_DB = 'vscode-logs-db'; +export const INDEXEDDB_VSCODE_DB = 'vscode-web-db'; export const INDEXEDDB_LOGS_OBJECT_STORE = 'vscode-logs-store'; export class IndexedDBLogProvider extends KeyValueLogProvider { - private readonly database: Promise; + readonly database: Promise; - constructor( - ) { - super(INDEXEDDB_LOG_SCHEME); + constructor(scheme: string) { + super(scheme); this.database = this.openDatabase(1); } private openDatabase(version: number): Promise { return new Promise((c, e) => { - const request = window.indexedDB.open(INDEXEDDB_LOGS_DB, version); + const request = window.indexedDB.open(INDEXEDDB_VSCODE_DB, version); request.onerror = (err) => e(request.error); request.onsuccess = () => { const db = request.result; diff --git a/src/vs/workbench/services/log/common/inMemoryLogProvider.ts b/src/vs/workbench/services/log/common/inMemoryLogProvider.ts index 16208cbae0975..f8d87167c6e8c 100644 --- a/src/vs/workbench/services/log/common/inMemoryLogProvider.ts +++ b/src/vs/workbench/services/log/common/inMemoryLogProvider.ts @@ -6,17 +6,10 @@ import { KeyValueLogProvider } from 'vs/workbench/services/log/common/keyValueLogProvider'; import { keys } from 'vs/base/common/map'; -export const INMEMORY_LOG_SCHEME = 'vscode-logs-inmemory'; - export class InMemoryLogProvider extends KeyValueLogProvider { private readonly logs: Map = new Map(); - constructor( - ) { - super(INMEMORY_LOG_SCHEME); - } - protected async getAllKeys(): Promise { return keys(this.logs); } From 366e1b275cf01e2d50e96083dede553de608b078 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 22 Aug 2019 11:57:52 +0200 Subject: [PATCH 529/613] :lipstick: --- src/vs/workbench/browser/web.main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 22b6b35251a33..57192e3e1c8e6 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -156,7 +156,7 @@ class CodeRendererMain extends Disposable { indexedDBLogProvider.database.then( () => fileService.registerProvider(logsPath.scheme, indexedDBLogProvider), e => { - (logService).info('Error while creating indexedDB log provider. Falling back to in memory log provider.'); + (logService).info('Error while creating indexedDB log provider. Falling back to in-memory log provider.'); (logService).error(e); fileService.registerProvider(logsPath.scheme, new InMemoryLogProvider(logsPath.scheme)); } From b97d6db4809ee14e0a8c6334eedb9b3f7b3121c4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 22 Aug 2019 12:36:44 +0200 Subject: [PATCH 530/613] some tsconfig polish --- src/tsconfig.base.json | 9 +-------- src/tsconfig.json | 7 +++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tsconfig.base.json b/src/tsconfig.base.json index 79d754c01f5c3..b782f00466ad9 100644 --- a/src/tsconfig.base.json +++ b/src/tsconfig.base.json @@ -17,13 +17,6 @@ "vs/*": [ "./vs/*" ] - }, - "types": [ - "keytar", - "mocha", - "semver", - "sinon", - "winreg" - ] + } } } diff --git a/src/tsconfig.json b/src/tsconfig.json index 7fbb44c3ec5dc..b847912b35c17 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -11,6 +11,13 @@ "es5", "es2015.iterable", "webworker" + ], + "types": [ + "keytar", + "mocha", + "semver", + "sinon", + "winreg" ] }, "include": [ From d5897baed7b5920f20b98b6d6712d4a60578f35d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 13:28:51 +0200 Subject: [PATCH 531/613] web auth - move interface out of env --- src/vs/vscode.proposed.d.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 665eb1130c1b7..19b06c217ba53 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1151,15 +1151,15 @@ declare module 'vscode' { // #region Ben - extension auth flow (desktop+web) - export namespace env { + export interface AppUriOptions { + payload?: { + path?: string; + query?: string; + fragment?: string; + }; + } - export interface AppUriOptions { - payload?: { - path?: string; - query?: string; - fragment?: string; - }; - } + export namespace env { /** * Creates a Uri that - if opened in a browser - will result in a From 36ae34767288c4051c9a4c7f05581279c609c98b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 13:52:07 +0200 Subject: [PATCH 532/613] fix compile --- src/vs/workbench/api/common/extHostUrls.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostUrls.ts b/src/vs/workbench/api/common/extHostUrls.ts index 2864722484810..6b6f8081ae58f 100644 --- a/src/vs/workbench/api/common/extHostUrls.ts +++ b/src/vs/workbench/api/common/extHostUrls.ts @@ -56,7 +56,7 @@ export class ExtHostUrls implements ExtHostUrlsShape { return Promise.resolve(undefined); } - async createAppUri(extensionId: ExtensionIdentifier, options?: vscode.env.AppUriOptions): Promise { + async createAppUri(extensionId: ExtensionIdentifier, options?: vscode.AppUriOptions): Promise { return URI.revive(await this._proxy.$createAppUri(extensionId, options)); } } From a06015b53693d2f8a5a83cacff0da25b025f9e6f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 22 Aug 2019 14:21:33 +0200 Subject: [PATCH 533/613] have only one way of enforcing proposed API --- .../workbench/api/common/extHost.api.impl.ts | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 5b4a69da168d6..0696b5ec82d12 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -73,14 +73,6 @@ export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; } -function proposedApiFunction(extension: IExtensionDescription, fn: T): T { - if (extension.enableProposedApi) { - return fn; - } else { - return throwProposedApiError.bind(null, extension) as any as T; - } -} - /** * This method instantiates and returns the extension API surface */ @@ -206,7 +198,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }); }); }, - registerDiffInformationCommand: proposedApiFunction(extension, (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => { + registerDiffInformationCommand: (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => { + checkProposedApiEnabled(extension); return extHostCommands.registerCommand(true, id, async (...args: any[]): Promise => { const activeTextEditor = extHostEditors.getActiveTextEditor(); if (!activeTextEditor) { @@ -217,7 +210,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const diff = await extHostEditors.getDiffInformation(activeTextEditor.id); callback.apply(thisArg, [diff, ...args]); }); - }), + }, executeCommand(id: string, ...args: any[]): Thenable { return extHostCommands.executeCommand(id, ...args); }, @@ -530,9 +523,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => { return extHostWebviews.registerWebviewPanelSerializer(viewType, serializer); }, - registerDecorationProvider: proposedApiFunction(extension, (provider: vscode.DecorationProvider) => { + registerDecorationProvider(provider: vscode.DecorationProvider) { + checkProposedApiEnabled(extension); return extHostDecorations.registerDecorationProvider(provider, extension.identifier); - }), + }, registerUriHandler(handler: vscode.UriHandler) { return extHostUrls.registerUriHandler(extension.identifier, handler); }, @@ -667,24 +661,30 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get fs() { return extHostFileSystem.fileSystem; }, - registerFileSearchProvider: proposedApiFunction(extension, (scheme: string, provider: vscode.FileSearchProvider) => { + registerFileSearchProvider: (scheme: string, provider: vscode.FileSearchProvider) => { + checkProposedApiEnabled(extension); return extHostSearch.registerFileSearchProvider(scheme, provider); - }), - registerTextSearchProvider: proposedApiFunction(extension, (scheme: string, provider: vscode.TextSearchProvider) => { + }, + registerTextSearchProvider: (scheme: string, provider: vscode.TextSearchProvider) => { + checkProposedApiEnabled(extension); return extHostSearch.registerTextSearchProvider(scheme, provider); - }), - registerRemoteAuthorityResolver: proposedApiFunction(extension, (authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver) => { + }, + registerRemoteAuthorityResolver: (authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver) => { + checkProposedApiEnabled(extension); return extensionService.registerRemoteAuthorityResolver(authorityPrefix, resolver); - }), - registerResourceLabelFormatter: proposedApiFunction(extension, (formatter: vscode.ResourceLabelFormatter) => { + }, + registerResourceLabelFormatter: (formatter: vscode.ResourceLabelFormatter) => { + checkProposedApiEnabled(extension); return extHostLabelService.$registerResourceLabelFormatter(formatter); - }), - onDidRenameFile: proposedApiFunction(extension, (listener: (e: vscode.FileRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => { + }, + onDidRenameFile: (listener: (e: vscode.FileRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => { + checkProposedApiEnabled(extension); return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables); - }), - onWillRenameFile: proposedApiFunction(extension, (listener: (e: vscode.FileWillRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => { + }, + onWillRenameFile: (listener: (e: vscode.FileWillRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => { + checkProposedApiEnabled(extension); return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables); - }) + } }; // namespace: scm From 8ecc032c23325018a57091189371f5f0da1b47f6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 22 Aug 2019 14:53:40 +0200 Subject: [PATCH 534/613] document symbols and deprecated, UX, internal API, #50972 --- src/vs/editor/common/modes.ts | 4 ++++ src/vs/editor/common/standalone/standaloneEnums.ts | 4 ++++ src/vs/editor/contrib/documentSymbols/media/outlineTree.css | 5 +++++ src/vs/editor/contrib/documentSymbols/outlineTree.ts | 6 +++++- .../contrib/documentSymbols/test/outlineModel.test.ts | 1 + src/vs/editor/contrib/quickOpen/quickOpen.ts | 1 + src/vs/editor/standalone/browser/standaloneLanguages.ts | 1 + src/vs/monaco.d.ts | 5 +++++ src/vs/workbench/api/common/extHostLanguageFeatures.ts | 1 + src/vs/workbench/api/common/extHostTypeConverters.ts | 3 ++- 10 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 78610e6c0040e..7060c53fdfcc4 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -867,6 +867,9 @@ export const enum SymbolKind { TypeParameter = 25 } +export const enum SymbolKindTag { + Deprecated = 1, +} /** * @internal @@ -910,6 +913,7 @@ export interface DocumentSymbol { name: string; detail: string; kind: SymbolKind; + kindTags: SymbolKindTag[]; containerName?: string; range: IRange; selectionRange: IRange; diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 76ee0d96933f2..4aa6084856207 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -660,4 +660,8 @@ export enum SymbolKind { Event = 23, Operator = 24, TypeParameter = 25 +} + +export enum SymbolKindTag { + Deprecated = 1 } \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/outlineTree.css b/src/vs/editor/contrib/documentSymbols/media/outlineTree.css index ebc2389a0e269..3d6454a1556e8 100644 --- a/src/vs/editor/contrib/documentSymbols/media/outlineTree.css +++ b/src/vs/editor/contrib/documentSymbols/media/outlineTree.css @@ -20,6 +20,11 @@ color: var(--outline-element-color); } +.monaco-list .outline-element .deprecated { + text-decoration: line-through; + opacity: 0.66; +} + .monaco-tree .monaco-tree-row.focused .outline-element .outline-element-detail { visibility: inherit; } diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 365b3e45c1399..6df7d87e2eece 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -12,7 +12,7 @@ import { createMatches, FuzzyScore } from 'vs/base/common/filters'; import 'vs/css!./media/outlineTree'; import 'vs/css!./media/symbol-icons'; import { Range } from 'vs/editor/common/core/range'; -import { SymbolKind, symbolKindToCssClass } from 'vs/editor/common/modes'; +import { SymbolKind, symbolKindToCssClass, SymbolKindTag } from 'vs/editor/common/modes'; import { OutlineElement, OutlineGroup, OutlineModel } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { localize } from 'vs/nls'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; @@ -127,6 +127,10 @@ export class OutlineElementRenderer implements ITreeRenderer= 0) { + options.extraClasses.push(`deprecated`); + options.matches = []; + } template.iconLabel.setLabel(element.symbol.name, element.symbol.detail, options); this._renderMarkerInfo(element, template); } diff --git a/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts b/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts index b15a14510720c..9622de2716e89 100644 --- a/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts +++ b/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts @@ -76,6 +76,7 @@ suite('OutlineModel', function () { name, detail: 'fake', kind: SymbolKind.Boolean, + kindTags: [], selectionRange: range, range: range }; diff --git a/src/vs/editor/contrib/quickOpen/quickOpen.ts b/src/vs/editor/contrib/quickOpen/quickOpen.ts index dba67c40b01dd..919e3eae79280 100644 --- a/src/vs/editor/contrib/quickOpen/quickOpen.ts +++ b/src/vs/editor/contrib/quickOpen/quickOpen.ts @@ -51,6 +51,7 @@ function flatten(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideCo for (let entry of entries) { bucket.push({ kind: entry.kind, + kindTags: [], name: entry.name, detail: entry.detail, containerName: entry.containerName || overrideContainerLabel, diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index 30209af005c55..0576af462b197 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -565,6 +565,7 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages { CompletionItemKindModifier: standaloneEnums.CompletionItemKindModifier, CompletionItemInsertTextRule: standaloneEnums.CompletionItemInsertTextRule, SymbolKind: standaloneEnums.SymbolKind, + SymbolKindTag: standaloneEnums.SymbolKindTag, IndentAction: standaloneEnums.IndentAction, CompletionTriggerKind: standaloneEnums.CompletionTriggerKind, SignatureHelpTriggerKind: standaloneEnums.SignatureHelpTriggerKind, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 5cab18f9cffd7..1d2b95c16942d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5232,10 +5232,15 @@ declare namespace monaco.languages { TypeParameter = 25 } + export enum SymbolKindTag { + Deprecated = 1 + } + export interface DocumentSymbol { name: string; detail: string; kind: SymbolKind; + kindTags: SymbolKindTag[]; containerName?: string; range: IRange; selectionRange: IRange; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 3954141c90aeb..a39d24d756a41 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -70,6 +70,7 @@ class DocumentSymbolAdapter { const element = { name: info.name || '!!MISSING: name!!', kind: typeConvert.SymbolKind.from(info.kind), + kindTags: [], detail: undefined!, // Strict null override — avoid changing behavior containerName: info.containerName, range: typeConvert.Range.from(info.location.range), diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 1fdc6fef86fa1..0a3819ccd832b 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -582,7 +582,8 @@ export namespace DocumentSymbol { detail: info.detail, range: Range.from(info.range), selectionRange: Range.from(info.selectionRange), - kind: SymbolKind.from(info.kind) + kind: SymbolKind.from(info.kind), + kindTags: [] }; if (info.children) { result.children = info.children.map(from); From 6f998109d8ff8ba2bef074eea1b5986a9443316e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 22 Aug 2019 07:06:13 -0700 Subject: [PATCH 535/613] Use void | number for onDidClose Fixes #79643 --- src/vs/vscode.proposed.d.ts | 7 ++++++- src/vs/workbench/api/node/extHostTerminalService.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 19b06c217ba53..8f2d4d5cc9446 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -881,6 +881,11 @@ declare module 'vscode' { /** * An event that when fired will signal that the pty is closed and dispose of the terminal. * + * A number can be used to provide an exit code for the terminal. Exit codes must be + * positive and a non-zero exit codes signals failure which shows a notification for a + * regular terminal and allows dependent tasks to proceed when used with the + * `CustomExecution2` API. + * * **Example:** Exit the terminal when "y" is pressed, otherwise show a notification. * ```typescript * const writeEmitter = new vscode.EventEmitter(); @@ -899,7 +904,7 @@ declare module 'vscode' { * }; * vscode.window.createTerminal({ name: 'Exit example', pty }); */ - onDidClose?: Event; + onDidClose?: Event; /** * Implement to handle when the pty is open and ready to start firing events. diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 5c5d76564f8f4..ecce50a23f39d 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -738,7 +738,7 @@ class ExtHostPseudoterminal implements ITerminalChildProcess { // Attach the listeners this._pty.onDidWrite(e => this._onProcessData.fire(e)); if (this._pty.onDidClose) { - this._pty.onDidClose(e => this._onProcessExit.fire(0)); + this._pty.onDidClose(e => this._onProcessExit.fire(e || 0)); } if (this._pty.onDidOverrideDimensions) { this._pty.onDidOverrideDimensions(e => this._onProcessOverrideDimensions.fire(e ? { cols: e.columns, rows: e.rows } : e)); From d65681b1afead90dc0bdcef46c9618a32a86b400 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 16:37:18 +0200 Subject: [PATCH 536/613] fix #79628 --- src/vs/workbench/browser/parts/editor/editorStatus.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index e1a7d9d0fecfa..0d54b781fb021 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -990,7 +990,7 @@ export class ChangeModeAction extends Action { private configureFileAssociation(resource: URI): void { const extension = extname(resource); const base = basename(resource); - const currentAssociation = this.modeService.getModeIdByFilepathOrFirstLine(resource.with({ path: base })); + const currentAssociation = this.modeService.getModeIdByFilepathOrFirstLine(URI.file(base)); const languages = this.modeService.getRegisteredLanguageNames(); const picks: IQuickPickItem[] = languages.sort().map((lang, index) => { From dabeb6f4042652e508799387802f75a18325d027 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 16:50:05 +0200 Subject: [PATCH 537/613] web urls - use absolute paths --- src/vs/workbench/services/url/browser/urlService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/url/browser/urlService.ts b/src/vs/workbench/services/url/browser/urlService.ts index f69c231457d2d..61c2278813dbc 100644 --- a/src/vs/workbench/services/url/browser/urlService.ts +++ b/src/vs/workbench/services/url/browser/urlService.ts @@ -124,7 +124,7 @@ class SelfhostURLCallbackProvider extends Disposable implements IURLCallbackProv // Start to poll on the callback being fired this.periodicFetchCallback(requestId, Date.now()); - return this.doCreateUri('callback', queryValues); + return this.doCreateUri('/callback', queryValues); } private async periodicFetchCallback(requestId: string, startTime: number): Promise { @@ -134,7 +134,7 @@ class SelfhostURLCallbackProvider extends Disposable implements IURLCallbackProv queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId); const result = await this.requestService.request({ - url: this.doCreateUri('fetch-callback', queryValues).toString(true) + url: this.doCreateUri('/fetch-callback', queryValues).toString(true) }, CancellationToken.None); // Check for callback results From 4acba1a2dd2b84b3af789dfac33fc328b01beb23 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 22 Aug 2019 18:05:41 +0200 Subject: [PATCH 538/613] Remove layer breaker from terminalTaskSystem Fixes #79210 --- .../contrib/tasks/browser/terminalTaskSystem.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 96148b4e58fca..7097c96cd89e5 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -44,6 +44,7 @@ import { Schemas } from 'vs/base/common/network'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { env as processEnv, cwd as processCwd } from 'vs/base/common/process'; interface TerminalData { terminal: ITerminalInstance; @@ -1376,8 +1377,7 @@ export class TerminalTaskSystem implements ITaskSystem { return command; } if (cwd === undefined) { - // tslint:disable-next-line: no-nodejs-globals - cwd = process.cwd(); + cwd = processCwd(); } const dir = path.dirname(command); if (dir !== '.') { @@ -1385,10 +1385,8 @@ export class TerminalTaskSystem implements ITaskSystem { // to the current working directory. return path.join(cwd, command); } - // tslint:disable-next-line: no-nodejs-globals - if (paths === undefined && Types.isString(process.env.PATH)) { - // tslint:disable-next-line: no-nodejs-globals - paths = process.env.PATH.split(path.delimiter); + if (paths === undefined && Types.isString(processEnv.PATH)) { + paths = processEnv.PATH.split(path.delimiter); } // No PATH environment. Make path absolute to the cwd. if (paths === undefined || paths.length === 0) { From da1745d34ce6ed0020a7a5ce2b6a09b00c9d66d3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 22 Aug 2019 09:22:12 -0700 Subject: [PATCH 539/613] xterm@3.15.0-beta101 Diff: https://github.com/xtermjs/xterm.js/compare/95ff154...2117005 Changes: - New paste API - New parser hook APIs Part of #78999 --- package.json | 2 +- remote/package.json | 2 +- remote/yarn.lock | 8 +- src/typings/xterm.d.ts | 207 +++++++++++++++++++++++++++++------------ yarn.lock | 8 +- 5 files changed, 159 insertions(+), 68 deletions(-) diff --git a/package.json b/package.json index e6a1abf6fcb0c..b7b3ce2bfe6f5 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "vscode-ripgrep": "^1.5.6", "vscode-sqlite3": "4.0.8", "vscode-textmate": "^4.2.2", - "xterm": "3.15.0-beta99", + "xterm": "3.15.0-beta101", "xterm-addon-search": "0.2.0-beta5", "xterm-addon-web-links": "0.1.0-beta10", "yauzl": "^2.9.2", diff --git a/remote/package.json b/remote/package.json index 2e8ceed08091f..9fa3881681da4 100644 --- a/remote/package.json +++ b/remote/package.json @@ -21,7 +21,7 @@ "vscode-proxy-agent": "0.4.0", "vscode-ripgrep": "^1.5.6", "vscode-textmate": "^4.2.2", - "xterm": "3.15.0-beta99", + "xterm": "3.15.0-beta101", "xterm-addon-search": "0.2.0-beta5", "xterm-addon-web-links": "0.1.0-beta10", "yauzl": "^2.9.2", diff --git a/remote/yarn.lock b/remote/yarn.lock index dca0e2501f02b..308bf9634b7a5 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -1225,10 +1225,10 @@ xterm-addon-web-links@0.1.0-beta10: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.1.0-beta10.tgz#610fa9773a2a5ccd41c1c83ba0e2dd2c9eb66a23" integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg== -xterm@3.15.0-beta99: - version "3.15.0-beta99" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta99.tgz#0010a7ea5d56cbb08a1e3a525b353c96a158e7a0" - integrity sha512-Vm0ZWToWwO4uk/28Kqvqt9L92h5EU2z4WR9I6xcQaPIBmkJPINIARU4LWQnvaOfgFhRbpwBMveTfh8/jM97lPg== +xterm@3.15.0-beta101: + version "3.15.0-beta101" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta101.tgz#38ffa0df5a3e9bdcb1818e74fe59b2f98b0fff69" + integrity sha512-HRa7+FDqQ8iWBTvb1Ni+uMGILnu6k9mF7JHMHRHfWxFoQlSoGYCyfdyXlJjk68YN8GsEQREmrII6cPLiQizdEQ== yauzl@^2.9.2: version "2.10.0" diff --git a/src/typings/xterm.d.ts b/src/typings/xterm.d.ts index 663b997022ba6..df7f20e233e68 100644 --- a/src/typings/xterm.d.ts +++ b/src/typings/xterm.d.ts @@ -207,47 +207,47 @@ declare module 'xterm' { */ export interface ITheme { /** The default foreground color */ - foreground?: string, + foreground?: string; /** The default background color */ - background?: string, + background?: string; /** The cursor color */ - cursor?: string, + cursor?: string; /** The accent color of the cursor (fg color for a block cursor) */ - cursorAccent?: string, + cursorAccent?: string; /** The selection background color (can be transparent) */ - selection?: string, + selection?: string; /** ANSI black (eg. `\x1b[30m`) */ - black?: string, + black?: string; /** ANSI red (eg. `\x1b[31m`) */ - red?: string, + red?: string; /** ANSI green (eg. `\x1b[32m`) */ - green?: string, + green?: string; /** ANSI yellow (eg. `\x1b[33m`) */ - yellow?: string, + yellow?: string; /** ANSI blue (eg. `\x1b[34m`) */ - blue?: string, + blue?: string; /** ANSI magenta (eg. `\x1b[35m`) */ - magenta?: string, + magenta?: string; /** ANSI cyan (eg. `\x1b[36m`) */ - cyan?: string, + cyan?: string; /** ANSI white (eg. `\x1b[37m`) */ - white?: string, + white?: string; /** ANSI bright black (eg. `\x1b[1;30m`) */ - brightBlack?: string, + brightBlack?: string; /** ANSI bright red (eg. `\x1b[1;31m`) */ - brightRed?: string, + brightRed?: string; /** ANSI bright green (eg. `\x1b[1;32m`) */ - brightGreen?: string, + brightGreen?: string; /** ANSI bright yellow (eg. `\x1b[1;33m`) */ - brightYellow?: string, + brightYellow?: string; /** ANSI bright blue (eg. `\x1b[1;34m`) */ - brightBlue?: string, + brightBlue?: string; /** ANSI bright magenta (eg. `\x1b[1;35m`) */ - brightMagenta?: string, + brightMagenta?: string; /** ANSI bright cyan (eg. `\x1b[1;36m`) */ - brightCyan?: string, + brightCyan?: string; /** ANSI bright white (eg. `\x1b[1;37m`) */ - brightWhite?: string + brightWhite?: string; } /** @@ -386,6 +386,12 @@ declare module 'xterm' { */ readonly markers: ReadonlyArray; + /** + * (EXPERIMENTAL) Get the parser interface to register + * custom escape sequence handlers. + */ + readonly parser: IParser; + /** * Natural language strings that can be localized. */ @@ -500,32 +506,6 @@ declare module 'xterm' { */ attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void; - /** - * (EXPERIMENTAL) Adds a handler for CSI escape sequences. - * @param flag The flag should be one-character string, which specifies the - * final character (e.g "m" for SGR) of the CSI sequence. - * @param callback The function to handle the escape sequence. The callback - * is called with the numerical params, as well as the special characters - * (e.g. "$" for DECSCPP). If the sequence has subparams the array will - * contain subarrays with their numercial values. - * Return true if the sequence was handled; false if - * we should try a previous handler (set by addCsiHandler or setCsiHandler). - * The most recently-added handler is tried first. - * @return An IDisposable you can call to remove this handler. - */ - addCsiHandler(flag: string, callback: (params: (number | number[])[], collect: string) => boolean): IDisposable; - - /** - * (EXPERIMENTAL) Adds a handler for OSC escape sequences. - * @param ident The number (first parameter) of the sequence. - * @param callback The function to handle the escape sequence. The callback - * is called with OSC data string. Return true if the sequence was handled; - * false if we should try a previous handler (set by addOscHandler or - * setOscHandler). The most recently-added handler is tried first. - * @return An IDisposable you can call to remove this handler. - */ - addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; - /** * (EXPERIMENTAL) Registers a link matcher, allowing custom link patterns to * be matched and handled. @@ -689,6 +669,12 @@ declare module 'xterm' { */ writeUtf8(data: Uint8Array): void; + /** + * Writes text to the terminal, performing the necessary transformations for pasted text. + * @param data The text to write to the terminal. + */ + paste(data: string): void; + /** * Retrieves an option's value from the terminal. * @param key The option key. @@ -804,18 +790,10 @@ declare module 'xterm' { /** * Perform a full reset (RIS, aka '\x1bc'). */ - reset(): void - - /** - * Applies an addon to the Terminal prototype, making it available to all - * newly created Terminals. - * @param addon The addon to apply. - * @deprecated Use the new loadAddon API/addon format. - */ - static applyAddon(addon: any): void; + reset(): void; /** - * (EXPERIMENTAL) Loads an addon into this instance of xterm.js. + * Loads an addon into this instance of xterm.js. * @param addon The addon to load. */ loadAddon(addon: ITerminalAddon): void; @@ -951,6 +929,119 @@ declare module 'xterm' { */ readonly width: number; } + + /** + * (EXPERIMENTAL) Data type to register a CSI, DCS or ESC callback in the parser + * in the form: + * ESC I..I F + * CSI Prefix P..P I..I F + * DCS Prefix P..P I..I F data_bytes ST + * + * with these rules/restrictions: + * - prefix can only be used with CSI and DCS + * - only one leading prefix byte is recognized by the parser + * before any other parameter bytes (P..P) + * - intermediate bytes are recognized up to 2 + * + * For custom sequences make sure to read ECMA-48 and the resources at + * vt100.net to not clash with existing sequences or reserved address space. + * General recommendations: + * - use private address space (see ECMA-48) + * - use max one intermediate byte (technically not limited by the spec, + * in practice there are no sequences with more than one intermediate byte, + * thus parsers might get confused with more intermediates) + * - test against other common emulators to check whether they escape/ignore + * the sequence correctly + * + * Notes: OSC command registration is handled differently (see addOscHandler) + * APC, PM or SOS is currently not supported. + */ + export interface IFunctionIdentifier { + /** + * Optional prefix byte, must be in range \x3c .. \x3f. + * Usable in CSI and DCS. + */ + prefix?: string; + /** + * Optional intermediate bytes, must be in range \x20 .. \x2f. + * Usable in CSI, DCS and ESC. + */ + intermediates?: string; + /** + * Final byte, must be in range \x40 .. \x7e for CSI and DCS, + * \x30 .. \x7e for ESC. + */ + final: string; + } + + /** + * (EXPERIMENTAL) Parser interface. + */ + export interface IParser { + /** + * Adds a handler for CSI escape sequences. + * @param id Specifies the function identifier under which the callback + * gets registered, e.g. {final: 'm'} for SGR. + * @param callback The function to handle the sequence. The callback is + * called with the numerical params. If the sequence has subparams the + * array will contain subarrays with their numercial values. + * Return true if the sequence was handled; false if we should try + * a previous handler (set by addCsiHandler or setCsiHandler). + * The most recently-added handler is tried first. + * @return An IDisposable you can call to remove this handler. + */ + addCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable; + + /** + * Adds a handler for DCS escape sequences. + * @param id Specifies the function identifier under which the callback + * gets registered, e.g. {intermediates: '$' final: 'q'} for DECRQSS. + * @param callback The function to handle the sequence. Note that the + * function will only be called once if the sequence finished sucessfully. + * There is currently no way to intercept smaller data chunks, data chunks + * will be stored up until the sequence is finished. Since DCS sequences + * are not limited by the amount of data this might impose a problem for + * big payloads. Currently xterm.js limits DCS payload to 10 MB + * which should give enough room for most use cases. + * The function gets the payload and numerical parameters as arguments. + * Return true if the sequence was handled; false if we should try + * a previous handler (set by addDcsHandler or setDcsHandler). + * The most recently-added handler is tried first. + * @return An IDisposable you can call to remove this handler. + */ + addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable; + + /** + * Adds a handler for ESC escape sequences. + * @param id Specifies the function identifier under which the callback + * gets registered, e.g. {intermediates: '%' final: 'G'} for + * default charset selection. + * @param callback The function to handle the sequence. + * Return true if the sequence was handled; false if we should try + * a previous handler (set by addEscHandler or setEscHandler). + * The most recently-added handler is tried first. + * @return An IDisposable you can call to remove this handler. + */ + addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable; + + /** + * Adds a handler for OSC escape sequences. + * @param ident The number (first parameter) of the sequence. + * @param callback The function to handle the sequence. Note that the + * function will only be called once if the sequence finished sucessfully. + * There is currently no way to intercept smaller data chunks, data chunks + * will be stored up until the sequence is finished. Since OSC sequences + * are not limited by the amount of data this might impose a problem for + * big payloads. Currently xterm.js limits OSC payload to 10 MB + * which should give enough room for most use cases. + * The callback is called with OSC data string. + * Return true if the sequence was handled; false if we should try + * a previous handler (set by addOscHandler or setOscHandler). + * The most recently-added handler is tried first. + * @return An IDisposable you can call to remove this handler. + */ + addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; + } } @@ -987,4 +1078,4 @@ declare module 'xterm' { interface Terminal { _core: TerminalCore; } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 90baeda9809a3..76486ff3f8362 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10002,10 +10002,10 @@ xterm-addon-web-links@0.1.0-beta10: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.1.0-beta10.tgz#610fa9773a2a5ccd41c1c83ba0e2dd2c9eb66a23" integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg== -xterm@3.15.0-beta99: - version "3.15.0-beta99" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta99.tgz#0010a7ea5d56cbb08a1e3a525b353c96a158e7a0" - integrity sha512-Vm0ZWToWwO4uk/28Kqvqt9L92h5EU2z4WR9I6xcQaPIBmkJPINIARU4LWQnvaOfgFhRbpwBMveTfh8/jM97lPg== +xterm@3.15.0-beta101: + version "3.15.0-beta101" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta101.tgz#38ffa0df5a3e9bdcb1818e74fe59b2f98b0fff69" + integrity sha512-HRa7+FDqQ8iWBTvb1Ni+uMGILnu6k9mF7JHMHRHfWxFoQlSoGYCyfdyXlJjk68YN8GsEQREmrII6cPLiQizdEQ== y18n@^3.2.1: version "3.2.1" From 0ef277ee78a9382fa25ce77dc21078c562ec56cd Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 22 Aug 2019 09:24:03 -0700 Subject: [PATCH 540/613] Adopt new paste and addCsiHandler APIs Fixes #78999 --- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 9ab3aa2138440..c210bb9d05dc8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -500,7 +500,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Force line data to be sent when the cursor is moved, the main purpose for // this is because ConPTY will often not do a line feed but instead move the // cursor, in which case we still want to send the current line's data to tasks. - xterm.addCsiHandler('H', () => { + xterm.parser.addCsiHandler({ final: 'H' }, () => { this._onCursorMove(); return false; }); @@ -865,7 +865,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return; } this.focus(); - this._xterm._core._coreService.triggerDataEvent(await this._clipboardService.readText(), true); + this._xterm.paste(await this._clipboardService.readText()); } public write(text: string): void { From 878fe0bb9a11b65419ce898e1ccfbcd7f2999782 Mon Sep 17 00:00:00 2001 From: skprabhanjan Date: Thu, 22 Aug 2019 22:00:15 +0530 Subject: [PATCH 541/613] Fast return if doesConatinSpecialCharacter is false --- src/vs/base/common/search.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/base/common/search.ts b/src/vs/base/common/search.ts index 0812df76b6e32..743967964b65f 100644 --- a/src/vs/base/common/search.ts +++ b/src/vs/base/common/search.ts @@ -28,8 +28,7 @@ export function buildReplaceStringWithCasePreserved(matches: string[] | null, pa function validateSpecificSpecialCharacter(matches: string[], pattern: string, specialCharacter: string): boolean { const doesConatinSpecialCharacter = matches[0].indexOf(specialCharacter) !== -1 && pattern.indexOf(specialCharacter) !== -1; - const doesConatinSameNumberOfSpecialCharacters = matches[0].split(specialCharacter).length === pattern.split(specialCharacter).length; - return doesConatinSpecialCharacter && doesConatinSameNumberOfSpecialCharacters; + return doesConatinSpecialCharacter && matches[0].split(specialCharacter).length === pattern.split(specialCharacter).length; } function buildReplaceStringForSpecificSpecialCharacter(matches: string[], pattern: string, specialCharacter: string): string { From 18641cd58d5559d2c4ee3564be8b4080225faaf1 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 22 Aug 2019 09:42:32 -0700 Subject: [PATCH 542/613] Remove mention of env in automation shell Part of #78497 --- .../contrib/terminal/browser/terminal.contribution.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 73a8047c94026..db587f13986e4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -72,17 +72,17 @@ configurationRegistry.registerConfiguration({ type: 'object', properties: { 'terminal.integrated.automationShell.linux': { - markdownDescription: nls.localize('terminal.integrated.automationShell.linux', "A path that when set will override {0} and ignore {1} and {2} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.linux`', '`shellArgs`', '`env`'), + markdownDescription: nls.localize('terminal.integrated.automationShell.linux', "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.linux`', '`shellArgs`'), type: ['string', 'null'], default: null }, 'terminal.integrated.automationShell.osx': { - markdownDescription: nls.localize('terminal.integrated.automationShell.osx', "A path that when set will override {0} and ignore {1} and {2} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.osx`', '`shellArgs`', '`env`'), + markdownDescription: nls.localize('terminal.integrated.automationShell.osx', "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.osx`', '`shellArgs`'), type: ['string', 'null'], default: null }, 'terminal.integrated.automationShell.windows': { - markdownDescription: nls.localize('terminal.integrated.automationShell.windows', "A path that when set will override {0} and ignore {1} and {2} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.windows`', '`shellArgs`', '`env`'), + markdownDescription: nls.localize('terminal.integrated.automationShell.windows', "A path that when set will override {0} and ignore {1} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.windows`', '`shellArgs`'), type: ['string', 'null'], default: null }, From 3cabab6d6099e79d96e6afda9cdee81c321cd770 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 22 Aug 2019 09:47:45 -0700 Subject: [PATCH 543/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b7b3ce2bfe6f5..aff00157a8971 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "97a3da8fc0166ace25ffb8f21585fd58ba3305c8", + "distro": "5bc548e4d0a04371b933e269bf73a507e995fb02", "author": { "name": "Microsoft Corporation" }, From c445d5c408389bfda8af6e188474e77b2cb45af6 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Thu, 22 Aug 2019 10:36:11 -0700 Subject: [PATCH 544/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aff00157a8971..5cc5aff80763c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "5bc548e4d0a04371b933e269bf73a507e995fb02", + "distro": "5af2b9835ba8caee9e5057d2272ba4c90fcd0d36", "author": { "name": "Microsoft Corporation" }, From ccd6d203a064efb308ca1b1043ef2c87e327a2ac Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 22 Aug 2019 20:03:55 +0200 Subject: [PATCH 545/613] tslint - also show warning when using NodeJS type --- build/lib/tslint/noNodejsGlobalsRule.js | 1 + build/lib/tslint/noNodejsGlobalsRule.ts | 1 + src/vs/workbench/contrib/logs/common/logsDataCleaner.ts | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build/lib/tslint/noNodejsGlobalsRule.js b/build/lib/tslint/noNodejsGlobalsRule.js index 1e955a41e1b47..8c36fa342c273 100644 --- a/build/lib/tslint/noNodejsGlobalsRule.js +++ b/build/lib/tslint/noNodejsGlobalsRule.js @@ -26,6 +26,7 @@ class NoNodejsGlobalsRuleWalker extends abstractGlobalsRule_1.AbstractGlobalsRul getDisallowedGlobals() { // https://nodejs.org/api/globals.html#globals_global_objects return [ + "NodeJS", "Buffer", "__dirname", "__filename", diff --git a/build/lib/tslint/noNodejsGlobalsRule.ts b/build/lib/tslint/noNodejsGlobalsRule.ts index 48b229333e9e8..7e5767d857086 100644 --- a/build/lib/tslint/noNodejsGlobalsRule.ts +++ b/build/lib/tslint/noNodejsGlobalsRule.ts @@ -37,6 +37,7 @@ class NoNodejsGlobalsRuleWalker extends AbstractGlobalsRuleWalker { getDisallowedGlobals(): string[] { // https://nodejs.org/api/globals.html#globals_global_objects return [ + "NodeJS", "Buffer", "__dirname", "__filename", diff --git a/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts b/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts index 90d328690edc1..4f7faa55ebe6e 100644 --- a/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts +++ b/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts @@ -22,7 +22,7 @@ export class LogsDataCleaner extends Disposable { } private cleanUpOldLogsSoon(): void { - let handle: NodeJS.Timeout | undefined = setTimeout(async () => { + let handle: any = setTimeout(async () => { handle = undefined; const logsPath = URI.file(this.environmentService.logsPath).with({ scheme: this.environmentService.logFile.scheme }); const stat = await this.fileService.resolve(dirname(logsPath)); From 4454769affdb83e84721305835b300e943ca0d61 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Thu, 22 Aug 2019 14:26:42 -0700 Subject: [PATCH 546/613] Trust https://go.microsoft.com. Fix build --- src/vs/workbench/contrib/url/common/url.contribution.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 7d8a5f9e8fcf0..9882864d0efe3 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -50,14 +50,17 @@ Registry.as(ActionExtensions.WorkbenchActions).registe localize('developer', 'Developer') ); -const VSCODE_DOMAIN = 'https://code.visualstudio.com'; +const DEAFULT_TRUSTED_DOMAINS = [ + 'https://code.visualstudio.com', + 'https://go.microsoft.com' +]; const configureTrustedDomainsHandler = async ( quickInputService: IQuickInputService, storageService: IStorageService, domainToConfigure?: string ) => { - let trustedDomains: string[] = [VSCODE_DOMAIN]; + let trustedDomains: string[] = DEAFULT_TRUSTED_DOMAINS; try { const trustedDomainsSrc = storageService.get('http.trustedDomains', StorageScope.GLOBAL); @@ -158,7 +161,7 @@ class OpenerValidatorContributions implements IWorkbenchContribution { return true; } - let trustedDomains: string[] = [VSCODE_DOMAIN]; + let trustedDomains: string[] = DEAFULT_TRUSTED_DOMAINS; try { const trustedDomainsSrc = this._storageService.get('http.trustedDomains', StorageScope.GLOBAL); if (trustedDomainsSrc) { From 99970f4d12164005875d4df3a499142da84b69a7 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Thu, 22 Aug 2019 12:50:20 -0700 Subject: [PATCH 547/613] Validation for array-of-string settings. Fix #77459 --- .../browser/media/settingsEditor2.css | 4 + .../preferences/browser/settingsTree.ts | 81 +++++++++++++++---- .../preferences/common/preferencesModels.ts | 61 ++++++++++++++ .../test/common/preferencesModel.test.ts | 80 +++++++++++++++++- 4 files changed, 209 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 5e8238fde91b1..faf363f6a3eaa 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -369,6 +369,10 @@ margin-top: -1px; z-index: 1; } +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-item-contents.invalid-input .setting-item-validation-message { + position: static; + margin-top: 1rem; +} .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-text .setting-item-validation-message { width: 500px; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 82465a2262467..0964f8ef312ba 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -224,8 +224,9 @@ interface ISettingComplexItemTemplate extends ISettingItemTemplate { button: Button; } -interface ISettingListItemTemplate extends ISettingItemTemplate { +interface ISettingListItemTemplate extends ISettingItemTemplate { listWidget: ListSettingWidget; + validationErrorMessageElement: HTMLElement; } interface ISettingExcludeItemTemplate extends ISettingItemTemplate { @@ -679,6 +680,9 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr renderTemplate(container: HTMLElement): ISettingListItemTemplate { const common = this.renderCommonTemplate(null, container, 'list'); + const descriptionElement = common.containerElement.querySelector('.setting-item-description')!; + const validationErrorMessageElement = $('.setting-item-validation-message'); + descriptionElement.after(validationErrorMessageElement); const listWidget = this._instantiationService.createInstance(ListSettingWidget, common.controlElement); listWidget.domNode.classList.add(AbstractSettingRenderer.CONTROL_CLASS); @@ -686,19 +690,40 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr const template: ISettingListItemTemplate = { ...common, - listWidget + listWidget, + validationErrorMessageElement }; this.addSettingElementFocusHandler(template); - common.toDispose.push(listWidget.onDidChangeList(e => this.onDidChangeList(template, e))); + common.toDispose.push( + listWidget.onDidChangeList(e => { + const newList = this.computeNewList(template, e); + this.onDidChangeList(template, newList); + if (newList !== null && template.onChange) { + template.onChange(newList); + } + }) + ); return template; } - private onDidChangeList(template: ISettingListItemTemplate, e: IListChangeEvent): void { + private onDidChangeList(template: ISettingListItemTemplate, newList: string[] | undefined | null): void { + if (!template.context || newList === null) { + return; + } + + this._onDidChangeSetting.fire({ + key: template.context.setting.key, + value: newList, + type: template.context.valueType + }); + } + + private computeNewList(template: ISettingListItemTemplate, e: IListChangeEvent): string[] | undefined | null { if (template.context) { - let newValue: any[] = []; + let newValue: string[] = []; if (isArray(template.context.scopeValue)) { newValue = [...template.context.scopeValue]; } else if (isArray(template.context.value)) { @@ -732,29 +757,30 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr template.context.defaultValue.length === newValue.length && template.context.defaultValue.join() === newValue.join() ) { - return this._onDidChangeSetting.fire({ - key: template.context.setting.key, - value: undefined, // reset setting - type: template.context.valueType - }); + return undefined; } - this._onDidChangeSetting.fire({ - key: template.context.setting.key, - value: newValue, - type: template.context.valueType - }); + return newValue; } + + return undefined; } renderElement(element: ITreeNode, index: number, templateData: ISettingListItemTemplate): void { super.renderSettingElement(element, index, templateData); } - protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingListItemTemplate, onChange: (value: string) => void): void { + protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingListItemTemplate, onChange: (value: string[] | undefined) => void): void { const value = getListDisplayValue(dataElement); template.listWidget.setValue(value); template.context = dataElement; + + template.onChange = (v) => { + onChange(v); + renderArrayValidations(dataElement, template, v, false); + }; + + renderArrayValidations(dataElement, template, value.map(v => v.value), true); } } @@ -1237,6 +1263,29 @@ function renderValidations(dataElement: SettingsTreeSettingElement, template: IS DOM.removeClass(template.containerElement, 'invalid-input'); } +function renderArrayValidations( + dataElement: SettingsTreeSettingElement, + template: ISettingListItemTemplate, + value: string[] | undefined, + calledOnStartup: boolean +) { + DOM.addClass(template.containerElement, 'invalid-input'); + if (dataElement.setting.validator) { + const errMsg = dataElement.setting.validator(value); + if (errMsg && errMsg !== '') { + DOM.addClass(template.containerElement, 'invalid-input'); + template.validationErrorMessageElement.innerText = errMsg; + const validationError = localize('validationError', "Validation Error."); + template.containerElement.setAttribute('aria-label', [dataElement.setting.key, validationError, errMsg].join(' ')); + if (!calledOnStartup) { ariaAlert(validationError + ' ' + errMsg); } + return; + } else { + template.containerElement.setAttribute('aria-label', dataElement.setting.key); + DOM.removeClass(template.containerElement, 'invalid-input'); + } + } +} + function cleanRenderedMarkdown(element: Node): void { for (let i = 0; i < element.childNodes.length; i++) { const child = element.childNodes.item(i); diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 3e252282711e1..2f334c3ca5a51 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -1028,6 +1028,67 @@ class SettingsContentBuilder { } export function createValidator(prop: IConfigurationPropertySchema): (value: any) => (string | null) { + // Only for array of string + if (prop.type === 'array' && prop.items && !isArray(prop.items) && prop.items.type === 'string') { + const propItems = prop.items; + if (propItems && !isArray(propItems) && propItems.type === 'string') { + const withQuotes = (s: string) => `'` + s + `'`; + + return value => { + if (!value) { + return null; + } + + let message = ''; + + const stringArrayValue = value as string[]; + + if (prop.minItems && stringArrayValue.length < prop.minItems) { + message += nls.localize('validations.stringArrayMinItem', 'Array must have at least {0} items', prop.minItems); + message += '\n'; + } + + if (prop.maxItems && stringArrayValue.length > prop.maxItems) { + message += nls.localize('validations.stringArrayMaxItem', 'Array must have less than {0} items', prop.maxItems); + message += '\n'; + } + + if (typeof propItems.pattern === 'string') { + const patternRegex = new RegExp(propItems.pattern); + stringArrayValue.forEach(v => { + if (!patternRegex.test(v)) { + message += + propItems.patternErrorMessage || + nls.localize( + 'validations.stringArrayItemPattern', + 'Value {0} must match regex {1}.', + withQuotes(v), + withQuotes(propItems.pattern!) + ); + } + }); + } + + const propItemsEnum = propItems.enum; + if (propItemsEnum) { + stringArrayValue.forEach(v => { + if (propItemsEnum.indexOf(v) === -1) { + message += nls.localize( + 'validations.stringArrayItemEnum', + 'Value {0} is not one of {1}', + withQuotes(v), + '[' + propItemsEnum.map(withQuotes).join(', ') + ']' + ); + message += '\n'; + } + }); + } + + return message; + }; + } + } + return value => { let exclusiveMax: number | undefined; let exclusiveMin: number | undefined; diff --git a/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts b/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts index 555da0949aa12..b02e29c0c9615 100644 --- a/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts +++ b/src/vs/workbench/services/preferences/test/common/preferencesModel.test.ts @@ -250,4 +250,82 @@ suite('Preferences Model test', () => { withMessage.rejects(' ').withMessage('always error!'); withMessage.rejects('1').withMessage('always error!'); }); -}); \ No newline at end of file + + class ArrayTester { + private validator: (value: any) => string | null; + + constructor(private settings: IConfigurationPropertySchema) { + this.validator = createValidator(settings)!; + } + + public accepts(input: string[]) { + assert.equal(this.validator(input), '', `Expected ${JSON.stringify(this.settings)} to accept \`${JSON.stringify(input)}\`. Got ${this.validator(input)}.`); + } + + public rejects(input: any[]) { + assert.notEqual(this.validator(input), '', `Expected ${JSON.stringify(this.settings)} to reject \`${JSON.stringify(input)}\`.`); + return { + withMessage: + (message: string) => { + const actual = this.validator(input); + assert.ok(actual); + assert(actual!.indexOf(message) > -1, + `Expected error of ${JSON.stringify(this.settings)} on \`${input}\` to contain ${message}. Got ${this.validator(input)}.`); + } + }; + } + } + + test('simple array', () => { + { + const arr = new ArrayTester({ type: 'array', items: { type: 'string' } }); + arr.accepts([]); + arr.accepts(['foo']); + arr.accepts(['foo', 'bar']); + } + }); + + test('min-max items array', () => { + { + const arr = new ArrayTester({ type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 2 }); + arr.rejects([]).withMessage('Array must have at least 1 items'); + arr.accepts(['a']); + arr.accepts(['a', 'a']); + arr.rejects(['a', 'a', 'a']).withMessage('Array must have less than 2 items'); + } + }); + + test('array of enums', () => { + { + const arr = new ArrayTester({ type: 'array', items: { type: 'string', enum: ['a', 'b'] } }); + arr.accepts(['a']); + arr.accepts(['a', 'b']); + + arr.rejects(['c']).withMessage(`Value 'c' is not one of`); + arr.rejects(['a', 'c']).withMessage(`Value 'c' is not one of`); + + arr.rejects(['c', 'd']).withMessage(`Value 'c' is not one of`); + arr.rejects(['c', 'd']).withMessage(`Value 'd' is not one of`); + } + }); + + test('min-max and enum', () => { + const arr = new ArrayTester({ type: 'array', items: { type: 'string', enum: ['a', 'b'] }, minItems: 1, maxItems: 2 }); + + arr.rejects(['a', 'b', 'c']).withMessage('Array must have less than 2 items'); + arr.rejects(['a', 'b', 'c']).withMessage(`Value 'c' is not one of`); + }); + + test('pattern', () => { + const arr = new ArrayTester({ type: 'array', items: { type: 'string', pattern: '^(hello)*$' } }); + + arr.accepts(['hello']); + arr.rejects(['a']).withMessage(`Value 'a' must match regex`); + }); + + test('pattern with error message', () => { + const arr = new ArrayTester({ type: 'array', items: { type: 'string', pattern: '^(hello)*$', patternErrorMessage: 'err: must be friendly' } }); + + arr.rejects(['a']).withMessage(`err: must be friendly`); + }); +}); From 016a400b1b49806b21ffa820ae242f24228a1862 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Thu, 22 Aug 2019 16:42:32 -0700 Subject: [PATCH 548/613] Only do workspace stats computations if telemetry is enabled --- .../contrib/stats/electron-browser/workspaceStats.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts b/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts index 7457e3b6bcd10..e6ca4e0869bad 100644 --- a/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts +++ b/src/vs/workbench/contrib/stats/electron-browser/workspaceStats.ts @@ -147,7 +147,9 @@ export class WorkspaceStats implements IWorkbenchContribution { @ISharedProcessService private readonly sharedProcessService: ISharedProcessService, @IWorkspaceStatsService private readonly workspaceStatsService: IWorkspaceStatsService ) { - this.report(); + if (this.telemetryService.isOptedIn) { + this.report(); + } } private report(): void { From 263312b6ddb97a464bed0d53b55b473d8a7fab1e Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 22 Aug 2019 17:06:53 -0700 Subject: [PATCH 549/613] add reconnect now button --- src/vs/platform/progress/common/progress.ts | 1 + .../remote/common/remoteAgentConnection.ts | 28 +++++-- .../electron-browser/remote.contribution.ts | 78 ++++++++++++++----- .../progress/browser/progressService.ts | 44 +++++++++-- 4 files changed, 121 insertions(+), 30 deletions(-) diff --git a/src/vs/platform/progress/common/progress.ts b/src/vs/platform/progress/common/progress.ts index 4308c72ce89e2..cbb0c6068e65e 100644 --- a/src/vs/platform/progress/common/progress.ts +++ b/src/vs/platform/progress/common/progress.ts @@ -50,6 +50,7 @@ export interface IProgressOptions { source?: string; total?: number; cancellable?: boolean; + buttons?: string[]; } export interface IProgressNotificationOptions extends IProgressOptions { diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts index 5653a6912b2e0..7b6e68976816a 100644 --- a/src/vs/platform/remote/common/remoteAgentConnection.ts +++ b/src/vs/platform/remote/common/remoteAgentConnection.ts @@ -12,6 +12,7 @@ import { Emitter } from 'vs/base/common/event'; import { RemoteAuthorityResolverError } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { ISignService } from 'vs/platform/sign/common/sign'; +import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; export const enum ConnectionType { Management = 1, @@ -245,9 +246,15 @@ export async function connectRemoteAgentTunnel(options: IConnectionOptions, tunn return protocol; } -function sleep(seconds: number): Promise { - return new Promise((resolve, reject) => { - setTimeout(resolve, seconds * 1000); +function sleep(seconds: number): CancelablePromise { + return createCancelablePromise(token => { + return new Promise((resolve, reject) => { + const timeout = setTimeout(resolve, seconds * 1000); + token.onCancellationRequested(() => { + clearTimeout(timeout); + resolve(); + }); + }); }); } @@ -264,8 +271,13 @@ export class ConnectionLostEvent { export class ReconnectionWaitEvent { public readonly type = PersistentConnectionEventType.ReconnectionWait; constructor( - public readonly durationSeconds: number + public readonly durationSeconds: number, + private readonly cancellableTimer: CancelablePromise ) { } + + public skipWait(): void { + this.cancellableTimer.cancel(); + } } export class ReconnectionRunningEvent { public readonly type = PersistentConnectionEventType.ReconnectionRunning; @@ -330,8 +342,12 @@ abstract class PersistentConnection extends Disposable { attempt++; const waitTime = (attempt < TIMES.length ? TIMES[attempt] : TIMES[TIMES.length - 1]); try { - this._onDidStateChange.fire(new ReconnectionWaitEvent(waitTime)); - await sleep(waitTime); + const sleepPromise = sleep(waitTime); + this._onDidStateChange.fire(new ReconnectionWaitEvent(waitTime, sleepPromise)); + + try { + await sleepPromise; + } catch { } // User canceled timer // connection was lost, let's try to re-establish it this._onDidStateChange.fire(new ReconnectionRunningEvent()); diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index 00e601638ecf2..4c3db2da9aad0 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -30,7 +30,7 @@ import { ipcRenderer as ipc } from 'electron'; import { IDiagnosticInfoOptions, IRemoteDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IProgressService, IProgress, IProgressStep, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; +import { PersistentConnectionEventType, ReconnectionWaitEvent } from 'vs/platform/remote/common/remoteAgentConnection'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import Severity from 'vs/base/common/severity'; @@ -315,7 +315,58 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { if (connection) { let currentProgressPromiseResolve: (() => void) | null = null; let progressReporter: ProgressReporter | null = null; + let lastLocation: ProgressLocation | null = null; let currentTimer: ReconnectionTimer | null = null; + let reconnectWaitEvent: ReconnectionWaitEvent | null = null; + + function showProgress(location: ProgressLocation, buttons?: string[]) { + if (currentProgressPromiseResolve) { + currentProgressPromiseResolve(); + } + + const promise = new Promise((resolve) => currentProgressPromiseResolve = resolve); + lastLocation = location; + + if (location === ProgressLocation.Dialog) { + // Show dialog + progressService!.withProgress( + { location: ProgressLocation.Dialog, buttons }, + (progress) => { progressReporter = new ProgressReporter(progress); return promise; }, + (choice?) => { + // Handle choice from dialog + if (choice === 0 && buttons && reconnectWaitEvent) { + reconnectWaitEvent.skipWait(); + } else { + showProgress(ProgressLocation.Notification, buttons); + } + + progressReporter!.report(); + }); + } else { + // Show notification + progressService!.withProgress( + { location: ProgressLocation.Notification, buttons }, + (progress) => { if (progressReporter) { progressReporter.currentProgress = progress; } return promise; }, + (choice?) => { + // Handle choice from notification + if (choice === 0 && buttons && reconnectWaitEvent) { + reconnectWaitEvent.skipWait(); + progressReporter!.report(); + } else { + hideProgress(); + } + }); + } + } + + function hideProgress() { + if (currentProgressPromiseResolve) { + currentProgressPromiseResolve(); + } + + currentProgressPromiseResolve = null; + progressReporter = null; + } connection.onDidStateChange((e) => { if (currentTimer) { @@ -325,31 +376,24 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { switch (e.type) { case PersistentConnectionEventType.ConnectionLost: if (!currentProgressPromiseResolve) { - let promise = new Promise((resolve) => currentProgressPromiseResolve = resolve); - progressService!.withProgress( - { location: ProgressLocation.Dialog }, - (progress: IProgress | null) => { progressReporter = new ProgressReporter(progress!); return promise; }, - () => { - currentProgressPromiseResolve!(); - promise = new Promise((resolve) => currentProgressPromiseResolve = resolve); - progressService!.withProgress({ location: ProgressLocation.Notification }, (progress) => { if (progressReporter) { progressReporter.currentProgress = progress; } return promise; }); - progressReporter!.report(); - } - ); + showProgress(ProgressLocation.Dialog, [nls.localize('reconnectNow', "Reconnect Now")]); } progressReporter!.report(nls.localize('connectionLost', "Connection Lost")); break; case PersistentConnectionEventType.ReconnectionWait: + hideProgress(); + reconnectWaitEvent = e; + showProgress(lastLocation || ProgressLocation.Notification, [nls.localize('reconnectNow', "Reconnect Now")]); currentTimer = new ReconnectionTimer(progressReporter!, Date.now() + 1000 * e.durationSeconds); break; case PersistentConnectionEventType.ReconnectionRunning: + hideProgress(); + showProgress(lastLocation || ProgressLocation.Notification); progressReporter!.report(nls.localize('reconnectionRunning', "Attempting to reconnect...")); break; case PersistentConnectionEventType.ReconnectionPermanentFailure: - currentProgressPromiseResolve!(); - currentProgressPromiseResolve = null; - progressReporter = null; + hideProgress(); dialogService.show(Severity.Error, nls.localize('reconnectionPermanentFailure', "Cannot reconnect. Please reload the window."), [nls.localize('reloadWindow', "Reload Window"), nls.localize('cancel', "Cancel")], { cancelId: 1 }).then(choice => { // Reload the window @@ -359,9 +403,7 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { }); break; case PersistentConnectionEventType.ConnectionGain: - currentProgressPromiseResolve!(); - currentProgressPromiseResolve = null; - progressReporter = null; + hideProgress(); break; } }); diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index 465920cd05ded..d4cef4f51f4dd 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -46,7 +46,7 @@ export class ProgressService extends Disposable implements IProgressService { super(); } - withProgress(options: IProgressOptions, task: (progress: IProgress) => Promise, onDidCancel?: () => void): Promise { + withProgress(options: IProgressOptions, task: (progress: IProgress) => Promise, onDidCancel?: (choice?: number) => void): Promise { const { location } = options; if (typeof location === 'string') { if (this.viewletService.getProgressIndicator(location)) { @@ -142,7 +142,7 @@ export class ProgressService extends Disposable implements IProgressService { } } - private withNotificationProgress

, R = unknown>(options: IProgressNotificationOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P { + private withNotificationProgress

, R = unknown>(options: IProgressNotificationOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: (choice?: number) => void): P { const toDispose = new DisposableStore(); const createNotification = (message: string | undefined, increment?: number): INotificationHandle | undefined => { @@ -152,6 +152,29 @@ export class ProgressService extends Disposable implements IProgressService { const primaryActions = options.primaryActions ? Array.from(options.primaryActions) : []; const secondaryActions = options.secondaryActions ? Array.from(options.secondaryActions) : []; + + if (options.buttons) { + options.buttons.forEach((button, index) => { + const buttonAction = new class extends Action { + constructor() { + super(`progress.button.${button}`, button, undefined, true); + } + + run(): Promise { + if (typeof onDidCancel === 'function') { + onDidCancel(index); + } + + return Promise.resolve(undefined); + } + }; + + toDispose.add(buttonAction); + + primaryActions.push(buttonAction); + }); + } + if (options.cancellable) { const cancelAction = new class extends Action { constructor() { @@ -182,6 +205,10 @@ export class ProgressService extends Disposable implements IProgressService { updateProgress(handle, increment); Event.once(handle.onDidClose)(() => { + if (typeof onDidCancel === 'function') { + onDidCancel(); + } + toDispose.dispose(); }); @@ -317,7 +344,7 @@ export class ProgressService extends Disposable implements IProgressService { return promise; } - private withDialogProgress

, R = unknown>(options: IProgressOptions, task: (progress: IProgress) => P, onDidCancel?: () => void): P { + private withDialogProgress

, R = unknown>(options: IProgressOptions, task: (progress: IProgress) => P, onDidCancel?: (choice?: number) => void): P { const disposables = new DisposableStore(); const allowableCommands = [ 'workbench.action.quit', @@ -327,12 +354,17 @@ export class ProgressService extends Disposable implements IProgressService { let dialog: Dialog; const createDialog = (message: string) => { + + const buttons = options.buttons || []; + buttons.push(options.cancellable ? localize('cancel', "Cancel") : localize('dismiss', "Dismiss")); + dialog = new Dialog( this.layoutService.container, message, - [options.cancellable ? localize('cancel', "Cancel") : localize('dismiss', "Dismiss")], + buttons, { type: 'pending', + cancelId: buttons.length - 1, keyEventProcessor: (event: StandardKeyboardEvent) => { const resolved = this.keybindingService.softDispatch(event, this.layoutService.container); if (resolved && resolved.commandId) { @@ -347,9 +379,9 @@ export class ProgressService extends Disposable implements IProgressService { disposables.add(dialog); disposables.add(attachDialogStyler(dialog, this.themeService)); - dialog.show().then(() => { + dialog.show().then((dialogResult) => { if (typeof onDidCancel === 'function') { - onDidCancel(); + onDidCancel(dialogResult.button); } dispose(dialog); From 0f73473c08055054f317c1c94502f7f39fdbb164 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 22 Aug 2019 17:46:15 -0700 Subject: [PATCH 550/613] Add deprecation warning for rootPath #69335 --- src/vs/workbench/api/common/extHost.api.impl.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 0696b5ec82d12..09e2b5980b29f 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -541,6 +541,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // namespace: workspace const workspace: typeof vscode.workspace = { get rootPath() { + console.warn(`[Deprecation Warning] 'workspace.rootPath' is deprecated and should no longer be used. Please use 'workspace.workspaceFolders' instead. (${extension.publisher}.${extension.name})`); return extHostWorkspace.getPath(); }, set rootPath(value) { From 7fedec2e5d144c4071be66ed0938eb30b89b54c5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 23 Aug 2019 07:27:41 +0200 Subject: [PATCH 551/613] update distro --- package.json | 2 +- src/vs/platform/product/common/product.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 5cc5aff80763c..0add763ee4272 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "5af2b9835ba8caee9e5057d2272ba4c90fcd0d36", + "distro": "d54cb1c81bc4458276bd38093586ee3566539d2d", "author": { "name": "Microsoft Corporation" }, diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 0a3b1f75c89ff..7122abcc6f5b5 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -85,7 +85,6 @@ export interface IProductConfiguration { readonly 'linux-x64': string; readonly 'darwin': string; }; - readonly logUploaderUrl: string; readonly portable?: string; readonly uiExtensions?: readonly string[]; } From 01ca032a64107ae6e6d19f06197e4efd994f85a6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 23 Aug 2019 07:52:46 +0200 Subject: [PATCH 552/613] fix exploration merge --- build/azure-pipelines/exploration-build.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index 797c4b5fce028..7def5e60047ee 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -13,10 +13,20 @@ steps: inputs: versionSpec: "10.15.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - script: | set -e cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF git config user.email "vscode@microsoft.com" git config user.name "VSCode" From eb2c35c562374d7cc8ae3b4de736cb6057bf52bc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 23 Aug 2019 07:56:23 +0200 Subject: [PATCH 553/613] build - merge to electron-6.0.x automatically --- build/azure-pipelines/exploration-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index 7def5e60047ee..39a8f23b26218 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -31,10 +31,10 @@ steps: git config user.email "vscode@microsoft.com" git config user.name "VSCode" - git checkout origin/ben/electron-test + git checkout origin/electron-6.0.x git merge origin/master # Push master branch into exploration branch - git push origin HEAD:ben/electron-test + git push origin HEAD:electron-6.0.x displayName: Sync & Merge Exploration From ce32c3aab877a77b239898eef6bd5a085e71bf43 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Thu, 22 Aug 2019 23:07:30 -0700 Subject: [PATCH 554/613] Add a few more trusted domains --- src/vs/workbench/contrib/url/common/url.contribution.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 9882864d0efe3..af92998139da6 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -52,7 +52,10 @@ Registry.as(ActionExtensions.WorkbenchActions).registe const DEAFULT_TRUSTED_DOMAINS = [ 'https://code.visualstudio.com', - 'https://go.microsoft.com' + 'https://go.microsoft.com', + 'https://github.com', + 'https://marketplace.visualstudio.com', + 'https://vscode-auth.github.com' ]; const configureTrustedDomainsHandler = async ( From 8b0bc473cd51f38224f9e051b95c9ed869cbaca6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Aug 2019 08:09:34 +0200 Subject: [PATCH 555/613] CompletionItemKindModifier -> CompletionItemKindTag, #23927 --- src/vs/editor/common/modes.ts | 4 ++-- src/vs/editor/common/standalone/standaloneEnums.ts | 2 +- src/vs/editor/contrib/suggest/suggestWidget.ts | 4 ++-- .../editor/standalone/browser/standaloneLanguages.ts | 2 +- src/vs/monaco.d.ts | 4 ++-- src/vs/vscode.proposed.d.ts | 6 +++--- .../api/browser/mainThreadLanguageFeatures.ts | 2 +- src/vs/workbench/api/common/extHost.api.impl.ts | 2 +- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostLanguageFeatures.ts | 4 ++-- src/vs/workbench/api/common/extHostTypeConverters.ts | 10 +++++----- src/vs/workbench/api/common/extHostTypes.ts | 4 ++-- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 7060c53fdfcc4..9bf53989111b6 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -367,7 +367,7 @@ export let completionKindFromString: { }; })(); -export const enum CompletionItemKindModifier { +export const enum CompletionItemKindTag { Deprecated = 1 } @@ -404,7 +404,7 @@ export interface CompletionItem { * A modifier to the `kind` which affect how the item * is rendered, e.g. Deprecated is rendered with a strikeout */ - kindModifier?: Set; + kindTags?: Set; /** * A human-readable string with additional information * about this item, like type or symbol information. diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 4aa6084856207..a5f3bbe5802d4 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -581,7 +581,7 @@ export enum CompletionItemKind { Snippet = 25 } -export enum CompletionItemKindModifier { +export enum CompletionItemKindTag { Deprecated = 1 } diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index cdad5fdb234b6..cb0260ecc609a 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -30,7 +30,7 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; -import { CompletionItemKind, completionKindToCssClass, CompletionItemKindModifier } from 'vs/editor/common/modes'; +import { CompletionItemKind, completionKindToCssClass, CompletionItemKindTag } from 'vs/editor/common/modes'; import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -193,7 +193,7 @@ class Renderer implements IListRenderer ]; } - if (suggestion.kindModifier && suggestion.kindModifier.has(CompletionItemKindModifier.Deprecated)) { + if (suggestion.kindTags && suggestion.kindTags.has(CompletionItemKindTag.Deprecated)) { labelOptions.extraClasses = (labelOptions.extraClasses || []).concat(['deprecated']); labelOptions.matches = []; } diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index 0576af462b197..a8bada84fbf7b 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -562,7 +562,7 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages { // enums DocumentHighlightKind: standaloneEnums.DocumentHighlightKind, CompletionItemKind: standaloneEnums.CompletionItemKind, - CompletionItemKindModifier: standaloneEnums.CompletionItemKindModifier, + CompletionItemKindTag: standaloneEnums.CompletionItemKindTag, CompletionItemInsertTextRule: standaloneEnums.CompletionItemInsertTextRule, SymbolKind: standaloneEnums.SymbolKind, SymbolKindTag: standaloneEnums.SymbolKindTag, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 1d2b95c16942d..79c36b8b67e42 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4786,7 +4786,7 @@ declare namespace monaco.languages { Snippet = 25 } - export enum CompletionItemKindModifier { + export enum CompletionItemKindTag { Deprecated = 1 } @@ -4822,7 +4822,7 @@ declare namespace monaco.languages { * A modifier to the `kind` which affect how the item * is rendered, e.g. Deprecated is rendered with a strikeout */ - kindModifier?: Set; + kindTags?: Set; /** * A human-readable string with additional information * about this item, like type or symbol information. diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 8f2d4d5cc9446..cdc3ddaeb63db 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1138,9 +1138,9 @@ declare module 'vscode' { //#endregion - //#region Joh - CompletionItemKindModifier, https://github.com/microsoft/vscode/issues/23927 + //#region Joh - CompletionItemKindTag, https://github.com/microsoft/vscode/issues/23927 - export enum CompletionItemKindModifier { + export enum CompletionItemKindTag { Deprecated = 1 } @@ -1149,7 +1149,7 @@ declare module 'vscode' { /** * */ - kind2?: CompletionItemKind | { base: CompletionItemKind, modifier: ReadonlyArray }; + kind2?: CompletionItemKind | { kind: CompletionItemKind, tags: ReadonlyArray }; } //#endregion diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 0feaf4b35993d..cbc61e52916a4 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -331,7 +331,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha return { label: data.a, kind: data.b, - kindModifier: data.n && fromArray(data.n), + kindTags: data.n && fromArray(data.n), detail: data.c, documentation: data.d, sortText: data.e, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 09e2b5980b29f..752ebbb306504 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -808,7 +808,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I CommentMode: extHostTypes.CommentMode, CompletionItem: extHostTypes.CompletionItem, CompletionItemKind: extHostTypes.CompletionItemKind, - CompletionItemKindModifier: extHostTypes.CompletionItemKindModifier, + CompletionItemKindTag: extHostTypes.CompletionItemKindTag, CompletionList: extHostTypes.CompletionList, CompletionTriggerKind: extHostTypes.CompletionTriggerKind, ConfigurationTarget: extHostTypes.ConfigurationTarget, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d6ca16a738cb0..0d840a6c9db25 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -935,7 +935,7 @@ export interface ISuggestDataDto { k/* commitCharacters */?: string[]; l/* additionalTextEdits */?: ISingleEditOperation[]; m/* command */?: modes.Command; - n/* kindModifier */?: modes.CompletionItemKindModifier[]; + n/* kindModifier */?: modes.CompletionItemKindTag[]; // not-standard x?: ChainedCacheId; } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index a39d24d756a41..f31350abe8c77 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -741,8 +741,8 @@ class SuggestAdapter { // kind2 if (typeof item.kind2 === 'object') { - result.b = typeConvert.CompletionItemKind.from(item.kind2.base); - result.n = item.kind2.modifier.map(typeConvert.CompletionItemKindModifier.from); + result.b = typeConvert.CompletionItemKind.from(item.kind2.kind); + result.n = item.kind2.tags.map(typeConvert.CompletionItemKindTag.from); } // 'insertText'-logic diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 0a3819ccd832b..47d58850a586d 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -682,17 +682,17 @@ export namespace CompletionContext { } } -export namespace CompletionItemKindModifier { +export namespace CompletionItemKindTag { - export function from(kind: types.CompletionItemKindModifier): modes.CompletionItemKindModifier { + export function from(kind: types.CompletionItemKindTag): modes.CompletionItemKindTag { switch (kind) { - case types.CompletionItemKindModifier.Deprecated: return modes.CompletionItemKindModifier.Deprecated; + case types.CompletionItemKindTag.Deprecated: return modes.CompletionItemKindTag.Deprecated; } } - export function to(kind: modes.CompletionItemKindModifier): types.CompletionItemKindModifier { + export function to(kind: modes.CompletionItemKindTag): types.CompletionItemKindTag { switch (kind) { - case modes.CompletionItemKindModifier.Deprecated: return types.CompletionItemKindModifier.Deprecated; + case modes.CompletionItemKindTag.Deprecated: return types.CompletionItemKindTag.Deprecated; } } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 53031ca7ca117..285bf8f69fc64 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1308,7 +1308,7 @@ export enum CompletionItemKind { TypeParameter = 24 } -export enum CompletionItemKindModifier { +export enum CompletionItemKindTag { Deprecated = 1, } @@ -1317,7 +1317,7 @@ export class CompletionItem implements vscode.CompletionItem { label: string; kind?: CompletionItemKind; - kind2?: CompletionItemKind | { base: CompletionItemKind, modifier: CompletionItemKindModifier[] }; + kind2?: CompletionItemKind | { kind: CompletionItemKind, tags: CompletionItemKindTag[] }; detail?: string; documentation?: string | MarkdownString; sortText?: string; From f7fe25dcfc77582f7dc061e8467a4afa2022661a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Aug 2019 08:46:25 +0200 Subject: [PATCH 556/613] use ReadonlyArray for tags, #50972 --- src/vs/base/common/map.ts | 8 -------- src/vs/editor/common/modes.ts | 4 ++-- src/vs/editor/contrib/suggest/suggestWidget.ts | 2 +- src/vs/monaco.d.ts | 4 ++-- .../workbench/api/browser/mainThreadLanguageFeatures.ts | 3 +-- 5 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index eee70e8268410..277ab50bb88d9 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -8,14 +8,6 @@ import { CharCode } from 'vs/base/common/charCode'; import { Iterator, IteratorResult, FIN } from './iterator'; -export function fromArray(array: readonly T[]): Set { - const result = new Set(); - for (const element of array) { - result.add(element); - } - return result; -} - export function values(set: Set): V[]; export function values(map: Map): V[]; export function values(forEachable: { forEach(callback: (value: V, ...more: any[]) => any): void }): V[] { diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 9bf53989111b6..b5428b8840339 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -404,7 +404,7 @@ export interface CompletionItem { * A modifier to the `kind` which affect how the item * is rendered, e.g. Deprecated is rendered with a strikeout */ - kindTags?: Set; + kindTags?: ReadonlyArray; /** * A human-readable string with additional information * about this item, like type or symbol information. @@ -913,7 +913,7 @@ export interface DocumentSymbol { name: string; detail: string; kind: SymbolKind; - kindTags: SymbolKindTag[]; + kindTags: ReadonlyArray; containerName?: string; range: IRange; selectionRange: IRange; diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index cb0260ecc609a..8a0c10b205c4b 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -193,7 +193,7 @@ class Renderer implements IListRenderer ]; } - if (suggestion.kindTags && suggestion.kindTags.has(CompletionItemKindTag.Deprecated)) { + if (suggestion.kindTags && suggestion.kindTags.indexOf(CompletionItemKindTag.Deprecated) >= 0) { labelOptions.extraClasses = (labelOptions.extraClasses || []).concat(['deprecated']); labelOptions.matches = []; } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 79c36b8b67e42..b55c54c2aa4d9 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4822,7 +4822,7 @@ declare namespace monaco.languages { * A modifier to the `kind` which affect how the item * is rendered, e.g. Deprecated is rendered with a strikeout */ - kindTags?: Set; + kindTags?: ReadonlyArray; /** * A human-readable string with additional information * about this item, like type or symbol information. @@ -5240,7 +5240,7 @@ declare namespace monaco.languages { name: string; detail: string; kind: SymbolKind; - kindTags: SymbolKindTag[]; + kindTags: ReadonlyArray; containerName?: string; range: IRange; selectionRange: IRange; diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index cbc61e52916a4..d9c397890d062 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -21,7 +21,6 @@ import { Selection } from 'vs/editor/common/core/selection'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { mixin } from 'vs/base/common/objects'; -import { fromArray } from 'vs/base/common/map'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape { @@ -331,7 +330,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha return { label: data.a, kind: data.b, - kindTags: data.n && fromArray(data.n), + kindTags: data.n, detail: data.c, documentation: data.d, sortText: data.e, From 62c31b71542b996e2adc7455448ca0607420055e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Aug 2019 09:03:18 +0200 Subject: [PATCH 557/613] add SymbolTag, make tag a propertiy, #23927 --- src/vs/editor/common/modes.ts | 8 ++--- .../common/standalone/standaloneEnums.ts | 4 +-- .../contrib/documentSymbols/outlineTree.ts | 4 +-- .../documentSymbols/test/outlineModel.test.ts | 2 +- src/vs/editor/contrib/quickOpen/quickOpen.ts | 2 +- .../editor/contrib/suggest/suggestWidget.ts | 4 +-- .../standalone/browser/standaloneLanguages.ts | 4 +-- src/vs/monaco.d.ts | 8 ++--- src/vs/vscode.proposed.d.ts | 17 ++++++++-- .../api/browser/mainThreadLanguageFeatures.ts | 2 +- .../workbench/api/common/extHost.api.impl.ts | 3 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostLanguageFeatures.ts | 9 ++--- .../api/common/extHostTypeConverters.ts | 33 +++++++++++++++---- src/vs/workbench/api/common/extHostTypes.ts | 9 +++-- 15 files changed, 71 insertions(+), 40 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index b5428b8840339..cc2adefb59e04 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -367,7 +367,7 @@ export let completionKindFromString: { }; })(); -export const enum CompletionItemKindTag { +export const enum CompletionItemTag { Deprecated = 1 } @@ -404,7 +404,7 @@ export interface CompletionItem { * A modifier to the `kind` which affect how the item * is rendered, e.g. Deprecated is rendered with a strikeout */ - kindTags?: ReadonlyArray; + tags?: ReadonlyArray; /** * A human-readable string with additional information * about this item, like type or symbol information. @@ -867,7 +867,7 @@ export const enum SymbolKind { TypeParameter = 25 } -export const enum SymbolKindTag { +export const enum SymbolTag { Deprecated = 1, } @@ -913,7 +913,7 @@ export interface DocumentSymbol { name: string; detail: string; kind: SymbolKind; - kindTags: ReadonlyArray; + tags: ReadonlyArray; containerName?: string; range: IRange; selectionRange: IRange; diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index a5f3bbe5802d4..a705c132478b7 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -581,7 +581,7 @@ export enum CompletionItemKind { Snippet = 25 } -export enum CompletionItemKindTag { +export enum CompletionItemTag { Deprecated = 1 } @@ -662,6 +662,6 @@ export enum SymbolKind { TypeParameter = 25 } -export enum SymbolKindTag { +export enum SymbolTag { Deprecated = 1 } \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 6df7d87e2eece..84ac8bb704c61 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -12,7 +12,7 @@ import { createMatches, FuzzyScore } from 'vs/base/common/filters'; import 'vs/css!./media/outlineTree'; import 'vs/css!./media/symbol-icons'; import { Range } from 'vs/editor/common/core/range'; -import { SymbolKind, symbolKindToCssClass, SymbolKindTag } from 'vs/editor/common/modes'; +import { SymbolKind, symbolKindToCssClass, SymbolTag } from 'vs/editor/common/modes'; import { OutlineElement, OutlineGroup, OutlineModel } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { localize } from 'vs/nls'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; @@ -127,7 +127,7 @@ export class OutlineElementRenderer implements ITreeRenderer= 0) { + if (element.symbol.tags.indexOf(SymbolTag.Deprecated) >= 0) { options.extraClasses.push(`deprecated`); options.matches = []; } diff --git a/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts b/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts index 9622de2716e89..b41a7f8dd8f27 100644 --- a/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts +++ b/src/vs/editor/contrib/documentSymbols/test/outlineModel.test.ts @@ -76,7 +76,7 @@ suite('OutlineModel', function () { name, detail: 'fake', kind: SymbolKind.Boolean, - kindTags: [], + tags: [], selectionRange: range, range: range }; diff --git a/src/vs/editor/contrib/quickOpen/quickOpen.ts b/src/vs/editor/contrib/quickOpen/quickOpen.ts index 919e3eae79280..e1e14f108b3cf 100644 --- a/src/vs/editor/contrib/quickOpen/quickOpen.ts +++ b/src/vs/editor/contrib/quickOpen/quickOpen.ts @@ -51,7 +51,7 @@ function flatten(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideCo for (let entry of entries) { bucket.push({ kind: entry.kind, - kindTags: [], + tags: [], name: entry.name, detail: entry.detail, containerName: entry.containerName || overrideContainerLabel, diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 8a0c10b205c4b..889b27e0bfa01 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -30,7 +30,7 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; -import { CompletionItemKind, completionKindToCssClass, CompletionItemKindTag } from 'vs/editor/common/modes'; +import { CompletionItemKind, completionKindToCssClass, CompletionItemTag } from 'vs/editor/common/modes'; import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -193,7 +193,7 @@ class Renderer implements IListRenderer ]; } - if (suggestion.kindTags && suggestion.kindTags.indexOf(CompletionItemKindTag.Deprecated) >= 0) { + if (suggestion.tags && suggestion.tags.indexOf(CompletionItemTag.Deprecated) >= 0) { labelOptions.extraClasses = (labelOptions.extraClasses || []).concat(['deprecated']); labelOptions.matches = []; } diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index a8bada84fbf7b..1818394dfdc02 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -562,10 +562,10 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages { // enums DocumentHighlightKind: standaloneEnums.DocumentHighlightKind, CompletionItemKind: standaloneEnums.CompletionItemKind, - CompletionItemKindTag: standaloneEnums.CompletionItemKindTag, + CompletionItemTag: standaloneEnums.CompletionItemTag, CompletionItemInsertTextRule: standaloneEnums.CompletionItemInsertTextRule, SymbolKind: standaloneEnums.SymbolKind, - SymbolKindTag: standaloneEnums.SymbolKindTag, + SymbolTag: standaloneEnums.SymbolTag, IndentAction: standaloneEnums.IndentAction, CompletionTriggerKind: standaloneEnums.CompletionTriggerKind, SignatureHelpTriggerKind: standaloneEnums.SignatureHelpTriggerKind, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index b55c54c2aa4d9..4291958ce59f8 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4786,7 +4786,7 @@ declare namespace monaco.languages { Snippet = 25 } - export enum CompletionItemKindTag { + export enum CompletionItemTag { Deprecated = 1 } @@ -4822,7 +4822,7 @@ declare namespace monaco.languages { * A modifier to the `kind` which affect how the item * is rendered, e.g. Deprecated is rendered with a strikeout */ - kindTags?: ReadonlyArray; + tags?: ReadonlyArray; /** * A human-readable string with additional information * about this item, like type or symbol information. @@ -5232,7 +5232,7 @@ declare namespace monaco.languages { TypeParameter = 25 } - export enum SymbolKindTag { + export enum SymbolTag { Deprecated = 1 } @@ -5240,7 +5240,7 @@ declare namespace monaco.languages { name: string; detail: string; kind: SymbolKind; - kindTags: ReadonlyArray; + tags: ReadonlyArray; containerName?: string; range: IRange; selectionRange: IRange; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index cdc3ddaeb63db..42c0642ec1cbc 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1138,9 +1138,20 @@ declare module 'vscode' { //#endregion - //#region Joh - CompletionItemKindTag, https://github.com/microsoft/vscode/issues/23927 + //#region Joh - CompletionItemTag, https://github.com/microsoft/vscode/issues/23927 - export enum CompletionItemKindTag { + export enum SymbolTag { + Deprecated = 1 + } + + export interface DocumentSymbol { + /** + * + */ + tags?: ReadonlyArray; + } + + export enum CompletionItemTag { Deprecated = 1 } @@ -1149,7 +1160,7 @@ declare module 'vscode' { /** * */ - kind2?: CompletionItemKind | { kind: CompletionItemKind, tags: ReadonlyArray }; + tags?: ReadonlyArray; } //#endregion diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index d9c397890d062..966a740a4f112 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -330,7 +330,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha return { label: data.a, kind: data.b, - kindTags: data.n, + tags: data.n, detail: data.c, documentation: data.d, sortText: data.e, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 752ebbb306504..9b3fbf50e0bf3 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -808,7 +808,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I CommentMode: extHostTypes.CommentMode, CompletionItem: extHostTypes.CompletionItem, CompletionItemKind: extHostTypes.CompletionItemKind, - CompletionItemKindTag: extHostTypes.CompletionItemKindTag, + CompletionItemTag: extHostTypes.CompletionItemTag, CompletionList: extHostTypes.CompletionList, CompletionTriggerKind: extHostTypes.CompletionTriggerKind, ConfigurationTarget: extHostTypes.ConfigurationTarget, @@ -863,6 +863,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I StatusBarAlignment: extHostTypes.StatusBarAlignment, SymbolInformation: extHostTypes.SymbolInformation, SymbolKind: extHostTypes.SymbolKind, + SymbolTag: extHostTypes.SymbolTag, Task: extHostTypes.Task, Task2: extHostTypes.Task, TaskGroup: extHostTypes.TaskGroup, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 0d840a6c9db25..01955cf7dfad9 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -935,7 +935,7 @@ export interface ISuggestDataDto { k/* commitCharacters */?: string[]; l/* additionalTextEdits */?: ISingleEditOperation[]; m/* command */?: modes.Command; - n/* kindModifier */?: modes.CompletionItemKindTag[]; + n/* kindModifier */?: modes.CompletionItemTag[]; // not-standard x?: ChainedCacheId; } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index f31350abe8c77..bcd098ec1b269 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -70,7 +70,7 @@ class DocumentSymbolAdapter { const element = { name: info.name || '!!MISSING: name!!', kind: typeConvert.SymbolKind.from(info.kind), - kindTags: [], + tags: [], detail: undefined!, // Strict null override — avoid changing behavior containerName: info.containerName, range: typeConvert.Range.from(info.location.range), @@ -728,6 +728,7 @@ class SuggestAdapter { // a: item.label, b: typeConvert.CompletionItemKind.from(item.kind), + n: item.tags && item.tags.map(typeConvert.CompletionItemTag.from), c: item.detail, d: typeof item.documentation === 'undefined' ? undefined : typeConvert.MarkdownString.fromStrict(item.documentation), e: item.sortText, @@ -739,12 +740,6 @@ class SuggestAdapter { m: this._commands.toInternal(item.command, disposables), }; - // kind2 - if (typeof item.kind2 === 'object') { - result.b = typeConvert.CompletionItemKind.from(item.kind2.kind); - result.n = item.kind2.tags.map(typeConvert.CompletionItemKindTag.from); - } - // 'insertText'-logic if (item.textEdit) { result.h = item.textEdit.newText; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 47d58850a586d..7f8d0ea38b4c4 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -28,7 +28,7 @@ import * as marked from 'vs/base/common/marked/marked'; import { parse } from 'vs/base/common/marshalling'; import { cloneAndChange } from 'vs/base/common/objects'; import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log'; -import { coalesce } from 'vs/base/common/arrays'; +import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays'; import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; export interface PositionLike { @@ -556,6 +556,21 @@ export namespace SymbolKind { } } +export namespace SymbolTag { + + export function from(kind: types.SymbolTag): modes.SymbolTag { + switch (kind) { + case types.SymbolTag.Deprecated: return modes.SymbolTag.Deprecated; + } + } + + export function to(kind: modes.SymbolTag): types.SymbolTag { + switch (kind) { + case modes.SymbolTag.Deprecated: return types.SymbolTag.Deprecated; + } + } +} + export namespace WorkspaceSymbol { export function from(info: vscode.SymbolInformation): search.IWorkspaceSymbol { return { @@ -583,7 +598,7 @@ export namespace DocumentSymbol { range: Range.from(info.range), selectionRange: Range.from(info.selectionRange), kind: SymbolKind.from(info.kind), - kindTags: [] + tags: info.tags ? info.tags.map(SymbolTag.from) : [] }; if (info.children) { result.children = info.children.map(from); @@ -598,6 +613,9 @@ export namespace DocumentSymbol { Range.to(info.range), Range.to(info.selectionRange), ); + if (isNonEmptyArray(info.tags)) { + result.tags = info.tags.map(SymbolTag.to); + } if (info.children) { result.children = info.children.map(to) as any; } @@ -682,17 +700,17 @@ export namespace CompletionContext { } } -export namespace CompletionItemKindTag { +export namespace CompletionItemTag { - export function from(kind: types.CompletionItemKindTag): modes.CompletionItemKindTag { + export function from(kind: types.CompletionItemTag): modes.CompletionItemTag { switch (kind) { - case types.CompletionItemKindTag.Deprecated: return modes.CompletionItemKindTag.Deprecated; + case types.CompletionItemTag.Deprecated: return modes.CompletionItemTag.Deprecated; } } - export function to(kind: modes.CompletionItemKindTag): types.CompletionItemKindTag { + export function to(kind: modes.CompletionItemTag): types.CompletionItemTag { switch (kind) { - case modes.CompletionItemKindTag.Deprecated: return types.CompletionItemKindTag.Deprecated; + case modes.CompletionItemTag.Deprecated: return types.CompletionItemTag.Deprecated; } } } @@ -768,6 +786,7 @@ export namespace CompletionItem { const result = new types.CompletionItem(suggestion.label); result.insertText = suggestion.insertText; result.kind = CompletionItemKind.to(suggestion.kind); + result.tags = suggestion.tags && suggestion.tags.map(CompletionItemTag.to); result.detail = suggestion.detail; result.documentation = htmlContent.isMarkdownString(suggestion.documentation) ? MarkdownString.to(suggestion.documentation) : suggestion.documentation; result.sortText = suggestion.sortText; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 285bf8f69fc64..e14c4117f9e9e 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -979,6 +979,10 @@ export enum SymbolKind { TypeParameter = 25 } +export enum SymbolTag { + Deprecated = 1, +} + @es5ClassCompat export class SymbolInformation { @@ -1041,6 +1045,7 @@ export class DocumentSymbol { name: string; detail: string; kind: SymbolKind; + tags?: SymbolTag[]; range: Range; selectionRange: Range; children: DocumentSymbol[]; @@ -1308,7 +1313,7 @@ export enum CompletionItemKind { TypeParameter = 24 } -export enum CompletionItemKindTag { +export enum CompletionItemTag { Deprecated = 1, } @@ -1317,7 +1322,7 @@ export class CompletionItem implements vscode.CompletionItem { label: string; kind?: CompletionItemKind; - kind2?: CompletionItemKind | { kind: CompletionItemKind, tags: CompletionItemKindTag[] }; + tags?: CompletionItemTag[]; detail?: string; documentation?: string | MarkdownString; sortText?: string; From cc845dc5866158ca0d51bb8bf78b58de7460684e Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 23 Aug 2019 09:28:04 +0200 Subject: [PATCH 558/613] Update dotnet tasks template --- src/vs/workbench/contrib/tasks/common/taskTemplates.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/common/taskTemplates.ts b/src/vs/workbench/contrib/tasks/common/taskTemplates.ts index a0412d55268ce..c367ede9ab7da 100644 --- a/src/vs/workbench/contrib/tasks/common/taskTemplates.ts +++ b/src/vs/workbench/contrib/tasks/common/taskTemplates.ts @@ -27,9 +27,10 @@ const dotnetBuild: TaskEntry = { '\t"tasks": [', '\t\t{', '\t\t\t"label": "build",', - '\t\t\t"command": "dotnet build",', + '\t\t\t"command": "dotnet",', '\t\t\t"type": "shell",', '\t\t\t"args": [', + '\t\t\t\t"build",', '\t\t\t\t// Ask dotnet build to generate full paths for file names.', '\t\t\t\t"/property:GenerateFullPaths=true",', '\t\t\t\t// Do not generate summary otherwise it leads to duplicate errors in Problems panel', From 5f5fce58809fab2a3fb3e10cac191da9b76f5b87 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 23 Aug 2019 10:22:17 +0200 Subject: [PATCH 559/613] web - switch back to window.onbeforeunload as otherwise the page unload can no longer be prevented --- src/vs/platform/lifecycle/browser/lifecycleService.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/lifecycle/browser/lifecycleService.ts b/src/vs/platform/lifecycle/browser/lifecycleService.ts index 79301acb261f6..bd34fc59e8ffc 100644 --- a/src/vs/platform/lifecycle/browser/lifecycleService.ts +++ b/src/vs/platform/lifecycle/browser/lifecycleService.ts @@ -8,7 +8,6 @@ import { ILogService } from 'vs/platform/log/common/log'; import { AbstractLifecycleService } from 'vs/platform/lifecycle/common/lifecycleService'; import { localize } from 'vs/nls'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { addDisposableListener, EventType } from 'vs/base/browser/dom'; export class BrowserLifecycleService extends AbstractLifecycleService { @@ -23,7 +22,9 @@ export class BrowserLifecycleService extends AbstractLifecycleService { } private registerListeners(): void { - addDisposableListener(window, EventType.BEFORE_UNLOAD, () => this.onBeforeUnload()); + // Note: we cannot change this to window.addEventListener('beforeUnload') + // because it seems that mechanism does not allow for preventing the unload + window.onbeforeunload = () => this.onBeforeUnload(); } private onBeforeUnload(): string | null { From 87c7042e1d94dd5ed78f543fc319cf9ef48a8c89 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 23 Aug 2019 10:25:39 +0200 Subject: [PATCH 560/613] web - we need to prevent unload when we detect a pending save or auto save because long running operations are not allowed --- .../workbench/services/textfile/browser/textFileService.ts | 6 +++++- .../services/textfile/common/textFileEditorModel.ts | 5 +++++ src/vs/workbench/services/textfile/common/textfiles.ts | 7 ++++++- .../services/textfile/test/textFileEditorModel.test.ts | 7 ++++++- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 3d2d91f5996d8..ed7f5273046b5 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService'; -import { ITextFileService, IResourceEncodings, IResourceEncoding } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, IResourceEncodings, IResourceEncoding, ModelState } from 'vs/workbench/services/textfile/common/textfiles'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; @@ -26,6 +26,10 @@ export class BrowserTextFileService extends TextFileService { } private doBeforeShutdownSync(): boolean { + if (this.models.getAll().some(model => model.hasState(ModelState.PENDING_SAVE) || model.hasState(ModelState.PENDING_AUTO_SAVE))) { + return true; // files are pending to be saved: veto + } + const dirtyResources = this.getDirty(); if (!dirtyResources.length) { return false; // no dirty: no veto diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 4fa61e19c8397..bd6332610d805 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -593,6 +593,9 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Create new save timer and store it for disposal as needed const handle = setTimeout(() => { + // Clear the timeout now that we are running + this.autoSaveDisposable.clear(); + // Only trigger save if the version id has not changed meanwhile if (versionId === this.versionId) { this.doSave(versionId, { reason: SaveReason.AUTO }); // Very important here to not return the promise because if the timeout promise is canceled it will bubble up the error otherwise - do not change @@ -941,6 +944,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.inOrphanMode; case ModelState.PENDING_SAVE: return this.saveSequentializer.hasPendingSave(); + case ModelState.PENDING_AUTO_SAVE: + return !!this.autoSaveDisposable.value; case ModelState.SAVED: return !this.dirty; } diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 02956fd1cdd18..4f770994393e6 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -248,10 +248,15 @@ export const enum ModelState { DIRTY, /** - * A model is transitioning from dirty to saved. + * A model is currently being saved but this operation has not completed yet. */ PENDING_SAVE, + /** + * A model is marked for being saved after a specific timeout. + */ + PENDING_AUTO_SAVE, + /** * A model is in conflict mode when changes cannot be saved because the * underlying file has changed. Models in conflict mode are always dirty. diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index b5609d42d322d..629e19c6b7705 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -52,6 +52,7 @@ suite('Files - TextFileEditorModel', () => { model.textEditorModel!.setValue('bar'); assert.ok(getLastModifiedTime(model) <= Date.now()); + assert.ok(model.hasState(ModelState.DIRTY)); let savedEvent = false; model.onDidStateChange(e => { @@ -60,9 +61,13 @@ suite('Files - TextFileEditorModel', () => { } }); - await model.save(); + const pendingSave = model.save(); + assert.ok(model.hasState(ModelState.PENDING_SAVE)); + + await pendingSave; assert.ok(model.getLastSaveAttemptTime() <= Date.now()); + assert.ok(model.hasState(ModelState.SAVED)); assert.ok(!model.isDirty()); assert.ok(savedEvent); From 043a06542d75a4ab42885cca9d3f072339105a60 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Aug 2019 10:25:15 +0200 Subject: [PATCH 561/613] add SymbolInformation.tags, render deprecated items in quick outline and workspace symbol search --- .../documentSymbols/media/outlineTree.css | 5 -- .../documentSymbols/media/symbol-icons.css | 5 ++ src/vs/editor/contrib/quickOpen/quickOpen.ts | 2 +- src/vs/vscode.proposed.d.ts | 7 +++ .../api/common/extHostLanguageFeatures.ts | 4 +- .../api/common/extHostTypeConverters.ts | 5 +- src/vs/workbench/api/common/extHostTypes.ts | 1 + .../quickopen/browser/gotoSymbolHandler.ts | 51 +++++++++---------- .../search/browser/openSymbolHandler.ts | 9 +++- .../workbench/contrib/search/common/search.ts | 3 +- 10 files changed, 53 insertions(+), 39 deletions(-) diff --git a/src/vs/editor/contrib/documentSymbols/media/outlineTree.css b/src/vs/editor/contrib/documentSymbols/media/outlineTree.css index 3d6454a1556e8..ebc2389a0e269 100644 --- a/src/vs/editor/contrib/documentSymbols/media/outlineTree.css +++ b/src/vs/editor/contrib/documentSymbols/media/outlineTree.css @@ -20,11 +20,6 @@ color: var(--outline-element-color); } -.monaco-list .outline-element .deprecated { - text-decoration: line-through; - opacity: 0.66; -} - .monaco-tree .monaco-tree-row.focused .outline-element .outline-element-detail { visibility: inherit; } diff --git a/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css b/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css index 4e312f7dfe6fe..0504a233a6388 100644 --- a/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css +++ b/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css @@ -3,6 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +.monaco-workbench .monaco-icon-label.deprecated { + text-decoration: line-through; + opacity: 0.66; +} + .monaco-workbench .symbol-icon.inline { background-position: left center; padding-left: 20px; diff --git a/src/vs/editor/contrib/quickOpen/quickOpen.ts b/src/vs/editor/contrib/quickOpen/quickOpen.ts index e1e14f108b3cf..fb054ae7f618c 100644 --- a/src/vs/editor/contrib/quickOpen/quickOpen.ts +++ b/src/vs/editor/contrib/quickOpen/quickOpen.ts @@ -51,7 +51,7 @@ function flatten(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideCo for (let entry of entries) { bucket.push({ kind: entry.kind, - tags: [], + tags: entry.tags, name: entry.name, detail: entry.detail, containerName: entry.containerName || overrideContainerLabel, diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 42c0642ec1cbc..4a15411821b46 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1144,6 +1144,13 @@ declare module 'vscode' { Deprecated = 1 } + export interface SymbolInformation { + /** + * + */ + tags?: ReadonlyArray; + } + export interface DocumentSymbol { /** * diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index bcd098ec1b269..6efdd4d5e8567 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -70,8 +70,8 @@ class DocumentSymbolAdapter { const element = { name: info.name || '!!MISSING: name!!', kind: typeConvert.SymbolKind.from(info.kind), - tags: [], - detail: undefined!, // Strict null override — avoid changing behavior + tags: info.tags && info.tags.map(typeConvert.SymbolTag.from), + detail: '', containerName: info.containerName, range: typeConvert.Range.from(info.location.range), selectionRange: typeConvert.Range.from(info.location.range), diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 7f8d0ea38b4c4..6a050937ddb7d 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -576,17 +576,20 @@ export namespace WorkspaceSymbol { return { name: info.name, kind: SymbolKind.from(info.kind), + tags: info.tags && info.tags.map(SymbolTag.from), containerName: info.containerName, location: location.from(info.location) }; } export function to(info: search.IWorkspaceSymbol): types.SymbolInformation { - return new types.SymbolInformation( + const result = new types.SymbolInformation( info.name, SymbolKind.to(info.kind), info.containerName, location.to(info.location) ); + result.tags = info.tags && info.tags.map(SymbolTag.to); + return result; } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index e14c4117f9e9e..4c92eddcaf79b 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -995,6 +995,7 @@ export class SymbolInformation { name: string; location!: Location; kind: SymbolKind; + tags?: SymbolTag[]; containerName: string | undefined; constructor(name: string, kind: SymbolKind, containerName: string | undefined, location: Location); diff --git a/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts index eef3c303d6c36..bafe2fdfe6167 100644 --- a/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts @@ -8,7 +8,7 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import * as strings from 'vs/base/common/strings'; import { IEntryRunContext, Mode, IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; -import { QuickOpenModel, IHighlight } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenHandler, EditorQuickOpenEntryGroup, QuickOpenAction } from 'vs/workbench/browser/quickopen'; import * as filters from 'vs/base/common/filters'; import { IEditor, IDiffEditorModel, IEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; @@ -16,15 +16,15 @@ import { IModelDecorationsChangeAccessor, OverviewRulerLane, IModelDeltaDecorati import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/quickOpen'; -import { DocumentSymbolProviderRegistry, DocumentSymbol, symbolKindToCssClass, SymbolKind } from 'vs/editor/common/modes'; +import { DocumentSymbolProviderRegistry, DocumentSymbol, symbolKindToCssClass, SymbolKind, SymbolTag } from 'vs/editor/common/modes'; import { IRange } from 'vs/editor/common/core/range'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry'; import { GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { asPromise } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; export const GOTO_SYMBOL_PREFIX = '@'; export const SCOPE_PREFIX = ':'; @@ -235,29 +235,20 @@ class OutlineModel extends QuickOpenModel { } class SymbolEntry extends EditorQuickOpenEntryGroup { - private editorService: IEditorService; - private index: number; - private name: string; - private kind: SymbolKind; - private icon: string; - private description: string; - private range: IRange; - private revealRange: IRange; - private handler: GotoSymbolHandler; - - constructor(index: number, name: string, kind: SymbolKind, description: string, icon: string, range: IRange, revealRange: IRange, highlights: IHighlight[], editorService: IEditorService, handler: GotoSymbolHandler) { - super(); - this.index = index; - this.name = name; - this.kind = kind; - this.icon = icon; - this.description = description; - this.range = range; - this.revealRange = revealRange || range; - this.setHighlights(highlights); - this.editorService = editorService; - this.handler = handler; + constructor( + private index: number, + private name: string, + private kind: SymbolKind, + private description: string, + private icon: string, + private deprecated: boolean, + private range: IRange, + private revealRange: IRange, + private editorService: IEditorService, + private handler: GotoSymbolHandler + ) { + super(); } getIndex(): number { @@ -276,6 +267,10 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { return this.icon; } + getLabelOptions(): IIconLabelValueOptions | undefined { + return this.deprecated ? { extraClasses: ['deprecated'] } : undefined; + } + getDescription(): string { return this.description; } @@ -479,8 +474,8 @@ export class GotoSymbolHandler extends QuickOpenHandler { // Add results.push(new SymbolEntry(i, - label, element.kind, description, `symbol-icon ${icon}`, - element.range, element.selectionRange, [], this.editorService, this + label, element.kind, description, `symbol-icon ${icon}`, element.tags && element.tags.indexOf(SymbolTag.Deprecated) >= 0, + element.range, element.selectionRange, this.editorService, this )); } @@ -504,7 +499,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { } if (model && types.isFunction((model).getLanguageIdentifier)) { - const entries = await asPromise(() => getDocumentSymbols(model, true, this.pendingOutlineRequest!.token)); + const entries = await getDocumentSymbols(model, true, this.pendingOutlineRequest!.token); return new OutlineModel(this.toQuickOpenEntries(entries)); } diff --git a/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts b/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts index 4a0a138d83b33..dfa1d48f15069 100644 --- a/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts +++ b/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts @@ -14,7 +14,7 @@ import * as filters from 'vs/base/common/filters'; import * as strings from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; -import { symbolKindToCssClass } from 'vs/editor/common/modes'; +import { symbolKindToCssClass, SymbolTag } from 'vs/editor/common/modes'; import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -25,6 +25,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Schemas } from 'vs/base/common/network'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; class SymbolEntry extends EditorQuickOpenEntry { private bearingResolve: Promise | undefined; @@ -65,6 +66,12 @@ class SymbolEntry extends EditorQuickOpenEntry { return symbolKindToCssClass(this.bearing.kind); } + getLabelOptions(): IIconLabelValueOptions | undefined { + return this.bearing.tags && this.bearing.tags.indexOf(SymbolTag.Deprecated) >= 0 + ? { extraClasses: ['deprecated'] } + : undefined; + } + getResource(): URI { return this.bearing.location.uri; } diff --git a/src/vs/workbench/contrib/search/common/search.ts b/src/vs/workbench/contrib/search/common/search.ts index 0f87a2f56f675..10e3f0938343a 100644 --- a/src/vs/workbench/contrib/search/common/search.ts +++ b/src/vs/workbench/contrib/search/common/search.ts @@ -6,7 +6,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ISearchConfiguration, ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; -import { SymbolKind, Location, ProviderResult } from 'vs/editor/common/modes'; +import { SymbolKind, Location, ProviderResult, SymbolTag } from 'vs/editor/common/modes'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; @@ -19,6 +19,7 @@ export interface IWorkspaceSymbol { name: string; containerName?: string; kind: SymbolKind; + tags?: SymbolTag[]; location: Location; } From 46993bce0607465764b170acdae73822528b15ca Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Aug 2019 10:28:14 +0200 Subject: [PATCH 562/613] disable failing test, #79692 --- .../terminalConfigHelper.test.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts index f1984496f23eb..0babdeccff0ac 100644 --- a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts @@ -16,14 +16,14 @@ suite('Workbench - TerminalConfigHelper', () => { fixture = document.body; }); - test('TerminalConfigHelper - getFont fontFamily', function () { - const configurationService = new TestConfigurationService(); - configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); - configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: 'bar' } }); - const configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); - configHelper.panelContainer = fixture; - assert.equal(configHelper.getFont().fontFamily, 'bar', 'terminal.integrated.fontFamily should be selected over editor.fontFamily'); - }); + // test('TerminalConfigHelper - getFont fontFamily', function () { + // const configurationService = new TestConfigurationService(); + // configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); + // configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: 'bar' } }); + // const configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); + // configHelper.panelContainer = fixture; + // assert.equal(configHelper.getFont().fontFamily, 'bar', 'terminal.integrated.fontFamily should be selected over editor.fontFamily'); + // }); test('TerminalConfigHelper - getFont fontFamily (Linux Fedora)', function () { const configurationService = new TestConfigurationService(); @@ -233,4 +233,4 @@ suite('Workbench - TerminalConfigHelper', () => { configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); -}); \ No newline at end of file +}); From 722f97208c21165e0830e08580ca350ffa506c7e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Aug 2019 11:01:16 +0200 Subject: [PATCH 563/613] fix #79694 --- .../contrib/codelens/codelensController.ts | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 1aed8453777cd..726133c3f7f6e 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -32,7 +32,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { private _currentCodeLensModel: CodeLensModel | undefined; private _modelChangeCounter: number = 0; private _currentResolveCodeLensSymbolsPromise: CancelablePromise | undefined; - private _detectVisibleLenses!: RunOnceScheduler; + private _detectVisibleLenses: RunOnceScheduler | undefined; constructor( private readonly _editor: editorBrowser.ICodeEditor, @@ -121,9 +121,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { } } - this._detectVisibleLenses = new RunOnceScheduler(() => { - this._onViewportChanged(); - }, 250); + const detectVisibleLenses = this._detectVisibleLenses = new RunOnceScheduler(() => this._onViewportChanged(), 250); const scheduler = new RunOnceScheduler(() => { const counterValue = ++this._modelChangeCounter; @@ -145,12 +143,12 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { // render lenses this._renderCodeLensSymbols(result); - this._detectVisibleLenses.schedule(); + detectVisibleLenses.schedule(); } }, onUnexpectedError); }, 250); this._localToDispose.add(scheduler); - this._localToDispose.add(this._detectVisibleLenses); + this._localToDispose.add(detectVisibleLenses); this._localToDispose.add(this._editor.onDidChangeModelContent(() => { this._editor.changeDecorations(decorationsAccessor => { this._editor.changeViewZones(viewZonesAccessor => { @@ -179,17 +177,17 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { }); // Compute new `visible` code lenses - this._detectVisibleLenses.schedule(); + detectVisibleLenses.schedule(); // Ask for all references again scheduler.schedule(); })); this._localToDispose.add(this._editor.onDidScrollChange(e => { if (e.scrollTopChanged && this._lenses.length > 0) { - this._detectVisibleLenses.schedule(); + detectVisibleLenses.schedule(); } })); this._localToDispose.add(this._editor.onDidLayoutChange(() => { - this._detectVisibleLenses.schedule(); + detectVisibleLenses.schedule(); })); this._localToDispose.add(toDisposable(() => { if (this._editor.getModel()) { @@ -281,7 +279,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { groupsIndex++; codeLensIndex++; } else { - this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, helper, viewZoneAccessor, () => this._detectVisibleLenses.schedule())); + this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule())); codeLensIndex++; groupsIndex++; } @@ -295,7 +293,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { // Create extra symbols while (groupsIndex < groups.length) { - this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, helper, viewZoneAccessor, () => this._detectVisibleLenses.schedule())); + this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule())); groupsIndex++; } From 21b1a576ac796e6731950a2a591e8284165c7905 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 23 Aug 2019 11:06:13 +0200 Subject: [PATCH 564/613] Always register a basic uri formatter for vscode-remote:/ --- .../remote/common/remote.contribution.ts | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 124fbfdbc9f2d..404f5a2a78d0f 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -7,8 +7,7 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { ILabelService } from 'vs/platform/label/common/label'; -import { isWeb, OperatingSystem } from 'vs/base/common/platform'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { OperatingSystem } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { IRemoteAgentService, RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService'; import { ILogService } from 'vs/platform/log/common/log'; @@ -49,28 +48,24 @@ export const VIEW_CONTAINER: ViewContainer = Registry.as { - if (remoteEnvironment) { - this.labelService.registerFormatter({ - scheme: Schemas.vscodeRemote, - authority: this.environmentService.configuration.remoteAuthority, - formatting: { - label: '${path}', - separator: remoteEnvironment.os === OperatingSystem.Windows ? '\\' : '/', - tildify: remoteEnvironment.os !== OperatingSystem.Windows, - normalizeDriveLetter: remoteEnvironment.os === OperatingSystem.Windows - } - }); - } - }); - } + this.remoteAgentService.getEnvironment().then(remoteEnvironment => { + if (remoteEnvironment) { + this.labelService.registerFormatter({ + scheme: Schemas.vscodeRemote, + formatting: { + label: '${path}', + separator: remoteEnvironment.os === OperatingSystem.Windows ? '\\' : '/', + tildify: remoteEnvironment.os !== OperatingSystem.Windows, + normalizeDriveLetter: remoteEnvironment.os === OperatingSystem.Windows + } + }); + } + }); } } From 202b31dc40037e82219ef1ff571d8da50f6a5eb6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 23 Aug 2019 11:23:21 +0200 Subject: [PATCH 565/613] web main :lipstick: --- src/vs/code/browser/workbench/workbench.js | 8 ++++-- src/vs/workbench/browser/web.main.ts | 31 ++++++++++++---------- src/vs/workbench/workbench.web.api.ts | 2 ++ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.js b/src/vs/code/browser/workbench/workbench.js index 5050cb4e5b42f..5687627518e66 100644 --- a/src/vs/code/browser/workbench/workbench.js +++ b/src/vs/code/browser/workbench/workbench.js @@ -3,11 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +//@ts-check 'use strict'; (function () { - require.config({ + /** @type any */ + const amdLoader = require; + + amdLoader.config({ baseUrl: `${window.location.origin}/static/out`, paths: { 'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`, @@ -20,7 +24,7 @@ } }); - require(['vs/workbench/workbench.web.api'], function (api) { + amdLoader(['vs/workbench/workbench.web.api'], function (api) { const options = JSON.parse(document.getElementById('vscode-workbench-web-configuration').getAttribute('data-settings')); api.create(document.body, options); diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 57192e3e1c8e6..04a72e9e91430 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -90,7 +90,7 @@ class CodeRendererMain extends Disposable { // Driver if (this.configuration.driver) { - registerWindowDriver().then(d => this._register(d)); + (async () => this._register(await registerWindowDriver()))(); } // Startup @@ -153,23 +153,24 @@ class CodeRendererMain extends Disposable { // Logger const indexedDBLogProvider = new IndexedDBLogProvider(logsPath.scheme); - indexedDBLogProvider.database.then( - () => fileService.registerProvider(logsPath.scheme, indexedDBLogProvider), - e => { + (async () => { + try { + await indexedDBLogProvider.database; + + fileService.registerProvider(logsPath.scheme, indexedDBLogProvider); + } catch (error) { (logService).info('Error while creating indexedDB log provider. Falling back to in-memory log provider.'); - (logService).error(e); + (logService).error(error); + fileService.registerProvider(logsPath.scheme, new InMemoryLogProvider(logsPath.scheme)); } - ).then(() => { + const consoleLogService = new ConsoleLogService(logService.getLevel()); const fileLogService = new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService); logService.logger = new MultiplexLogService([consoleLogService, fileLogService]); - }); - - // Static Extensions - const staticExtensions = new StaticExtensionsService(this.configuration.staticExtensions || []); - serviceCollection.set(IStaticExtensionsService, staticExtensions); + })(); + // User Data Provider let userDataProvider: IFileSystemProvider | undefined = this.configuration.userDataProvider; const connection = remoteAgentService.getConnection(); if (connection) { @@ -185,14 +186,16 @@ class CodeRendererMain extends Disposable { } } } - if (!userDataProvider) { userDataProvider = this._register(new InMemoryUserDataProvider()); } - - // User Data Provider fileService.registerProvider(Schemas.userData, userDataProvider); + // Static Extensions + const staticExtensions = new StaticExtensionsService(this.configuration.staticExtensions || []); + serviceCollection.set(IStaticExtensionsService, staticExtensions); + + // Long running services (workspace, config, storage) const services = await Promise.all([ this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, logService).then(service => { diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 844ff1da56a73..7f1da9f12035f 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/workbench/workbench.web.main'; +import 'vs/nls!vs/workbench/workbench.web.main'; +import 'vs/css!vs/workbench/workbench.web.main'; import { main } from 'vs/workbench/browser/web.main'; import { UriComponents } from 'vs/base/common/uri'; import { IFileSystemProvider } from 'vs/platform/files/common/files'; From b690e2f0d1554afd8e763aca77334e2e41a2cc31 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 23 Aug 2019 11:24:13 +0200 Subject: [PATCH 566/613] registerSingleton for IStaticExtensionsService --- src/vs/workbench/browser/web.main.ts | 5 ----- .../services/extensions/common/staticExtensions.ts | 12 +++++++++--- src/vs/workbench/workbench.common.main.ts | 1 + src/vs/workbench/workbench.desktop.main.ts | 2 -- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 04a72e9e91430..b006dbd6ea0ac 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -40,7 +40,6 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/theme/common/themeService'; import { InMemoryUserDataProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider'; import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; -import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { FileLogService } from 'vs/platform/log/common/fileLogService'; import { toLocalISOString } from 'vs/base/common/date'; @@ -191,10 +190,6 @@ class CodeRendererMain extends Disposable { } fileService.registerProvider(Schemas.userData, userDataProvider); - // Static Extensions - const staticExtensions = new StaticExtensionsService(this.configuration.staticExtensions || []); - serviceCollection.set(IStaticExtensionsService, staticExtensions); - // Long running services (workspace, config, storage) const services = await Promise.all([ this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, logService).then(service => { diff --git a/src/vs/workbench/services/extensions/common/staticExtensions.ts b/src/vs/workbench/services/extensions/common/staticExtensions.ts index 06f102534d1b0..1d5e3f57b05c3 100644 --- a/src/vs/workbench/services/extensions/common/staticExtensions.ts +++ b/src/vs/workbench/services/extensions/common/staticExtensions.ts @@ -4,8 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionDescription, IExtensionManifest, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { UriComponents, URI } from 'vs/base/common/uri'; +import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { URI } from 'vs/base/common/uri'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export const IStaticExtensionsService = createDecorator('IStaticExtensionsService'); @@ -20,7 +22,9 @@ export class StaticExtensionsService implements IStaticExtensionsService { private readonly _descriptions: IExtensionDescription[] = []; - constructor(staticExtensions: { packageJSON: IExtensionManifest, extensionLocation: UriComponents }[]) { + constructor(@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService) { + const staticExtensions = environmentService.options && Array.isArray(environmentService.options.staticExtensions) ? environmentService.options.staticExtensions : []; + this._descriptions = staticExtensions.map(data => { identifier: new ExtensionIdentifier(`${data.packageJSON.publisher}.${data.packageJSON.name}`), extensionLocation: URI.revive(data.extensionLocation), @@ -32,3 +36,5 @@ export class StaticExtensionsService implements IStaticExtensionsService { return this._descriptions; } } + +registerSingleton(IStaticExtensionsService, StaticExtensionsService, true); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 2fdb06fe05894..c2f7f1a958182 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -74,6 +74,7 @@ import 'vs/workbench/services/themes/browser/workbenchThemeService'; import 'vs/workbench/services/label/common/labelService'; import 'vs/workbench/services/extensionManagement/common/extensionEnablementService'; import 'vs/workbench/services/notification/common/notificationService'; +import 'vs/workbench/services/extensions/common/staticExtensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index a566d1d7900a3..27ef543f04065 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -70,7 +70,6 @@ import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { WorkspacesService } from 'vs/platform/workspaces/electron-browser/workspacesService'; import { IMenubarService } from 'vs/platform/menubar/node/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; -import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; registerSingleton(IClipboardService, ClipboardService, true); registerSingleton(IRequestService, RequestService, true); @@ -82,7 +81,6 @@ registerSingleton(IUpdateService, UpdateService); registerSingleton(IIssueService, IssueService); registerSingleton(IWorkspacesService, WorkspacesService); registerSingleton(IMenubarService, MenubarService); -registerSingleton(IStaticExtensionsService, class extends StaticExtensionsService { constructor() { super([]); } }); //#endregion From 4d312af6e0a5670a717ff2c7ac0a9353f8934160 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 23 Aug 2019 11:25:45 +0200 Subject: [PATCH 567/613] Normalize task problem path before replacing slashes Fixes #79578 --- src/vs/workbench/contrib/tasks/common/problemMatcher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/common/problemMatcher.ts b/src/vs/workbench/contrib/tasks/common/problemMatcher.ts index ec7c7bcbb96aa..73f8f8f218f16 100644 --- a/src/vs/workbench/contrib/tasks/common/problemMatcher.ts +++ b/src/vs/workbench/contrib/tasks/common/problemMatcher.ts @@ -216,11 +216,11 @@ export async function getResource(filename: string, matcher: ProblemMatcher, fil if (fullPath === undefined) { throw new Error('FileLocationKind is not actionable. Does the matcher have a filePrefix? This should never happen.'); } + fullPath = normalize(fullPath); fullPath = fullPath.replace(/\\/g, '/'); if (fullPath[0] !== '/') { fullPath = '/' + fullPath; } - fullPath = normalize(fullPath); if (matcher.uriProvider !== undefined) { return matcher.uriProvider(fullPath); } else { From 00b43d36f9ab69fabf7b97b27459dc11c700bd2e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 23 Aug 2019 11:28:33 +0200 Subject: [PATCH 568/613] build - schedule back at 7 --- build/azure-pipelines/product-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index a2fdc9e4ec684..f38f431182644 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -143,8 +143,8 @@ trigger: none pr: none schedules: -- cron: "10 5 * * Mon-Fri" - displayName: Mon-Fri at 7:10 +- cron: "0 5 * * Mon-Fri" + displayName: Mon-Fri at 7:00 branches: include: - master From b9ccfc4b3fa4fe32991e02f5767cfe093292717b Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 23 Aug 2019 12:17:41 +0200 Subject: [PATCH 569/613] labelService: getUriBasenameLabel --- .../standalone/browser/simpleServices.ts | 6 ++++++ src/vs/platform/label/common/label.ts | 1 + .../common/editor/untitledEditorInput.ts | 3 +-- .../files/common/editors/fileEditorInput.ts | 5 ++--- .../services/label/common/labelService.ts | 11 ++++++++++ .../services/label/test/label.test.ts | 21 +++++++++++++++++++ 6 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 54804eb3c0f93..ce860a9dbac5c 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -45,6 +45,7 @@ import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platf import { ILayoutService, IDimension } from 'vs/platform/layout/browser/layoutService'; import { SimpleServicesNLS } from 'vs/editor/common/standaloneStrings'; import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; +import { basename } from 'vs/base/common/path'; export class SimpleModel implements IResolvedTextEditorModel { @@ -656,6 +657,7 @@ export class SimpleBulkEditService implements IBulkEditService { } export class SimpleUriLabelService implements ILabelService { + _serviceBrand: any; private readonly _onDidRegisterFormatter = new Emitter(); @@ -668,6 +670,10 @@ export class SimpleUriLabelService implements ILabelService { return resource.path; } + getUriBasenameLabel(resource: URI): string { + return basename(resource.path); + } + public getWorkspaceLabel(workspace: IWorkspaceIdentifier | URI | IWorkspace, options?: { verbose: boolean; }): string { return ''; } diff --git a/src/vs/platform/label/common/label.ts b/src/vs/platform/label/common/label.ts index ffc20e10abb85..4565228e69032 100644 --- a/src/vs/platform/label/common/label.ts +++ b/src/vs/platform/label/common/label.ts @@ -21,6 +21,7 @@ export interface ILabelService { * If noPrefix is passed does not tildify the label and also does not prepand the root name for relative labels in a multi root scenario. */ getUriLabel(resource: URI, options?: { relative?: boolean, noPrefix?: boolean, endWithSeparator?: boolean }): string; + getUriBasenameLabel(resource: URI): string; getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string; getHostLabel(scheme: string, authority?: string): string; getSeparator(scheme: string, authority?: string): '/' | '\\'; diff --git a/src/vs/workbench/common/editor/untitledEditorInput.ts b/src/vs/workbench/common/editor/untitledEditorInput.ts index 561b5cb6f87f0..1a0af06b7a70b 100644 --- a/src/vs/workbench/common/editor/untitledEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledEditorInput.ts @@ -7,7 +7,6 @@ import { URI } from 'vs/base/common/uri'; import { suggestFilename } from 'vs/base/common/mime'; import { memoize } from 'vs/base/common/decorators'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; -import { basename } from 'vs/base/common/path'; import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult, Verbosity, IModeSupport } from 'vs/workbench/common/editor'; import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; @@ -64,7 +63,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport @memoize private get shortDescription(): string { - return basename(this.labelService.getUriLabel(dirname(this.resource))); + return this.labelService.getUriBasenameLabel(dirname(this.resource)); } @memoize diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 3aae29a21be2e..b95d1949bdf58 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -5,7 +5,6 @@ import { localize } from 'vs/nls'; import { memoize } from 'vs/base/common/decorators'; -import { basename } from 'vs/base/common/path'; import { dirname } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { EncodingMode, ConfirmResult, EditorInput, IFileEditorInput, ITextEditorModel, Verbosity, IRevertOptions } from 'vs/workbench/common/editor'; @@ -147,14 +146,14 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { getName(): string { if (!this.name) { - this.name = basename(this.labelService.getUriLabel(this.resource)); + this.name = this.labelService.getUriBasenameLabel(this.resource); } return this.decorateLabel(this.name); } private get shortDescription(): string { - return basename(this.labelService.getUriLabel(dirname(this.resource))); + return this.labelService.getUriBasenameLabel(dirname(this.resource)); } private get mediumDescription(): string { diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index a8e2290be22a1..2e9e9e9a1da69 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -6,6 +6,7 @@ import { localize } from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; +import * as paths from 'vs/base/common/path'; import { Event, Emitter } from 'vs/base/common/event'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -159,6 +160,16 @@ export class LabelService implements ILabelService { return options.endWithSeparator ? this.appendSeparatorIfMissing(label, formatting) : label; } + getUriBasenameLabel(resource: URI): string { + const label = this.getUriLabel(resource); + const formatting = this.findFormatting(resource); + if (formatting && formatting.separator === '\\') { + return paths.win32.basename(label); + } + + return paths.posix.basename(label); + } + getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string { if (IWorkspace.isIWorkspace(workspace)) { const identifier = toWorkspaceIdentifier(workspace); diff --git a/src/vs/workbench/services/label/test/label.test.ts b/src/vs/workbench/services/label/test/label.test.ts index ee4650f4d2ba4..acafcc5f3d507 100644 --- a/src/vs/workbench/services/label/test/label.test.ts +++ b/src/vs/workbench/services/label/test/label.test.ts @@ -33,9 +33,11 @@ suite('URI Label', () => { const uri1 = TestWorkspace.folders[0].uri.with({ path: TestWorkspace.folders[0].uri.path.concat('/a/b/c/d') }); assert.equal(labelService.getUriLabel(uri1, { relative: true }), isWindows ? 'a\\b\\c\\d' : 'a/b/c/d'); assert.equal(labelService.getUriLabel(uri1, { relative: false }), isWindows ? 'C:\\testWorkspace\\a\\b\\c\\d' : '/testWorkspace/a/b/c/d'); + assert.equal(labelService.getUriBasenameLabel(uri1), 'd'); const uri2 = URI.file('c:\\1/2/3'); assert.equal(labelService.getUriLabel(uri2, { relative: false }), isWindows ? 'C:\\1\\2\\3' : '/c:\\1/2/3'); + assert.equal(labelService.getUriBasenameLabel(uri2), '3'); }); test('custom scheme', function () { @@ -51,6 +53,23 @@ suite('URI Label', () => { const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5'); assert.equal(labelService.getUriLabel(uri1, { relative: false }), 'LABEL//1/2/3/4/5/microsoft.com/END'); + assert.equal(labelService.getUriBasenameLabel(uri1), 'END'); + }); + + test.only('separator', function () { + labelService.registerFormatter({ + scheme: 'vscode', + formatting: { + label: 'LABEL\\${path}\\${authority}\\END', + separator: '\\', + tildify: true, + normalizeDriveLetter: true + } + }); + + const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5'); + assert.equal(labelService.getUriLabel(uri1, { relative: false }), 'LABEL\\\\1\\2\\3\\4\\5\\microsoft.com\\END'); + assert.equal(labelService.getUriBasenameLabel(uri1), 'END'); }); test('custom authority', function () { @@ -65,6 +84,7 @@ suite('URI Label', () => { const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5'); assert.equal(labelService.getUriLabel(uri1, { relative: false }), 'LABEL//1/2/3/4/5/microsoft.com/END'); + assert.equal(labelService.getUriBasenameLabel(uri1), 'END'); }); test('mulitple authority', function () { @@ -96,6 +116,7 @@ suite('URI Label', () => { // Make sure the most specific authority is picked const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5'); assert.equal(labelService.getUriLabel(uri1, { relative: false }), 'second'); + assert.equal(labelService.getUriBasenameLabel(uri1), 'second'); }); test('custom query', function () { From 20a403c38f80615f5588a562de22be9754f83afb Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 23 Aug 2019 12:46:56 +0200 Subject: [PATCH 570/613] fixes #79693 --- .../workbench/contrib/files/browser/views/explorerViewer.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 016249db38c95..0c368120dff74 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -170,7 +170,11 @@ export class FilesRenderer implements ITreeRenderer { - this.updateWidth(stat); + try { + this.updateWidth(stat); + } catch (e) { + // noop since the element might no longer be in the tree, no update of width necessery + } }); } From e17398bc339df29d47f6ff8aa26d0b1558baac4a Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 23 Aug 2019 13:12:54 +0200 Subject: [PATCH 571/613] Fix for-in loops over arrays in tasks Part of #79432 --- .../workbench/contrib/tasks/browser/terminalTaskSystem.ts | 4 +--- src/vs/workbench/contrib/tasks/common/tasks.ts | 7 +++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 7097c96cd89e5..d56d5f1c70516 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -340,9 +340,7 @@ export class TerminalTaskSystem implements ITaskSystem { private async executeTask(task: Task, resolver: ITaskResolver, trigger: string): Promise { let promises: Promise[] = []; if (task.configurationProperties.dependsOn) { - // tslint:disable-next-line: no-for-in-array - for (let index in task.configurationProperties.dependsOn) { - const dependency = task.configurationProperties.dependsOn[index]; + for (const dependency of task.configurationProperties.dependsOn) { let dependencyTask = resolver.resolve(dependency.workspaceFolder, dependency.task!); if (dependencyTask) { let key = dependencyTask.getMapKey(); diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 49d370e71e023..24d77f427d789 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -985,15 +985,14 @@ export namespace KeyedTaskIdentifier { function sortedStringify(literal: any): string { const keys = Object.keys(literal).sort(); let result: string = ''; - // tslint:disable-next-line: no-for-in-array - for (let position in keys) { - let stringified = literal[keys[position]]; + for (const key of keys) { + let stringified = literal[key]; if (stringified instanceof Object) { stringified = sortedStringify(stringified); } else if (typeof stringified === 'string') { stringified = stringified.replace(/,/g, ',,'); } - result += keys[position] + ',' + stringified + ','; + result += key + ',' + stringified + ','; } return result; } From f610ae3f357e6e73a2095444eb330e818de650ea Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Aug 2019 13:09:31 +0200 Subject: [PATCH 572/613] don't highlight quick outline items, also align sorting/highlighting with IntelliSense, #50972 --- .../quickopen/browser/gotoSymbolHandler.ts | 191 +++++++----------- 1 file changed, 72 insertions(+), 119 deletions(-) diff --git a/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts index bafe2fdfe6167..1742ba33738b6 100644 --- a/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/contrib/quickopen/browser/gotoSymbolHandler.ts @@ -8,7 +8,7 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import * as strings from 'vs/base/common/strings'; import { IEntryRunContext, Mode, IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; -import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenModel, IHighlight } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenHandler, EditorQuickOpenEntryGroup, QuickOpenAction } from 'vs/workbench/browser/quickopen'; import * as filters from 'vs/base/common/filters'; import { IEditor, IDiffEditorModel, IEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; @@ -73,10 +73,8 @@ class OutlineModel extends QuickOpenModel { applyFilter(searchValue: string): void { // Normalize search - let normalizedSearchValue = searchValue; - if (searchValue.indexOf(SCOPE_PREFIX) === 0) { - normalizedSearchValue = normalizedSearchValue.substr(SCOPE_PREFIX.length); - } + const searchValueLow = searchValue.toLowerCase(); + const searchValuePos = searchValue.indexOf(SCOPE_PREFIX) === 0 ? 1 : 0; // Check for match and update visibility and group label this.entries.forEach((entry: SymbolEntry) => { @@ -84,34 +82,24 @@ class OutlineModel extends QuickOpenModel { // Clear all state first entry.setGroupLabel(undefined); entry.setShowBorder(false); - entry.setHighlights([]); + entry.setScore(undefined); entry.setHidden(false); // Filter by search - if (normalizedSearchValue) { - const highlights = filters.matchesFuzzy2(normalizedSearchValue, entry.getLabel()); - if (highlights) { - entry.setHighlights(highlights); - entry.setHidden(false); - } else if (!entry.isHidden()) { - entry.setHidden(true); - } + if (searchValue.length > searchValuePos) { + const score = filters.fuzzyScore( + searchValue.substr(searchValuePos), searchValueLow.substr(searchValuePos), 0, + entry.getLabel(), entry.getLabel().toLowerCase(), 0, + true + ); + entry.setScore(score); + entry.setHidden(!score); } }); - // Sort properly if actually searching - if (searchValue) { - if (searchValue.indexOf(SCOPE_PREFIX) === 0) { - this.entries.sort(this.sortScoped.bind(this, searchValue.toLowerCase())); - } else { - this.entries.sort(this.sortNormal.bind(this, searchValue.toLowerCase())); - } - } + this.entries.sort(SymbolEntry.compareByRank); + - // Otherwise restore order as appearing in outline - else { - this.entries.sort((a: SymbolEntry, b: SymbolEntry) => a.getIndex() - b.getIndex()); - } // Mark all type groups const visibleResults = this.getEntries(true); @@ -156,74 +144,6 @@ class OutlineModel extends QuickOpenModel { } } - private sortNormal(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number { - - // Handle hidden elements - if (elementA.isHidden() && elementB.isHidden()) { - return 0; - } else if (elementA.isHidden()) { - return 1; - } else if (elementB.isHidden()) { - return -1; - } - - const elementAName = elementA.getLabel().toLowerCase(); - const elementBName = elementB.getLabel().toLowerCase(); - - // Compare by name - const r = elementAName.localeCompare(elementBName); - if (r !== 0) { - return r; - } - - // If name identical sort by range instead - const elementARange = elementA.getRange(); - const elementBRange = elementB.getRange(); - - return elementARange.startLineNumber - elementBRange.startLineNumber; - } - - private sortScoped(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number { - - // Handle hidden elements - if (elementA.isHidden() && elementB.isHidden()) { - return 0; - } else if (elementA.isHidden()) { - return 1; - } else if (elementB.isHidden()) { - return -1; - } - - // Remove scope char - searchValue = searchValue.substr(SCOPE_PREFIX.length); - - // Sort by type first if scoped search - const elementATypeLabel = NLS_SYMBOL_KIND_CACHE[elementA.getKind()] || FALLBACK_NLS_SYMBOL_KIND; - const elementBTypeLabel = NLS_SYMBOL_KIND_CACHE[elementB.getKind()] || FALLBACK_NLS_SYMBOL_KIND; - let r = elementATypeLabel.localeCompare(elementBTypeLabel); - if (r !== 0) { - return r; - } - - // Special sort when searching in scoped mode - if (searchValue) { - const elementAName = elementA.getLabel().toLowerCase(); - const elementBName = elementB.getLabel().toLowerCase(); - - // Compare by name - r = elementAName.localeCompare(elementBName); - if (r !== 0) { - return r; - } - } - - // Default to sort by range - const elementARange = elementA.getRange(); - const elementBRange = elementB.getRange(); - - return elementARange.startLineNumber - elementBRange.startLineNumber; - } - private renderGroupLabel(type: SymbolKind, count: number): string { let pattern = NLS_SYMBOL_KIND_CACHE[type]; if (!pattern) { @@ -236,23 +156,25 @@ class OutlineModel extends QuickOpenModel { class SymbolEntry extends EditorQuickOpenEntryGroup { + private score?: filters.FuzzyScore; + constructor( - private index: number, - private name: string, - private kind: SymbolKind, - private description: string, - private icon: string, - private deprecated: boolean, - private range: IRange, - private revealRange: IRange, - private editorService: IEditorService, - private handler: GotoSymbolHandler + private readonly index: number, + private readonly name: string, + private readonly kind: SymbolKind, + private readonly description: string, + private readonly icon: string, + private readonly deprecated: boolean, + private readonly range: IRange, + private readonly revealRange: IRange, + private readonly editorService: IEditorService, + private readonly handler: GotoSymbolHandler ) { super(); } - getIndex(): number { - return this.index; + setScore(score: filters.FuzzyScore | undefined): void { + this.score = score; } getLabel(): string { @@ -271,6 +193,14 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { return this.deprecated ? { extraClasses: ['deprecated'] } : undefined; } + getHighlights(): [IHighlight[], IHighlight[] | undefined, IHighlight[] | undefined] { + return [ + this.deprecated ? [] : filters.createMatches(this.score), + undefined, + undefined + ]; + } + getDescription(): string { return this.description; } @@ -289,7 +219,7 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { getOptions(pinned?: boolean): ITextEditorOptions { return { - selection: this.toSelection(), + selection: this.revealRange, pinned }; } @@ -312,7 +242,7 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { // Apply selection and focus else { - const range = this.toSelection(); + const range = this.revealRange; const activeTextEditorWidget = this.editorService.activeTextEditorWidget; if (activeTextEditorWidget) { activeTextEditorWidget.setSelection(range); @@ -326,7 +256,7 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { private runPreview(): boolean { // Select Outline Position - const range = this.toSelection(); + const range = this.revealRange; const activeTextEditorWidget = this.editorService.activeTextEditorWidget; if (activeTextEditorWidget) { activeTextEditorWidget.revealRangeInCenter(range, ScrollType.Smooth); @@ -340,13 +270,36 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { return false; } - private toSelection(): IRange { - return { - startLineNumber: this.revealRange.startLineNumber, - startColumn: this.revealRange.startColumn || 1, - endLineNumber: this.revealRange.startLineNumber, - endColumn: this.revealRange.startColumn || 1 - }; + static compareByRank(a: SymbolEntry, b: SymbolEntry): number { + if (!a.score && b.score) { + return 1; + } else if (a.score && !b.score) { + return -1; + } + if (a.score && b.score) { + if (a.score[0] > b.score[0]) { + return -1; + } else if (a.score[0] < b.score[0]) { + return 1; + } + } + if (a.index < b.index) { + return -1; + } else if (a.index > b.index) { + return 1; + } + return 0; + } + + static compareByKindAndRank(a: SymbolEntry, b: SymbolEntry): number { + // Sort by type first if scoped search + const kindA = NLS_SYMBOL_KIND_CACHE[a.getKind()] || FALLBACK_NLS_SYMBOL_KIND; + const kindB = NLS_SYMBOL_KIND_CACHE[b.getKind()] || FALLBACK_NLS_SYMBOL_KIND; + let r = kindA.localeCompare(kindB); + if (r === 0) { + r = this.compareByRank(a, b); + } + return r; } } @@ -461,11 +414,11 @@ export class GotoSymbolHandler extends QuickOpenHandler { }; } - private toQuickOpenEntries(flattened: DocumentSymbol[]): SymbolEntry[] { + private toQuickOpenEntries(symbols: DocumentSymbol[]): SymbolEntry[] { const results: SymbolEntry[] = []; - for (let i = 0; i < flattened.length; i++) { - const element = flattened[i]; + for (let i = 0; i < symbols.length; i++) { + const element = symbols[i]; const label = strings.trim(element.name); // Show parent scope as description From 5458ae72644c9cc476160fdab1f423538c6ab1f8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Aug 2019 13:31:06 +0200 Subject: [PATCH 573/613] no highlights for deprecated workspace symbols, also tweak sorting, #50972 --- .../parts/quickopen/browser/quickOpenModel.ts | 35 ----------- .../search/browser/openSymbolHandler.ts | 59 +++++++++++++------ 2 files changed, 42 insertions(+), 52 deletions(-) diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index bb9dec7560598..fdd8bcbf06b5b 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -10,7 +10,6 @@ import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree'; import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen'; import { IAction, IActionRunner } from 'vs/base/common/actions'; -import { compareAnything } from 'vs/base/common/comparers'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import * as DOM from 'vs/base/browser/dom'; @@ -576,37 +575,3 @@ export class QuickOpenModel implements return entry.run(mode, context); } } - -/** - * A good default sort implementation for quick open entries respecting highlight information - * as well as associated resources. - */ -export function compareEntries(elementA: QuickOpenEntry, elementB: QuickOpenEntry, lookFor: string): number { - - // Give matches with label highlights higher priority over - // those with only description highlights - const labelHighlightsA = elementA.getHighlights()[0] || []; - const labelHighlightsB = elementB.getHighlights()[0] || []; - if (labelHighlightsA.length && !labelHighlightsB.length) { - return -1; - } - - if (!labelHighlightsA.length && labelHighlightsB.length) { - return 1; - } - - // Fallback to the full path if labels are identical and we have associated resources - let nameA = elementA.getLabel()!; - let nameB = elementB.getLabel()!; - if (nameA === nameB) { - const resourceA = elementA.getResource(); - const resourceB = elementB.getResource(); - - if (resourceA && resourceB) { - nameA = resourceA.fsPath; - nameB = resourceB.fsPath; - } - } - - return compareAnything(nameA, nameB, lookFor); -} diff --git a/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts b/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts index dfa1d48f15069..c0bcedcb22111 100644 --- a/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts +++ b/src/vs/workbench/contrib/search/browser/openSymbolHandler.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ThrottledDelayer } from 'vs/base/common/async'; import { QuickOpenHandler, EditorQuickOpenEntry } from 'vs/workbench/browser/quickopen'; -import { QuickOpenModel, QuickOpenEntry, compareEntries } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { QuickOpenModel, QuickOpenEntry, IHighlight } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen'; import * as filters from 'vs/base/common/filters'; import * as strings from 'vs/base/common/strings'; @@ -28,7 +28,9 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; class SymbolEntry extends EditorQuickOpenEntry { - private bearingResolve: Promise | undefined; + + private bearingResolve?: Promise; + private score?: filters.FuzzyScore; constructor( private bearing: IWorkspaceSymbol, @@ -41,6 +43,14 @@ class SymbolEntry extends EditorQuickOpenEntry { super(editorService); } + setScore(score: filters.FuzzyScore | undefined) { + this.score = score; + } + + getHighlights(): [IHighlight[] /* Label */, IHighlight[] | undefined /* Description */, IHighlight[] | undefined /* Detail */] { + return [this.isDeprecated() ? [] : filters.createMatches(this.score), undefined, undefined]; + } + getLabel(): string { return this.bearing.name; } @@ -67,15 +77,17 @@ class SymbolEntry extends EditorQuickOpenEntry { } getLabelOptions(): IIconLabelValueOptions | undefined { - return this.bearing.tags && this.bearing.tags.indexOf(SymbolTag.Deprecated) >= 0 - ? { extraClasses: ['deprecated'] } - : undefined; + return this.isDeprecated() ? { extraClasses: ['deprecated'] } : undefined; } getResource(): URI { return this.bearing.location.uri; } + private isDeprecated(): boolean { + return this.bearing.tags ? this.bearing.tags.indexOf(SymbolTag.Deprecated) >= 0 : false; + } + run(mode: Mode, context: IEntryRunContext): boolean { // resolve this type bearing if neccessary @@ -118,18 +130,24 @@ class SymbolEntry extends EditorQuickOpenEntry { return input; } - static compare(elementA: SymbolEntry, elementB: SymbolEntry, searchValue: string): number { - - // Sort by Type if name is identical - const elementAName = elementA.getLabel().toLowerCase(); - const elementBName = elementB.getLabel().toLowerCase(); - if (elementAName === elementBName) { - let elementAType = symbolKindToCssClass(elementA.bearing.kind); - let elementBType = symbolKindToCssClass(elementB.bearing.kind); - return elementAType.localeCompare(elementBType); + static compare(a: SymbolEntry, b: SymbolEntry, searchValue: string): number { + // order: score, name, kind + if (a.score && b.score) { + if (a.score[0] > b.score[0]) { + return -1; + } else if (a.score[0] < b.score[0]) { + return 1; + } } - - return compareEntries(elementA, elementB, searchValue); + const aName = a.getLabel().toLowerCase(); + const bName = b.getLabel().toLowerCase(); + let res = aName.localeCompare(bName); + if (res !== 0) { + return res; + } + let aKind = symbolKindToCssClass(a.bearing.kind); + let bKind = symbolKindToCssClass(b.bearing.kind); + return aKind.localeCompare(bKind); } } @@ -205,6 +223,9 @@ export class OpenSymbolHandler extends QuickOpenHandler { private fillInSymbolEntries(bucket: SymbolEntry[], provider: IWorkspaceSymbolProvider, types: IWorkspaceSymbol[], searchValue: string): void { + const pattern = strings.stripWildcards(searchValue); + const patternLow = pattern.toLowerCase(); + // Convert to Entries for (let element of types) { if (this.options.skipLocalSymbols && !!element.containerName) { @@ -212,7 +233,11 @@ export class OpenSymbolHandler extends QuickOpenHandler { } const entry = this.instantiationService.createInstance(SymbolEntry, element, provider); - entry.setHighlights(filters.matchesFuzzy2(searchValue, entry.getLabel()) || []); + entry.setScore(filters.fuzzyScore( + pattern, patternLow, 0, + entry.getLabel(), entry.getLabel().toLowerCase(), 0, + true + )); bucket.push(entry); } } From 8748114548a823566f0925dd589119d0d41bc918 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Aug 2019 13:48:57 +0200 Subject: [PATCH 574/613] debt - quick outline using the same model cache as breadcrumbs and outline tree --- src/vs/editor/contrib/quickOpen/quickOpen.ts | 49 +++++++++----------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/src/vs/editor/contrib/quickOpen/quickOpen.ts b/src/vs/editor/contrib/quickOpen/quickOpen.ts index fb054ae7f618c..2730114175b26 100644 --- a/src/vs/editor/contrib/quickOpen/quickOpen.ts +++ b/src/vs/editor/contrib/quickOpen/quickOpen.ts @@ -3,44 +3,41 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors'; +import { illegalArgument } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; -import { DocumentSymbol, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; +import { DocumentSymbol } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { OutlineModel, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; +import { values } from 'vs/base/common/collections'; -export function getDocumentSymbols(model: ITextModel, flat: boolean, token: CancellationToken): Promise { +export async function getDocumentSymbols(document: ITextModel, flat: boolean, token: CancellationToken): Promise { - let roots: DocumentSymbol[] = []; - - let promises = DocumentSymbolProviderRegistry.all(model).map(support => { - - return Promise.resolve(support.provideDocumentSymbols(model, token)).then(result => { - if (Array.isArray(result)) { - roots.push(...result); - } - }, err => { - onUnexpectedExternalError(err); - }); - }); - - return Promise.all(promises).then(() => { - let flatEntries: DocumentSymbol[] = []; - if (token.isCancellationRequested) { - return flatEntries; - } - if (flat) { - flatten(flatEntries, roots, ''); + const model = await OutlineModel.create(document, token); + const roots: DocumentSymbol[] = []; + for (const child of values(model.children)) { + if (child instanceof OutlineElement) { + roots.push(child.symbol); } else { - flatEntries = roots; + roots.push(...values(child.children).map(child => child.symbol)); } - flatEntries.sort(compareEntriesUsingStart); + } + + let flatEntries: DocumentSymbol[] = []; + if (token.isCancellationRequested) { return flatEntries; - }); + } + if (flat) { + flatten(flatEntries, roots, ''); + } else { + flatEntries = roots; + } + + return flatEntries.sort(compareEntriesUsingStart); } function compareEntriesUsingStart(a: DocumentSymbol, b: DocumentSymbol): number { From 00d4b4817a9822469683865062dd1d44ac50cb08 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Aug 2019 14:07:45 +0200 Subject: [PATCH 575/613] re-enable all tests --- src/vs/workbench/services/label/test/label.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/label/test/label.test.ts b/src/vs/workbench/services/label/test/label.test.ts index acafcc5f3d507..a0d8af36a201f 100644 --- a/src/vs/workbench/services/label/test/label.test.ts +++ b/src/vs/workbench/services/label/test/label.test.ts @@ -56,7 +56,7 @@ suite('URI Label', () => { assert.equal(labelService.getUriBasenameLabel(uri1), 'END'); }); - test.only('separator', function () { + test('separator', function () { labelService.registerFormatter({ scheme: 'vscode', formatting: { From b83d45da55cd917970777a76c181af33a0f639fb Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 23 Aug 2019 14:28:38 +0200 Subject: [PATCH 576/613] Don't disable unnecessary features when a screen reader is attached --- src/vs/editor/common/config/editorOptions.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 71b0cbb884391..997d22be61459 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2185,7 +2185,7 @@ export class InternalEditorOptionsFactory { selectOnLineNumbers: opts.viewInfo.selectOnLineNumbers, glyphMargin: opts.viewInfo.glyphMargin, revealHorizontalRightPadding: opts.viewInfo.revealHorizontalRightPadding, - roundedSelection: (accessibilityIsOn ? false : opts.viewInfo.roundedSelection), // DISABLED WHEN SCREEN READER IS ATTACHED + roundedSelection: opts.viewInfo.roundedSelection, overviewRulerLanes: opts.viewInfo.overviewRulerLanes, overviewRulerBorder: opts.viewInfo.overviewRulerBorder, cursorBlinking: opts.viewInfo.cursorBlinking, @@ -2198,15 +2198,15 @@ export class InternalEditorOptionsFactory { scrollBeyondLastColumn: opts.viewInfo.scrollBeyondLastColumn, smoothScrolling: opts.viewInfo.smoothScrolling, stopRenderingLineAfter: opts.viewInfo.stopRenderingLineAfter, - renderWhitespace: (accessibilityIsOn ? 'none' : opts.viewInfo.renderWhitespace), // DISABLED WHEN SCREEN READER IS ATTACHED - renderControlCharacters: (accessibilityIsOn ? false : opts.viewInfo.renderControlCharacters), // DISABLED WHEN SCREEN READER IS ATTACHED + renderWhitespace: opts.viewInfo.renderWhitespace, + renderControlCharacters: opts.viewInfo.renderControlCharacters, fontLigatures: opts.viewInfo.fontLigatures, - renderIndentGuides: (accessibilityIsOn ? false : opts.viewInfo.renderIndentGuides), // DISABLED WHEN SCREEN READER IS ATTACHED + renderIndentGuides: opts.viewInfo.renderIndentGuides, highlightActiveIndentGuide: opts.viewInfo.highlightActiveIndentGuide, renderLineHighlight: opts.viewInfo.renderLineHighlight, scrollbar: opts.viewInfo.scrollbar, minimap: { - enabled: (accessibilityIsOn ? false : opts.viewInfo.minimap.enabled), // DISABLED WHEN SCREEN READER IS ATTACHED + enabled: opts.viewInfo.minimap.enabled, side: opts.viewInfo.minimap.side, renderCharacters: opts.viewInfo.minimap.renderCharacters, showSlider: opts.viewInfo.minimap.showSlider, @@ -2218,7 +2218,7 @@ export class InternalEditorOptionsFactory { contribInfo: { selectionClipboard: opts.contribInfo.selectionClipboard, hover: opts.contribInfo.hover, - links: (accessibilityIsOn ? false : opts.contribInfo.links), // DISABLED WHEN SCREEN READER IS ATTACHED + links: opts.contribInfo.links, contextmenu: opts.contribInfo.contextmenu, quickSuggestions: opts.contribInfo.quickSuggestions, quickSuggestionsDelay: opts.contribInfo.quickSuggestionsDelay, @@ -2235,13 +2235,13 @@ export class InternalEditorOptionsFactory { tabCompletion: opts.contribInfo.tabCompletion, suggest: opts.contribInfo.suggest, gotoLocation: opts.contribInfo.gotoLocation, - selectionHighlight: (accessibilityIsOn ? false : opts.contribInfo.selectionHighlight), // DISABLED WHEN SCREEN READER IS ATTACHED - occurrencesHighlight: (accessibilityIsOn ? false : opts.contribInfo.occurrencesHighlight), // DISABLED WHEN SCREEN READER IS ATTACHED - codeLens: (accessibilityIsOn ? false : opts.contribInfo.codeLens), // DISABLED WHEN SCREEN READER IS ATTACHED + selectionHighlight: opts.contribInfo.selectionHighlight, + occurrencesHighlight: opts.contribInfo.occurrencesHighlight, + codeLens: opts.contribInfo.codeLens, folding: (accessibilityIsOn ? false : opts.contribInfo.folding), // DISABLED WHEN SCREEN READER IS ATTACHED foldingStrategy: opts.contribInfo.foldingStrategy, showFoldingControls: opts.contribInfo.showFoldingControls, - matchBrackets: (accessibilityIsOn ? false : opts.contribInfo.matchBrackets), // DISABLED WHEN SCREEN READER IS ATTACHED + matchBrackets: opts.contribInfo.matchBrackets, find: opts.contribInfo.find, colorDecorators: opts.contribInfo.colorDecorators, lightbulbEnabled: opts.contribInfo.lightbulbEnabled, From 40e79350b4292235bbcf4582da99fc9d939fd46e Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 23 Aug 2019 14:56:25 +0200 Subject: [PATCH 577/613] Include userdata in configuration resolver file resolution Fixes #78247 --- .../browser/configurationResolverService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index 4709a15555b50..f5d0b60b07b67 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -55,7 +55,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR if (activeEditor instanceof DiffEditorInput) { activeEditor = activeEditor.modifiedInput; } - const fileResource = toResource(activeEditor, { filterByScheme: Schemas.file }); + const fileResource = toResource(activeEditor, { filterByScheme: [Schemas.file, Schemas.userData] }); if (!fileResource) { return undefined; } From 760df9342ba7869fcd62e29ccd17c817d5522b29 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 23 Aug 2019 15:21:01 +0200 Subject: [PATCH 578/613] build - document loading approach better --- src/vs/code/electron-browser/workbench/workbench.js | 5 ++++- src/vs/workbench/workbench.web.api.ts | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 98e67927fe123..22d5dbe329c81 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -14,7 +14,10 @@ const bootstrapWindow = require('../../../../bootstrap-window'); // Setup shell environment process['lazyEnv'] = getLazyEnv(); -// Load workbench main +// Load workbench main JS, CSS and NLS all in parallel. This is an +// optimization to prevent a waterfall of loading to happen, because +// we know for a fact that workbench.desktop.main will depend on +// the related CSS and NLS counterparts. bootstrapWindow.load([ 'vs/workbench/workbench.desktop.main', 'vs/nls!vs/workbench/workbench.desktop.main', diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 7f1da9f12035f..844ff1da56a73 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -4,8 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/workbench/workbench.web.main'; -import 'vs/nls!vs/workbench/workbench.web.main'; -import 'vs/css!vs/workbench/workbench.web.main'; import { main } from 'vs/workbench/browser/web.main'; import { UriComponents } from 'vs/base/common/uri'; import { IFileSystemProvider } from 'vs/platform/files/common/files'; From 00d53e96861523c96789c5242ffe78101acfe1fb Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 23 Aug 2019 15:21:24 +0200 Subject: [PATCH 579/613] Option to configure which services should be run (microsoft/vscode-remote-release#211) --- .../schemas/devContainer.schema.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index 55950a9021eba..c24857720e65d 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -129,6 +129,13 @@ "type": "string", "description": "The service you want to work on." }, + "runServices": { + "type": "array", + "description": "An array of services that should be started and stopped.", + "items": { + "type": "string" + } + }, "workspaceFolder": { "type": "string", "description": "The path of the workspace folder inside the container." @@ -178,4 +185,4 @@ "$ref": "#/definitions/devContainerCommon" } ] -} \ No newline at end of file +} From edee5c03241c45fc8b9c04e88ef1916e0d595909 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 23 Aug 2019 15:24:22 +0200 Subject: [PATCH 580/613] fix tests --- src/vs/editor/standalone/browser/simpleServices.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index ce860a9dbac5c..f8c1def46a268 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -671,7 +671,7 @@ export class SimpleUriLabelService implements ILabelService { } getUriBasenameLabel(resource: URI): string { - return basename(resource.path); + return basename(this.getUriLabel(resource)); } public getWorkspaceLabel(workspace: IWorkspaceIdentifier | URI | IWorkspace, options?: { verbose: boolean; }): string { From df880b0434db1ab7889a5ffa777e8ec7ccd73f90 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 23 Aug 2019 15:36:05 +0200 Subject: [PATCH 581/613] Workaround for broken WSL2 in Win build 18947. For #77898 --- resources/win32/bin/code.sh | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh index 4bc4677e1e483..745f2adb7aafc 100644 --- a/resources/win32/bin/code.sh +++ b/resources/win32/bin/code.sh @@ -11,14 +11,9 @@ VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")" ELECTRON="$VSCODE_PATH/$NAME.exe" if grep -qi Microsoft /proc/version; then # in a wsl shell - if [ "$WSL_DISTRO_NAME" ]; then - # $WSL_DISTRO_NAME is available since WSL builds 18362, also for WSL2 - WSL_BUILD=18362 - else - WSL_BUILD=$(uname -r | sed -E 's/^.+-([0-9]+)-Microsoft/\1/') - if [ -z "$WSL_BUILD" ]; then - WSL_BUILD=0 - fi + WSL_BUILD=$(uname -r | sed -E 's/^[0-9.]+-([0-9]+)-Microsoft|([0-9]+).([0-9]+).([0-9]+)-microsoft-standard|.*/\1\2\3\4/') + if [ -z "$WSL_BUILD" ]; then + WSL_BUILD=0 fi if [ $WSL_BUILD -ge 17063 ]; then @@ -30,7 +25,18 @@ if grep -qi Microsoft /proc/version; then # use the Remote WSL extension if installed WSL_EXT_ID="ms-vscode-remote.remote-wsl" - WSL_EXT_WLOC=$(ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID) + + if [ $WSL_BUILD -ge 41955 ]; then + # WSL2 in workaround for https://github.com/microsoft/WSL/issues/4337 + CWD="$(pwd)" + cd "$VSCODE_PATH" + cmd.exe /C ".\\bin\\$APP_NAME.cmd --locate-extension $WSL_EXT_ID >remote-wsl-loc.txt" + WSL_EXT_WLOC="$(cat ./remote-wsl-loc.txt)" + rm remote-wsl-loc.txt + cd "$CWD" + else + WSL_EXT_WLOC=$(ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID) + fi if [ -n "$WSL_EXT_WLOC" ]; then # replace \r\n with \n in WSL_EXT_WLOC WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode.sh From 82d915c2e9fbc9055807fdc215539e423e91915b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Aug 2019 15:42:26 +0200 Subject: [PATCH 582/613] worker - block some globals from being used --- .../extensions/worker/extensionHostWorker.ts | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts index 34e3f35a624bf..3d6659698bed3 100644 --- a/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts +++ b/src/vs/workbench/services/extensions/worker/extensionHostWorker.ts @@ -12,15 +12,31 @@ import { ExtensionHostMain } from 'vs/workbench/services/extensions/common/exten import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService'; import 'vs/workbench/services/extensions/worker/extHost.services'; -// worker-self +//#region --- Define, capture, and override some globals +//todo@joh do not allow extensions to call postMessage and other globals... + declare namespace self { - function close(): void; + let close: any; + let postMessage: any; + let addEventLister: any; + let indexedDB: any; + let caches: any; } -// do not allow extensions to call terminate const nativeClose = self.close.bind(self); -self.close = () => console.trace('An extension called terminate and this was prevented'); -let onTerminate = nativeClose; +self.close = () => console.trace(`'close' has been blocked`); + +const nativePostMessage = postMessage.bind(self); +self.postMessage = () => console.trace(`'postMessage' has been blocked`); + +const nativeAddEventLister = addEventListener.bind(self); +self.addEventLister = () => console.trace(`'addEventListener' has been blocked`); + +// readonly, cannot redefine... +// self.indexedDB = undefined; +// self.caches = undefined; + +//#endregion --- const hostUtil = new class implements IHostUtils { _serviceBrand: any; @@ -35,7 +51,6 @@ const hostUtil = new class implements IHostUtils { } }; -//todo@joh do not allow extensions to call postMessage and other globals... class ExtensionWorker { @@ -47,7 +62,8 @@ class ExtensionWorker { let emitter = new Emitter(); let terminating = false; - onmessage = event => { + + nativeAddEventLister('message', event => { const { data } = event; if (!(data instanceof ArrayBuffer)) { console.warn('UNKNOWN data received', data); @@ -64,14 +80,14 @@ class ExtensionWorker { // emit non-terminate messages to the outside emitter.fire(msg); - }; + }); this.protocol = { onMessage: emitter.event, send: vsbuf => { if (!terminating) { const data = vsbuf.buffer.buffer.slice(vsbuf.buffer.byteOffset, vsbuf.buffer.byteOffset + vsbuf.buffer.byteLength); - postMessage(data, [data]); + nativePostMessage(data, [data]); } } }; @@ -94,6 +110,8 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise Date: Fri, 23 Aug 2019 16:30:03 +0200 Subject: [PATCH 583/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0add763ee4272..27866eca5911d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "d54cb1c81bc4458276bd38093586ee3566539d2d", + "distro": "740bacd7f9f039377fb63de6d4dc85f9ae88236a", "author": { "name": "Microsoft Corporation" }, From ebe53708ac47643becdda0e11cf5fb751b3ddd0c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 23 Aug 2019 16:31:25 +0200 Subject: [PATCH 584/613] web - prevent waterfall loading --- src/vs/code/browser/workbench/workbench.html | 23 +++++++++++-------- ...nch.web.main.css => workbench.web.api.css} | 3 --- ...b.main.nls.js => workbench.web.api.nls.js} | 2 -- 3 files changed, 13 insertions(+), 15 deletions(-) rename src/vs/workbench/{workbench.web.main.css => workbench.web.api.css} (94%) rename src/vs/workbench/{workbench.web.main.nls.js => workbench.web.api.nls.js} (96%) diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 9bf87281e2ccd..9c7defe5af3a7 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -4,29 +4,32 @@ - - - + + + - + - - + + + + - + - - + diff --git a/src/vs/workbench/workbench.web.main.css b/src/vs/workbench/workbench.web.api.css similarity index 94% rename from src/vs/workbench/workbench.web.main.css rename to src/vs/workbench/workbench.web.api.css index 06ed8a197b2a3..3a0641938d56e 100644 --- a/src/vs/workbench/workbench.web.main.css +++ b/src/vs/workbench/workbench.web.api.css @@ -4,6 +4,3 @@ *--------------------------------------------------------------------------------------------*/ /* NOTE: THIS FILE WILL BE OVERWRITTEN DURING BUILD TIME, DO NOT EDIT */ - -div.monaco.main.css { -} \ No newline at end of file diff --git a/src/vs/workbench/workbench.web.main.nls.js b/src/vs/workbench/workbench.web.api.nls.js similarity index 96% rename from src/vs/workbench/workbench.web.main.nls.js rename to src/vs/workbench/workbench.web.api.nls.js index d6a8b487eaf92..6113d093d5cce 100644 --- a/src/vs/workbench/workbench.web.main.nls.js +++ b/src/vs/workbench/workbench.web.api.nls.js @@ -4,5 +4,3 @@ *--------------------------------------------------------------------------------------------*/ // NOTE: THIS FILE WILL BE OVERWRITTEN DURING BUILD TIME, DO NOT EDIT - -define([], {}); \ No newline at end of file From f58c8f6a0c5b90f2503666dad5a91969ddcbb895 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 23 Aug 2019 16:37:17 +0200 Subject: [PATCH 585/613] Revert "build - add pool" This reverts commit 7125817376817d861b719619920f3b4ba0d2dca0. --- build/azure-pipelines/distro-build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index 74fddcc55a84b..62ee67ad1c6d9 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -1,6 +1,3 @@ -pool: - vmImage: 'Ubuntu-16.04' - trigger: branches: include: ['master', 'release/*'] From 4c2f7756d20acabbff07ca70dfc0de3e43b9bd35 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 23 Aug 2019 17:30:19 +0200 Subject: [PATCH 586/613] Improve connection logic (fixes microsoft/vscode-remote-release#1224) --- src/vs/base/parts/ipc/common/ipc.net.ts | 99 ++++--- .../remote/common/remoteAgentConnection.ts | 265 +++++++++++++----- src/vs/workbench/browser/web.main.ts | 2 +- .../electron-browser/desktop.main.ts | 2 +- .../configurationService.test.ts | 2 +- .../common/remoteExtensionHostClient.ts | 3 +- .../node/extensionHostProcessSetup.ts | 8 +- .../remote/browser/remoteAgentServiceImpl.ts | 6 +- .../common/abstractRemoteAgentService.ts | 7 +- .../remoteAgentServiceImpl.ts | 6 +- .../services/remote/node/tunnelService.ts | 7 +- 11 files changed, 273 insertions(+), 134 deletions(-) diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index 207fceb65b964..1e2fdbccec466 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -405,42 +405,53 @@ export class Client extends IPCClient { /** * Will ensure no messages are lost if there are no event listeners. */ -export function createBufferedEvent(source: Event): Event { - let emitter: Emitter; - let hasListeners = false; - let isDeliveringMessages = false; - let bufferedMessages: T[] = []; - - const deliverMessages = () => { - if (isDeliveringMessages) { +export class BufferedEmitter { + private _emitter: Emitter; + public readonly event: Event; + + private _hasListeners = false; + private _isDeliveringMessages = false; + private _bufferedMessages: T[] = []; + + constructor() { + this._emitter = new Emitter({ + onFirstListenerAdd: () => { + this._hasListeners = true; + // it is important to deliver these messages after this call, but before + // other messages have a chance to be received (to guarantee in order delivery) + // that's why we're using here nextTick and not other types of timeouts + process.nextTick(() => this._deliverMessages); + }, + onLastListenerRemove: () => { + this._hasListeners = false; + } + }); + + this.event = this._emitter.event; + } + + private _deliverMessages(): void { + if (this._isDeliveringMessages) { return; } - isDeliveringMessages = true; - while (hasListeners && bufferedMessages.length > 0) { - emitter.fire(bufferedMessages.shift()!); + this._isDeliveringMessages = true; + while (this._hasListeners && this._bufferedMessages.length > 0) { + this._emitter.fire(this._bufferedMessages.shift()!); } - isDeliveringMessages = false; - }; + this._isDeliveringMessages = false; + } - source((e: T) => { - bufferedMessages.push(e); - deliverMessages(); - }); - - emitter = new Emitter({ - onFirstListenerAdd: () => { - hasListeners = true; - // it is important to deliver these messages after this call, but before - // other messages have a chance to be received (to guarantee in order delivery) - // that's why we're using here nextTick and not other types of timeouts - process.nextTick(deliverMessages); - }, - onLastListenerRemove: () => { - hasListeners = false; - } - }); - - return emitter.event; + public fire(event: T): void { + if (this._hasListeners) { + this._emitter.fire(event); + } else { + this._bufferedMessages.push(event); + } + } + + public flushBuffer(): void { + this._bufferedMessages = []; + } } class QueueElement { @@ -530,20 +541,20 @@ export class PersistentProtocol implements IMessagePassingProtocol { private _socketReader: ProtocolReader; private _socketDisposables: IDisposable[]; - private _onControlMessage = new Emitter(); - readonly onControlMessage: Event = createBufferedEvent(this._onControlMessage.event); + private readonly _onControlMessage = new BufferedEmitter(); + readonly onControlMessage: Event = this._onControlMessage.event; - private _onMessage = new Emitter(); - readonly onMessage: Event = createBufferedEvent(this._onMessage.event); + private readonly _onMessage = new BufferedEmitter(); + readonly onMessage: Event = this._onMessage.event; - private _onClose = new Emitter(); - readonly onClose: Event = createBufferedEvent(this._onClose.event); + private readonly _onClose = new BufferedEmitter(); + readonly onClose: Event = this._onClose.event; - private _onSocketClose = new Emitter(); - readonly onSocketClose: Event = createBufferedEvent(this._onSocketClose.event); + private readonly _onSocketClose = new BufferedEmitter(); + readonly onSocketClose: Event = this._onSocketClose.event; - private _onSocketTimeout = new Emitter(); - readonly onSocketTimeout: Event = createBufferedEvent(this._onSocketTimeout.event); + private readonly _onSocketTimeout = new BufferedEmitter(); + readonly onSocketTimeout: Event = this._onSocketTimeout.event; public get unacknowledgedCount(): number { return this._outgoingMsgId - this._outgoingAckId; @@ -656,6 +667,10 @@ export class PersistentProtocol implements IMessagePassingProtocol { this._isReconnecting = true; this._socketDisposables = dispose(this._socketDisposables); + this._onControlMessage.flushBuffer(); + this._onSocketClose.flushBuffer(); + this._onSocketTimeout.flushBuffer(); + this._socket.dispose(); this._socket = socket; this._socketWriter = new ProtocolWriter(this._socket); diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts index 7b6e68976816a..b526d149fa3af 100644 --- a/src/vs/platform/remote/common/remoteAgentConnection.ts +++ b/src/vs/platform/remote/common/remoteAgentConnection.ts @@ -10,9 +10,10 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter } from 'vs/base/common/event'; import { RemoteAuthorityResolverError } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { isPromiseCanceledError } from 'vs/base/common/errors'; +import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; import { ISignService } from 'vs/platform/sign/common/sign'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { ILogService } from 'vs/platform/log/common/log'; export const enum ConnectionType { Management = 1, @@ -20,6 +21,17 @@ export const enum ConnectionType { Tunnel = 3, } +function connectionTypeToString(connectionType: ConnectionType): string { + switch (connectionType) { + case ConnectionType.Management: + return 'Management'; + case ConnectionType.ExtensionHost: + return 'ExtensionHost'; + case ConnectionType.Tunnel: + return 'Tunnel'; + } +} + export interface AuthRequest { type: 'auth'; auth: string; @@ -58,6 +70,7 @@ interface ISimpleConnectionOptions { reconnectionProtocol: PersistentProtocol | null; socketFactory: ISocketFactory; signService: ISignService; + logService: ILogService; } export interface IConnectCallback { @@ -68,29 +81,46 @@ export interface ISocketFactory { connect(host: string, port: number, query: string, callback: IConnectCallback): void; } -async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptions, connectionType: ConnectionType, args: any | undefined): Promise { - const protocol = await new Promise((c, e) => { +async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptions, connectionType: ConnectionType, args: any | undefined): Promise<{ protocol: PersistentProtocol; ownsProtocol: boolean; }> { + const logPrefix = connectLogPrefix(options, connectionType); + const { protocol, ownsProtocol } = await new Promise<{ protocol: PersistentProtocol; ownsProtocol: boolean; }>((c, e) => { + options.logService.trace(`${logPrefix} 1/6. invoking socketFactory.connect().`); options.socketFactory.connect( options.host, options.port, `reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, (err: any, socket: ISocket) => { if (err) { + options.logService.error(`${logPrefix} socketFactory.connect() failed. Error:`); + options.logService.error(err); e(err); return; } + options.logService.trace(`${logPrefix} 2/6. socketFactory.connect() was successful.`); if (options.reconnectionProtocol) { options.reconnectionProtocol.beginAcceptReconnection(socket, null); - c(options.reconnectionProtocol); + c({ protocol: options.reconnectionProtocol, ownsProtocol: false }); } else { - c(new PersistentProtocol(socket, null)); + c({ protocol: new PersistentProtocol(socket, null), ownsProtocol: true }); } } ); }); - return new Promise((c, e) => { + return new Promise<{ protocol: PersistentProtocol; ownsProtocol: boolean; }>((c, e) => { + + const errorTimeoutToken = setTimeout(() => { + const error: any = new Error('handshake timeout'); + error.code = 'ETIMEDOUT'; + error.syscall = 'connect'; + options.logService.error(`${logPrefix} the handshake took longer than 10 seconds. Error:`); + options.logService.error(error); + if (ownsProtocol) { + safeDisposeProtocolAndSocket(protocol); + } + e(error); + }, 10000); const messageRegistration = protocol.onControlMessage(async raw => { const msg = JSON.parse(raw.toString()); @@ -99,11 +129,16 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio const error = getErrorFromMessage(msg); if (error) { + options.logService.error(`${logPrefix} received error control message when negotiating connection. Error:`); + options.logService.error(error); + if (ownsProtocol) { + safeDisposeProtocolAndSocket(protocol); + } return e(error); } if (msg.type === 'sign') { - + options.logService.trace(`${logPrefix} 4/6. received SignRequest control message.`); const signed = await options.signService.sign(msg.data); const connTypeRequest: ConnectionTypeRequest = { type: 'connectionType', @@ -114,17 +149,22 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio if (args) { connTypeRequest.args = args; } + options.logService.trace(`${logPrefix} 5/6. sending ConnectionTypeRequest control message.`); protocol.sendControl(VSBuffer.fromString(JSON.stringify(connTypeRequest))); - c(protocol); + clearTimeout(errorTimeoutToken); + c({ protocol, ownsProtocol }); } else { - e(new Error('handshake error')); + const error = new Error('handshake error'); + options.logService.error(`${logPrefix} received unexpected control message. Error:`); + options.logService.error(error); + if (ownsProtocol) { + safeDisposeProtocolAndSocket(protocol); + } + e(error); } }); - setTimeout(() => { - e(new Error('handshake timeout')); - }, 2000); - + options.logService.trace(`${logPrefix} 3/6. sending AuthRequest control message.`); // TODO@vs-remote: use real nonce here const authRequest: AuthRequest = { type: 'auth', @@ -138,24 +178,37 @@ interface IManagementConnectionResult { protocol: PersistentProtocol; } -async function doConnectRemoteAgentManagement(options: ISimpleConnectionOptions): Promise { - const protocol = await connectToRemoteExtensionHostAgent(options, ConnectionType.Management, undefined); - return new Promise((c, e) => { +async function connectToRemoteExtensionHostAgentAndReadOneMessage(options: ISimpleConnectionOptions, connectionType: ConnectionType, args: any | undefined): Promise<{ protocol: PersistentProtocol; firstMessage: any }> { + const startTime = Date.now(); + const logPrefix = connectLogPrefix(options, connectionType); + const { protocol, ownsProtocol } = await connectToRemoteExtensionHostAgent(options, connectionType, args); + return new Promise<{ protocol: PersistentProtocol; firstMessage: any }>((c, e) => { const registration = protocol.onControlMessage(raw => { registration.dispose(); const msg = JSON.parse(raw.toString()); const error = getErrorFromMessage(msg); if (error) { + options.logService.error(`${logPrefix} received error control message when negotiating connection. Error:`); + options.logService.error(error); + if (ownsProtocol) { + safeDisposeProtocolAndSocket(protocol); + } return e(error); } if (options.reconnectionProtocol) { options.reconnectionProtocol.endAcceptReconnection(); } - c({ protocol }); + options.logService.trace(`${logPrefix} 6/6. handshake finished, connection is up and running after ${logElapsed(startTime)}!`); + c({ protocol, firstMessage: msg }); }); }); } +async function doConnectRemoteAgentManagement(options: ISimpleConnectionOptions): Promise { + const { protocol } = await connectToRemoteExtensionHostAgentAndReadOneMessage(options, ConnectionType.Management, undefined); + return { protocol }; +} + export interface IRemoteExtensionHostStartParams { language: string; debugId?: string; @@ -170,22 +223,9 @@ interface IExtensionHostConnectionResult { } async function doConnectRemoteAgentExtensionHost(options: ISimpleConnectionOptions, startArguments: IRemoteExtensionHostStartParams): Promise { - const protocol = await connectToRemoteExtensionHostAgent(options, ConnectionType.ExtensionHost, startArguments); - return new Promise((c, e) => { - const registration = protocol.onControlMessage(raw => { - registration.dispose(); - const msg = JSON.parse(raw.toString()); - const error = getErrorFromMessage(msg); - if (error) { - return e(error); - } - const debugPort = msg && msg.debugPort; - if (options.reconnectionProtocol) { - options.reconnectionProtocol.endAcceptReconnection(); - } - c({ protocol, debugPort }); - }); - }); + const { protocol, firstMessage } = await connectToRemoteExtensionHostAgentAndReadOneMessage(options, ConnectionType.ExtensionHost, startArguments); + const debugPort = firstMessage && firstMessage.debugPort; + return { protocol, debugPort }; } export interface ITunnelConnectionStartParams { @@ -193,7 +233,10 @@ export interface ITunnelConnectionStartParams { } async function doConnectRemoteAgentTunnel(options: ISimpleConnectionOptions, startParams: ITunnelConnectionStartParams): Promise { - const protocol = await connectToRemoteExtensionHostAgent(options, ConnectionType.Tunnel, startParams); + const startTime = Date.now(); + const logPrefix = connectLogPrefix(options, ConnectionType.Tunnel); + const { protocol } = await connectToRemoteExtensionHostAgent(options, ConnectionType.Tunnel, startParams); + options.logService.trace(`${logPrefix} 6/6. handshake finished, connection is up and running after ${logElapsed(startTime)}!`); return protocol; } @@ -202,6 +245,7 @@ export interface IConnectionOptions { socketFactory: ISocketFactory; addressProvider: IAddressProvider; signService: ISignService; + logService: ILogService; } async function resolveConnectionOptions(options: IConnectionOptions, reconnectionToken: string, reconnectionProtocol: PersistentProtocol | null): Promise { @@ -213,7 +257,8 @@ async function resolveConnectionOptions(options: IConnectionOptions, reconnectio reconnectionToken: reconnectionToken, reconnectionProtocol: reconnectionProtocol, socketFactory: options.socketFactory, - signService: options.signService + signService: options.signService, + logService: options.logService }; } @@ -227,22 +272,36 @@ export interface IAddressProvider { } export async function connectRemoteAgentManagement(options: IConnectionOptions, remoteAuthority: string, clientId: string): Promise { - const reconnectionToken = generateUuid(); - const simpleOptions = await resolveConnectionOptions(options, reconnectionToken, null); - const { protocol } = await doConnectRemoteAgentManagement(simpleOptions); - return new ManagementPersistentConnection(options, remoteAuthority, clientId, reconnectionToken, protocol); + try { + const reconnectionToken = generateUuid(); + const simpleOptions = await resolveConnectionOptions(options, reconnectionToken, null); + const { protocol } = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentManagement(simpleOptions), 30 * 1000 /*30s*/); + return new ManagementPersistentConnection(options, remoteAuthority, clientId, reconnectionToken, protocol); + } catch (err) { + options.logService.error(`[remote-connection] An error occurred in the very first connect attempt, it will be treated as a permanent error! Error:`); + options.logService.error(err); + PersistentConnection.triggerPermanentFailure(); + throw err; + } } export async function connectRemoteAgentExtensionHost(options: IConnectionOptions, startArguments: IRemoteExtensionHostStartParams): Promise { - const reconnectionToken = generateUuid(); - const simpleOptions = await resolveConnectionOptions(options, reconnectionToken, null); - const { protocol, debugPort } = await doConnectRemoteAgentExtensionHost(simpleOptions, startArguments); - return new ExtensionHostPersistentConnection(options, startArguments, reconnectionToken, protocol, debugPort); + try { + const reconnectionToken = generateUuid(); + const simpleOptions = await resolveConnectionOptions(options, reconnectionToken, null); + const { protocol, debugPort } = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentExtensionHost(simpleOptions, startArguments), 30 * 1000 /*30s*/); + return new ExtensionHostPersistentConnection(options, startArguments, reconnectionToken, protocol, debugPort); + } catch (err) { + options.logService.error(`[remote-connection] An error occurred in the very first connect attempt, it will be treated as a permanent error! Error:`); + options.logService.error(err); + PersistentConnection.triggerPermanentFailure(); + throw err; + } } export async function connectRemoteAgentTunnel(options: IConnectionOptions, tunnelRemotePort: number): Promise { const simpleOptions = await resolveConnectionOptions(options, generateUuid(), null); - const protocol = await doConnectRemoteAgentTunnel(simpleOptions, { port: tunnelRemotePort }); + const protocol = await connectWithTimeLimit(simpleOptions.logService, doConnectRemoteAgentTunnel(simpleOptions, { port: tunnelRemotePort }), 30 * 1000 /*30s*/); return protocol; } @@ -292,6 +351,13 @@ export type PersistenConnectionEvent = ConnectionGainEvent | ConnectionLostEvent abstract class PersistentConnection extends Disposable { + public static triggerPermanentFailure(): void { + this._permanentFailure = true; + this._instances.forEach(instance => instance._gotoPermanentFailure()); + } + private static _permanentFailure: boolean = false; + private static _instances: PersistentConnection[] = []; + private readonly _onDidStateChange = this._register(new Emitter()); public readonly onDidStateChange = this._onDidStateChange.event; @@ -300,20 +366,24 @@ abstract class PersistentConnection extends Disposable { public readonly protocol: PersistentProtocol; private _isReconnecting: boolean; - private _permanentFailure: boolean; - constructor(options: IConnectionOptions, reconnectionToken: string, protocol: PersistentProtocol) { + constructor(private readonly _connectionType: ConnectionType, options: IConnectionOptions, reconnectionToken: string, protocol: PersistentProtocol) { super(); this._options = options; this.reconnectionToken = reconnectionToken; this.protocol = protocol; this._isReconnecting = false; - this._permanentFailure = false; this._onDidStateChange.fire(new ConnectionGainEvent()); this._register(protocol.onSocketClose(() => this._beginReconnecting())); this._register(protocol.onSocketTimeout(() => this._beginReconnecting())); + + PersistentConnection._instances.push(this); + + if (PersistentConnection._permanentFailure) { + this._gotoPermanentFailure(); + } } private async _beginReconnecting(): Promise { @@ -330,10 +400,12 @@ abstract class PersistentConnection extends Disposable { } private async _runReconnectingLoop(): Promise { - if (this._permanentFailure) { + if (PersistentConnection._permanentFailure) { // no more attempts! return; } + const logPrefix = commonLogPrefix(this._connectionType, this.reconnectionToken, true); + this._options.logService.info(`${logPrefix} starting reconnecting loop. You can get more information with the trace log level.`); this._onDidStateChange.fire(new ConnectionLostEvent()); const TIMES = [5, 5, 10, 10, 10, 10, 10, 30]; const disconnectStartTime = Date.now(); @@ -345,59 +417,68 @@ abstract class PersistentConnection extends Disposable { const sleepPromise = sleep(waitTime); this._onDidStateChange.fire(new ReconnectionWaitEvent(waitTime, sleepPromise)); + this._options.logService.info(`${logPrefix} waiting for ${waitTime} seconds before reconnecting...`); try { await sleepPromise; } catch { } // User canceled timer + if (PersistentConnection._permanentFailure) { + this._options.logService.error(`${logPrefix} permanent failure occurred while running the reconnecting loop.`); + break; + } + // connection was lost, let's try to re-establish it this._onDidStateChange.fire(new ReconnectionRunningEvent()); + this._options.logService.info(`${logPrefix} resolving connection...`); const simpleOptions = await resolveConnectionOptions(this._options, this.reconnectionToken, this.protocol); - await connectWithTimeLimit(this._reconnect(simpleOptions), 30 * 1000 /*30s*/); + this._options.logService.info(`${logPrefix} connecting to ${simpleOptions.host}:${simpleOptions.port}...`); + await connectWithTimeLimit(simpleOptions.logService, this._reconnect(simpleOptions), 30 * 1000 /*30s*/); + this._options.logService.info(`${logPrefix} reconnected!`); this._onDidStateChange.fire(new ConnectionGainEvent()); break; } catch (err) { if (err.code === 'VSCODE_CONNECTION_ERROR') { - console.error(`A permanent connection error occurred`); - console.error(err); - this._permanentFailure = true; - this._onDidStateChange.fire(new ReconnectionPermanentFailureEvent()); - this.protocol.acceptDisconnect(); + this._options.logService.error(`${logPrefix} A permanent error occurred in the reconnecting loop! Will give up now! Error:`); + this._options.logService.error(err); + PersistentConnection.triggerPermanentFailure(); break; } if (Date.now() - disconnectStartTime > ProtocolConstants.ReconnectionGraceTime) { - console.error(`Giving up after reconnection grace time has expired!`); - this._permanentFailure = true; - this._onDidStateChange.fire(new ReconnectionPermanentFailureEvent()); - this.protocol.acceptDisconnect(); + this._options.logService.error(`${logPrefix} An error occurred while reconnecting, but it will be treated as a permanent error because the reconnection grace time has expired! Will give up now! Error:`); + this._options.logService.error(err); + PersistentConnection.triggerPermanentFailure(); break; } if (RemoteAuthorityResolverError.isTemporarilyNotAvailable(err)) { - console.warn(`A temporarily not available error occured while trying to reconnect:`); - console.warn(err); + this._options.logService.info(`${logPrefix} A temporarily not available error occured while trying to reconnect, will try again...`); + this._options.logService.trace(err); // try again! continue; } if ((err.code === 'ETIMEDOUT' || err.code === 'ENETUNREACH' || err.code === 'ECONNREFUSED' || err.code === 'ECONNRESET') && err.syscall === 'connect') { - console.warn(`A connect error occured while trying to reconnect:`); - console.warn(err); + this._options.logService.info(`${logPrefix} A network error occured while trying to reconnect, will try again...`); + this._options.logService.trace(err); // try again! continue; } if (isPromiseCanceledError(err)) { - console.warn(`A cancel error occured while trying to reconnect:`); - console.warn(err); + this._options.logService.info(`${logPrefix} A promise cancelation error occured while trying to reconnect, will try again...`); + this._options.logService.trace(err); // try again! continue; } - console.error(`An error occured while trying to reconnect:`); - console.error(err); - this._permanentFailure = true; - this._onDidStateChange.fire(new ReconnectionPermanentFailureEvent()); - this.protocol.acceptDisconnect(); + this._options.logService.error(`${logPrefix} An unknown error occured while trying to reconnect, since this is an unknown case, it will be treated as a permanent error! Will give up now! Error:`); + this._options.logService.error(err); + PersistentConnection.triggerPermanentFailure(); break; } - } while (!this._permanentFailure); + } while (!PersistentConnection._permanentFailure); + } + + private _gotoPermanentFailure(): void { + this._onDidStateChange.fire(new ReconnectionPermanentFailureEvent()); + safeDisposeProtocolAndSocket(this.protocol); } protected abstract _reconnect(options: ISimpleConnectionOptions): Promise; @@ -408,7 +489,7 @@ export class ManagementPersistentConnection extends PersistentConnection { public readonly client: Client; constructor(options: IConnectionOptions, remoteAuthority: string, clientId: string, reconnectionToken: string, protocol: PersistentProtocol) { - super(options, reconnectionToken, protocol); + super(ConnectionType.Management, options, reconnectionToken, protocol); this.client = this._register(new Client(protocol, { remoteAuthority: remoteAuthority, clientId: clientId @@ -426,7 +507,7 @@ export class ExtensionHostPersistentConnection extends PersistentConnection { public readonly debugPort: number | undefined; constructor(options: IConnectionOptions, startArguments: IRemoteExtensionHostStartParams, reconnectionToken: string, protocol: PersistentProtocol, debugPort: number | undefined) { - super(options, reconnectionToken, protocol); + super(ConnectionType.ExtensionHost, options, reconnectionToken, protocol); this._startArguments = startArguments; this.debugPort = debugPort; } @@ -436,17 +517,19 @@ export class ExtensionHostPersistentConnection extends PersistentConnection { } } -function connectWithTimeLimit(p: Promise, timeLimit: number): Promise { - return new Promise((resolve, reject) => { +function connectWithTimeLimit(logService: ILogService, p: Promise, timeLimit: number): Promise { + return new Promise((resolve, reject) => { let timeout = setTimeout(() => { const err: any = new Error('Time limit reached'); err.code = 'ETIMEDOUT'; err.syscall = 'connect'; + logService.error(`[remote-connection] The time limit has been reached for a connection. Error:`); + logService.error(err); reject(err); }, timeLimit); - p.then(() => { + p.then((value) => { clearTimeout(timeout); - resolve(); + resolve(value); }, (err) => { clearTimeout(timeout); reject(err); @@ -454,6 +537,17 @@ function connectWithTimeLimit(p: Promise, timeLimit: number): Promise { const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, new DiskFileSystemProvider(new NullLogService()), environmentService)); - workspaceContextService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService({}, environmentService, new RemoteAuthorityResolverService(), new SignService(undefined))); + workspaceContextService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService({}, environmentService, new RemoteAuthorityResolverService(), new SignService(undefined), new NullLogService())); return (workspaceContextService).initialize(convertToWorkspacePayload(URI.file(folderDir))); }); }); diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts index 47aaf4a22bb3b..f2e25b99d6612 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts @@ -79,7 +79,8 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH return { host: authority.host, port: authority.port }; } }, - signService: this._signService + signService: this._signService, + logService: this._logService }; return this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority).then((resolverResult) => { diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index 8b5028acac6be..6d31b177acaa7 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -7,9 +7,9 @@ import * as nativeWatchdog from 'native-watchdog'; import * as net from 'net'; import * as minimist from 'vscode-minimist'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; -import { PersistentProtocol, ProtocolConstants, createBufferedEvent } from 'vs/base/parts/ipc/common/ipc.net'; +import { PersistentProtocol, ProtocolConstants, BufferedEmitter } from 'vs/base/parts/ipc/common/ipc.net'; import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import product from 'vs/platform/product/node/product'; import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; @@ -164,8 +164,8 @@ async function createExtHostProtocol(): Promise { return new class implements IMessagePassingProtocol { - private readonly _onMessage = new Emitter(); - readonly onMessage: Event = createBufferedEvent(this._onMessage.event); + private readonly _onMessage = new BufferedEmitter(); + readonly onMessage: Event = this._onMessage.event; private _terminating: boolean; diff --git a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts index fd979ca094c62..ea4a572bf9361 100644 --- a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts @@ -11,6 +11,7 @@ import { IProductService } from 'vs/platform/product/common/product'; import { IWebSocketFactory, BrowserSocketFactory } from 'vs/platform/remote/browser/browserSocketFactory'; import { ISignService } from 'vs/platform/sign/common/sign'; import { ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; +import { ILogService } from 'vs/platform/log/common/log'; export class RemoteAgentService extends AbstractRemoteAgentService implements IRemoteAgentService { @@ -23,12 +24,13 @@ export class RemoteAgentService extends AbstractRemoteAgentService implements IR @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IProductService productService: IProductService, @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, - @ISignService signService: ISignService + @ISignService signService: ISignService, + @ILogService logService: ILogService ) { super(environmentService); this.socketFactory = new BrowserSocketFactory(webSocketFactory); - this._connection = this._register(new RemoteAgentConnection(environmentService.configuration.remoteAuthority!, productService.commit, this.socketFactory, remoteAuthorityResolverService, signService)); + this._connection = this._register(new RemoteAgentConnection(environmentService.configuration.remoteAuthority!, productService.commit, this.socketFactory, remoteAuthorityResolverService, signService, logService)); } getConnection(): IRemoteAgentConnection | null { diff --git a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts index 167e7a5bf145a..977a224418b33 100644 --- a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts @@ -20,6 +20,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; import { Emitter } from 'vs/base/common/event'; import { ISignService } from 'vs/platform/sign/common/sign'; +import { ILogService } from 'vs/platform/log/common/log'; export abstract class AbstractRemoteAgentService extends Disposable { @@ -86,7 +87,8 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon private readonly _commit: string | undefined, private readonly _socketFactory: ISocketFactory, private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, - private readonly _signService: ISignService + private readonly _signService: ISignService, + private readonly _logService: ILogService ) { super(); this.remoteAuthority = remoteAuthority; @@ -124,7 +126,8 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon return { host: authority.host, port: authority.port }; } }, - signService: this._signService + signService: this._signService, + logService: this._logService }; const connection = this._register(await connectRemoteAgentManagement(options, this.remoteAuthority, `renderer`)); this._register(connection.onDidStateChange(e => this._onDidStateChange.fire(e))); diff --git a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts index 82d039a6c5405..dc3d808786a05 100644 --- a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts @@ -12,6 +12,7 @@ import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; import { AbstractRemoteAgentService, RemoteAgentConnection } from 'vs/workbench/services/remote/common/abstractRemoteAgentService'; import { ISignService } from 'vs/platform/sign/common/sign'; import { ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; +import { ILogService } from 'vs/platform/log/common/log'; export class RemoteAgentService extends AbstractRemoteAgentService implements IRemoteAgentService { @@ -22,12 +23,13 @@ export class RemoteAgentService extends AbstractRemoteAgentService implements IR constructor({ remoteAuthority }: IWindowConfiguration, @IEnvironmentService environmentService: IEnvironmentService, @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, - @ISignService signService: ISignService + @ISignService signService: ISignService, + @ILogService logService: ILogService ) { super(environmentService); this.socketFactory = nodeSocketFactory; if (remoteAuthority) { - this._connection = this._register(new RemoteAgentConnection(remoteAuthority, product.commit, nodeSocketFactory, remoteAuthorityResolverService, signService)); + this._connection = this._register(new RemoteAgentConnection(remoteAuthority, product.commit, nodeSocketFactory, remoteAuthorityResolverService, signService, logService)); } } diff --git a/src/vs/workbench/services/remote/node/tunnelService.ts b/src/vs/workbench/services/remote/node/tunnelService.ts index 457411cd670d8..1cab2997a3709 100644 --- a/src/vs/workbench/services/remote/node/tunnelService.ts +++ b/src/vs/workbench/services/remote/node/tunnelService.ts @@ -15,6 +15,7 @@ import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; import { ISignService } from 'vs/platform/sign/common/sign'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; export async function createRemoteTunnel(options: IConnectionOptions, tunnelRemotePort: number): Promise { const tunnel = new NodeRemoteTunnel(options, tunnelRemotePort); @@ -90,7 +91,8 @@ export class TunnelService implements ITunnelService { public constructor( @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, - @ISignService private readonly signService: ISignService + @ISignService private readonly signService: ISignService, + @ILogService private readonly logService: ILogService ) { } @@ -109,7 +111,8 @@ export class TunnelService implements ITunnelService { return { host: authority.host, port: authority.port }; } }, - signService: this.signService + signService: this.signService, + logService: this.logService }; return createRemoteTunnel(options, remotePort); } From 0e3a3d1ead0f4da58bbbb11cd407abac31b43adf Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 23 Aug 2019 17:36:03 +0200 Subject: [PATCH 587/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 27866eca5911d..b9bbc0bef6bcd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "740bacd7f9f039377fb63de6d4dc85f9ae88236a", + "distro": "6d1d8ff3558559807b76a5ad90f268578c89477c", "author": { "name": "Microsoft Corporation" }, From 9acf73fbe54d4a9ebcfe9152bdaa31735aa74621 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 23 Aug 2019 17:39:14 +0200 Subject: [PATCH 588/613] fix tests second attempt --- src/vs/editor/standalone/browser/simpleServices.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index f8c1def46a268..b042afbc213cc 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -45,7 +45,7 @@ import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platf import { ILayoutService, IDimension } from 'vs/platform/layout/browser/layoutService'; import { SimpleServicesNLS } from 'vs/editor/common/standaloneStrings'; import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; -import { basename } from 'vs/base/common/path'; +import { basename } from 'vs/base/common/resources'; export class SimpleModel implements IResolvedTextEditorModel { @@ -671,7 +671,7 @@ export class SimpleUriLabelService implements ILabelService { } getUriBasenameLabel(resource: URI): string { - return basename(this.getUriLabel(resource)); + return basename(resource); } public getWorkspaceLabel(workspace: IWorkspaceIdentifier | URI | IWorkspace, options?: { verbose: boolean; }): string { From 0be6954e8855137045d58b9fe8354944d56a3760 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 23 Aug 2019 10:22:09 -0700 Subject: [PATCH 589/613] Tweak deprecation messag #69335 --- src/vs/workbench/api/common/extHost.api.impl.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 9b3fbf50e0bf3..c5fd74ffe75f4 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -539,9 +539,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }; // namespace: workspace + let warnedRootPathDeprecated = false; const workspace: typeof vscode.workspace = { get rootPath() { - console.warn(`[Deprecation Warning] 'workspace.rootPath' is deprecated and should no longer be used. Please use 'workspace.workspaceFolders' instead. (${extension.publisher}.${extension.name})`); + if (extension.isUnderDevelopment && !warnedRootPathDeprecated) { + warnedRootPathDeprecated = true; + console.warn(`[Deprecation Warning] 'workspace.rootPath' is deprecated and should no longer be used. Please use 'workspace.workspaceFolders' instead. More details: https://aka.ms/vscode-eliminating-rootpath`); + } + return extHostWorkspace.getPath(); }, set rootPath(value) { From cd1ed8165920bdbc127a6ac13254a254f3adacc9 Mon Sep 17 00:00:00 2001 From: Sana Ajani Date: Fri, 23 Aug 2019 11:59:56 -0700 Subject: [PATCH 590/613] distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b9bbc0bef6bcd..f7cc7bc934e7e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "6d1d8ff3558559807b76a5ad90f268578c89477c", + "distro": "9e21b46b0b313fa16ba7810134425a38b64601e2", "author": { "name": "Microsoft Corporation" }, From adbee7d26e051db1e79d8e7760dabe629dedc23a Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Fri, 23 Aug 2019 11:59:48 -0700 Subject: [PATCH 591/613] Update html/css services --- .../css-language-features/server/package.json | 2 +- .../css-language-features/server/yarn.lock | 8 ++++---- .../html-language-features/server/package.json | 4 ++-- .../html-language-features/server/yarn.lock | 16 ++++++++-------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index b27fb14aef7ee..fe8b74901ac7e 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -9,7 +9,7 @@ }, "main": "./out/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^4.0.3-next.3", + "vscode-css-languageservice": "^4.0.3-next.4", "vscode-languageserver": "^5.3.0-next.8" }, "devDependencies": { diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 1f3a45c084421..b440901a68cc5 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -781,10 +781,10 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^4.0.3-next.3: - version "4.0.3-next.3" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.3.tgz#eb7f642f2785d388d74a1a98fd14f7736a11e316" - integrity sha512-6j/y9ccecrq7/APLPEijx+uWHsEdTFH5ZQHG4ZMKjZx6euny27B1wvLCjpxKnZCWcHgmi7cMDLWpUdElvHjjPQ== +vscode-css-languageservice@^4.0.3-next.4: + version "4.0.3-next.4" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.4.tgz#3f0ccf116567650597b5cbfd8ce09846a64dec13" + integrity sha512-9eaKw6ez+l407/uzIho51ElMqGSJcOV+M5B/HmAtdBPSc/chkAfx3r7zXOqrAlLKsBzZNVpnsA3C3YDjyZbrdg== dependencies: vscode-languageserver-types "^3.15.0-next.2" vscode-nls "^4.1.1" diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 9b26165edbdfc..3fa4cd14d3cd7 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,8 +9,8 @@ }, "main": "./out/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^4.0.3-next.3", - "vscode-html-languageservice": "^3.0.4-next.0", + "vscode-css-languageservice": "^4.0.3-next.4", + "vscode-html-languageservice": "^3.0.4-next.1", "vscode-languageserver": "^5.3.0-next.8", "vscode-languageserver-types": "3.15.0-next.2", "vscode-nls": "^4.1.1", diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index f8687c97f6352..94a2693189a73 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -229,19 +229,19 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^4.0.3-next.3: - version "4.0.3-next.3" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.3.tgz#eb7f642f2785d388d74a1a98fd14f7736a11e316" - integrity sha512-6j/y9ccecrq7/APLPEijx+uWHsEdTFH5ZQHG4ZMKjZx6euny27B1wvLCjpxKnZCWcHgmi7cMDLWpUdElvHjjPQ== +vscode-css-languageservice@^4.0.3-next.4: + version "4.0.3-next.4" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.4.tgz#3f0ccf116567650597b5cbfd8ce09846a64dec13" + integrity sha512-9eaKw6ez+l407/uzIho51ElMqGSJcOV+M5B/HmAtdBPSc/chkAfx3r7zXOqrAlLKsBzZNVpnsA3C3YDjyZbrdg== dependencies: vscode-languageserver-types "^3.15.0-next.2" vscode-nls "^4.1.1" vscode-uri "^2.0.3" -vscode-html-languageservice@^3.0.4-next.0: - version "3.0.4-next.0" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.0.tgz#d4f5a103b94753a19b374158212fe734dbe670e8" - integrity sha512-5Z5ITtokWt/zuPKemKEXfC+4XHoQryBAZVAcTwpAel2qqueUwGqjd5ZrVy/2x5GZAdZAipl0BvsTTMkOBS1BFQ== +vscode-html-languageservice@^3.0.4-next.1: + version "3.0.4-next.1" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.1.tgz#fcab383db185cb03cfb10d9039d865a7b243c6e8" + integrity sha512-WgqGH7nDhijveDqrSp9zhcOKUGgOBn4FWO5HzfZV4LnfzaFG7hLMbtbplblJVpqLR6lhTWM8B6E4BlFg/szhWw== dependencies: vscode-languageserver-types "^3.15.0-next.2" vscode-nls "^4.1.1" From aaa794de44ca0a40dd22313e1c185b3f66d9fed8 Mon Sep 17 00:00:00 2001 From: Sana Ajani Date: Fri, 23 Aug 2019 12:05:49 -0700 Subject: [PATCH 592/613] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f7cc7bc934e7e..db4b8dcc29844 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "9e21b46b0b313fa16ba7810134425a38b64601e2", + "distro": "b2ada6eb449d568902c07c04a2fbb73194d7bbdc", "author": { "name": "Microsoft Corporation" }, From 93ca4df136b0a9f4fdefe1fa7cdf1b7a92d2825a Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Fri, 23 Aug 2019 13:16:33 -0700 Subject: [PATCH 593/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index db4b8dcc29844..74fe953e15589 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "b2ada6eb449d568902c07c04a2fbb73194d7bbdc", + "distro": "d2a0db67334421d01633b3bec425a9e8812d3b9a", "author": { "name": "Microsoft Corporation" }, From c9042cb5210290a9a3c88c463c10b2ea4e4923d5 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 23 Aug 2019 15:18:23 -0700 Subject: [PATCH 594/613] Hover actions on gutter in inline diff editor. --- .../editor/browser/widget/diffEditorWidget.ts | 53 ++++++-- .../widget/embeddedCodeEditorWidget.ts | 8 +- .../editor/browser/widget/inlineDiffMargin.ts | 126 ++++++++++++++++++ .../browser/standaloneCodeEditor.ts | 9 +- .../standalone/browser/standaloneEditor.ts | 5 +- 5 files changed, 183 insertions(+), 18 deletions(-) create mode 100644 src/vs/editor/browser/widget/inlineDiffMargin.ts diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index ee32431b77a8a..519cc25cbfd10 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -40,6 +40,9 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { INotificationService } from 'vs/platform/notification/common/notification'; import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow } from 'vs/platform/theme/common/colorRegistry'; import { ITheme, IThemeService, getThemeTypeSelector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; interface IEditorDiffDecorations { decorations: IModelDeltaDecoration[]; @@ -47,7 +50,7 @@ interface IEditorDiffDecorations { } interface IEditorDiffDecorationsWithZones extends IEditorDiffDecorations { - zones: editorBrowser.IViewZone[]; + zones: IMyViewZone[]; } interface IEditorsDiffDecorationsWithZones { @@ -56,8 +59,8 @@ interface IEditorsDiffDecorationsWithZones { } interface IEditorsZones { - original: editorBrowser.IViewZone[]; - modified: editorBrowser.IViewZone[]; + original: IMyViewZone[]; + modified: IMyViewZone[]; } interface IDiffEditorWidgetStyle { @@ -70,11 +73,16 @@ interface IDiffEditorWidgetStyle { class VisualEditorState { private _zones: string[]; + private inlineDiffMargins: InlineDiffMargin[]; private _zonesMap: { [zoneId: string]: boolean; }; private _decorations: string[]; - constructor() { + constructor( + private _contextMenuService: IContextMenuService, + private _clipboardService: IClipboardService + ) { this._zones = []; + this.inlineDiffMargins = []; this._zonesMap = {}; this._decorations = []; } @@ -108,13 +116,22 @@ class VisualEditorState { for (let i = 0, length = this._zones.length; i < length; i++) { viewChangeAccessor.removeZone(this._zones[i]); } + for (let i = 0, length = this.inlineDiffMargins.length; i < length; i++) { + this.inlineDiffMargins[i].dispose(); + } this._zones = []; this._zonesMap = {}; + this.inlineDiffMargins = []; for (let i = 0, length = newDecorations.zones.length; i < length; i++) { - newDecorations.zones[i].suppressMouseDown = true; - let zoneId = viewChangeAccessor.addZone(newDecorations.zones[i]); + const viewZone = newDecorations.zones[i]; + viewZone.suppressMouseDown = false; + let zoneId = viewChangeAccessor.addZone(viewZone); this._zones.push(zoneId); this._zonesMap[String(zoneId)] = true; + + if (newDecorations.zones[i].diff && viewZone.marginDomNode) { + this.inlineDiffMargins.push(new InlineDiffMargin(viewZone.marginDomNode, editor, newDecorations.zones[i].diff!, this._contextMenuService, this._clipboardService)); + } } }); @@ -202,7 +219,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE @IInstantiationService instantiationService: IInstantiationService, @ICodeEditorService codeEditorService: ICodeEditorService, @IThemeService themeService: IThemeService, - @INotificationService notificationService: INotificationService + @INotificationService notificationService: INotificationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IClipboardService clipboardService: IClipboardService ) { super(); @@ -282,8 +301,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._currentlyChangingViewZones = false; this._diffComputationToken = 0; - this._originalEditorState = new VisualEditorState(); - this._modifiedEditorState = new VisualEditorState(); + this._originalEditorState = new VisualEditorState(contextMenuService, clipboardService); + this._modifiedEditorState = new VisualEditorState(contextMenuService, clipboardService); this._isVisible = true; this._isHandlingScrollEvent = false; @@ -1258,6 +1277,7 @@ interface IMyViewZone { minWidthInPx?: number; domNode: HTMLElement | null; marginDomNode?: HTMLElement | null; + diff?: IDiffLinesChange; } class ForeignViewZonesIterator { @@ -1469,12 +1489,12 @@ abstract class ViewZonesComputer { }; } - private static _ensureDomNodes(zones: IMyViewZone[]): editorBrowser.IViewZone[] { + private static _ensureDomNodes(zones: IMyViewZone[]): IMyViewZone[] { return zones.map((z) => { if (!z.domNode) { z.domNode = createFakeLinesDiv(); } - return z; + return z; }); } @@ -1977,8 +1997,10 @@ class InlineViewZonesComputer extends ViewZonesComputer { let lineHeight = this.modifiedEditorConfiguration.lineHeight; const typicalHalfwidthCharacterWidth = this.modifiedEditorConfiguration.fontInfo.typicalHalfwidthCharacterWidth; let maxCharsPerLine = 0; + const originalContent: string[] = []; for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { maxCharsPerLine = Math.max(maxCharsPerLine, this._renderOriginalLine(lineNumber - lineChange.originalStartLineNumber, this.originalModel, this.modifiedEditorConfiguration, this.modifiedEditorTabSize, lineNumber, decorations, sb)); + originalContent.push(this.originalModel.getLineContent(lineNumber)); if (this.renderIndicators) { let index = lineNumber - lineChange.originalStartLineNumber; @@ -2005,7 +2027,14 @@ class InlineViewZonesComputer extends ViewZonesComputer { heightInLines: lineChangeOriginalLength, minWidthInPx: (maxCharsPerLine * typicalHalfwidthCharacterWidth), domNode: domNode, - marginDomNode: marginDomNode + marginDomNode: marginDomNode, + diff: { + originalStartLineNumber: lineChange.originalStartLineNumber, + originalEndLineNumber: lineChange.originalEndLineNumber, + modifiedStartLineNumber: lineChange.modifiedStartLineNumber, + modifiedEndLineNumber: lineChange.modifiedEndLineNumber, + originalContent: originalContent + } }; } diff --git a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts index 1c6f251a3edda..2f36d8e97656d 100644 --- a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts @@ -16,6 +16,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { INotificationService } from 'vs/platform/notification/common/notification'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; export class EmbeddedCodeEditorWidget extends CodeEditorWidget { @@ -74,9 +76,11 @@ export class EmbeddedDiffEditorWidget extends DiffEditorWidget { @IInstantiationService instantiationService: IInstantiationService, @ICodeEditorService codeEditorService: ICodeEditorService, @IThemeService themeService: IThemeService, - @INotificationService notificationService: INotificationService + @INotificationService notificationService: INotificationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IClipboardService clipboardService: IClipboardService ) { - super(domElement, parentEditor.getRawConfiguration(), editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService); + super(domElement, parentEditor.getRawConfiguration(), editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService, contextMenuService, clipboardService); this._parentEditor = parentEditor; this._overwriteOptions = options; diff --git a/src/vs/editor/browser/widget/inlineDiffMargin.ts b/src/vs/editor/browser/widget/inlineDiffMargin.ts new file mode 100644 index 0000000000000..feafa76cc943d --- /dev/null +++ b/src/vs/editor/browser/widget/inlineDiffMargin.ts @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as dom from 'vs/base/browser/dom'; +import { Action } from 'vs/base/common/actions'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { Range } from 'vs/editor/common/core/range'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; + +export interface IDiffLinesChange { + readonly originalStartLineNumber: number; + readonly originalEndLineNumber: number; + readonly modifiedStartLineNumber: number; + readonly modifiedEndLineNumber: number; + readonly originalContent: string[]; +} + +export class InlineDiffMargin extends Disposable { + private readonly _lightBulb: HTMLElement; + + constructor( + marginDomNode: HTMLElement, + public editor: CodeEditorWidget, + public diff: IDiffLinesChange, + private _contextMenuService: IContextMenuService, + private _clipboardService: IClipboardService + ) { + super(); + + // make sure the diff margin shows above overlay. + marginDomNode.style.zIndex = '10'; + + this._lightBulb = document.createElement('div'); + this._lightBulb.className = 'lightbulb-glyph'; + this._lightBulb.style.position = 'absolute'; + const lineHeight = editor.getConfiguration().lineHeight; + const lineFeed = editor.getModel()!.getEOL(); + this._lightBulb.style.right = '0px'; + this._lightBulb.style.visibility = 'hidden'; + this._lightBulb.style.height = `${lineHeight}px`; + marginDomNode.appendChild(this._lightBulb); + + const actions = [ + new Action( + 'diff.clipboard.copyDeletedContent', + nls.localize('diff.clipboard.copyDeletedContent.label', "Copy deleted lines content to clipboard"), + undefined, + true, + async () => { + await this._clipboardService.writeText(diff.originalContent.join(lineFeed) + lineFeed); + } + ) + ]; + + let currentLineNumberOffset = 0; + + const copyLineAction = new Action( + 'diff.clipboard.copyDeletedLineContent', + nls.localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line {0} content to clipboard", diff.originalStartLineNumber), + undefined, + true, + async () => { + await this._clipboardService.writeText(diff.originalContent[currentLineNumberOffset] + lineFeed); + } + ); + + actions.push(copyLineAction); + + const readOnly = editor.getConfiguration().readOnly; + if (!readOnly) { + actions.push(new Action('diff.inline.revertChange', nls.localize('diff.inline.revertChange.label', "Revert this change"), undefined, true, async () => { + editor.executeEdits('diffEditor', [ + { + range: new Range(diff.modifiedStartLineNumber, 1, diff.modifiedEndLineNumber + 1, 1), + text: diff.originalContent.join(lineFeed) + lineFeed + } + ]); + })); + } + + this._register(dom.addStandardDisposableListener(marginDomNode, 'mouseenter', e => { + this._lightBulb.style.visibility = 'visible'; + currentLineNumberOffset = this._updateLightBulbPosition(marginDomNode, e.y, lineHeight); + })); + + this._register(dom.addStandardDisposableListener(marginDomNode, 'mouseleave', e => { + this._lightBulb.style.visibility = 'hidden'; + })); + + this._register(dom.addStandardDisposableListener(marginDomNode, 'mousemove', e => { + currentLineNumberOffset = this._updateLightBulbPosition(marginDomNode, e.y, lineHeight); + })); + + this._register(dom.addStandardDisposableListener(this._lightBulb, 'mousedown', e => { + const { top, height } = dom.getDomNodePagePosition(this._lightBulb); + let pad = Math.floor(lineHeight / 3) + lineHeight; + this._contextMenuService.showContextMenu({ + getAnchor: () => { + return { + x: e.posx, + y: top + height + pad + }; + }, + getActions: () => { + copyLineAction.label = nls.localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line {0} content to clipboard", diff.originalStartLineNumber + currentLineNumberOffset); + return actions; + }, + autoSelectFirstItem: true + }); + })); + } + + private _updateLightBulbPosition(marginDomNode: HTMLElement, y: number, lineHeight: number): number { + const { top } = dom.getDomNodePagePosition(marginDomNode); + const offset = y - top; + const lineNumberOffset = Math.floor(offset / lineHeight); + const newTop = lineNumberOffset * lineHeight; + this._lightBulb.style.top = `${newTop}px`; + return lineNumberOffset; + } +} diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 31a3f56b7042c..d64e0e6e2afbb 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -21,7 +21,7 @@ import { IMenuItem, MenuId, MenuRegistry } from 'vs/platform/actions/common/acti import { CommandsRegistry, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -29,6 +29,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { StandaloneCodeEditorNLS } from 'vs/editor/common/standaloneStrings'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; /** * Description of an action contribution @@ -373,7 +374,9 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon @ICodeEditorService codeEditorService: ICodeEditorService, @IStandaloneThemeService themeService: IStandaloneThemeService, @INotificationService notificationService: INotificationService, - @IConfigurationService configurationService: IConfigurationService + @IConfigurationService configurationService: IConfigurationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IClipboardService clipboardService: IClipboardService ) { applyConfigurationValues(configurationService, options, true); options = options || {}; @@ -381,7 +384,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon options.theme = themeService.setTheme(options.theme); } - super(domElement, options, editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService); + super(domElement, options, editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService, contextMenuService, clipboardService); this._contextViewService = contextViewService; this._configurationService = configurationService; diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 8750db8f94301..280e00cbdf712 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -30,7 +30,7 @@ import { IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standal import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IMarker, IMarkerData } from 'vs/platform/markers/common/markers'; @@ -38,6 +38,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { clearAllFontInfos } from 'vs/editor/browser/config/configuration'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; type Omit = Pick>; @@ -119,6 +120,8 @@ export function createDiffEditor(domElement: HTMLElement, options?: IDiffEditorC services.get(IStandaloneThemeService), services.get(INotificationService), services.get(IConfigurationService), + services.get(IContextMenuService), + services.get(IClipboardService) ); }); } From f664f7597b29136ab1c3ef4fc64f7873e1963dea Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 23 Aug 2019 15:39:49 -0700 Subject: [PATCH 595/613] correct code revert --- .../editor/browser/widget/inlineDiffMargin.ts | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/widget/inlineDiffMargin.ts b/src/vs/editor/browser/widget/inlineDiffMargin.ts index feafa76cc943d..fa5b94f0d7d72 100644 --- a/src/vs/editor/browser/widget/inlineDiffMargin.ts +++ b/src/vs/editor/browser/widget/inlineDiffMargin.ts @@ -74,12 +74,25 @@ export class InlineDiffMargin extends Disposable { const readOnly = editor.getConfiguration().readOnly; if (!readOnly) { actions.push(new Action('diff.inline.revertChange', nls.localize('diff.inline.revertChange.label', "Revert this change"), undefined, true, async () => { - editor.executeEdits('diffEditor', [ - { - range: new Range(diff.modifiedStartLineNumber, 1, diff.modifiedEndLineNumber + 1, 1), - text: diff.originalContent.join(lineFeed) + lineFeed - } - ]); + if (diff.modifiedEndLineNumber === 0) { + // deletion only + const column = editor.getModel()!.getLineMaxColumn(diff.modifiedStartLineNumber); + editor.executeEdits('diffEditor', [ + { + range: new Range(diff.modifiedStartLineNumber, column, diff.modifiedStartLineNumber, column), + text: lineFeed + diff.originalContent.join(lineFeed) + } + ]); + } else { + const column = editor.getModel()!.getLineMaxColumn(diff.modifiedEndLineNumber); + editor.executeEdits('diffEditor', [ + { + range: new Range(diff.modifiedStartLineNumber, 1, diff.modifiedEndLineNumber, column), + text: diff.originalContent.join(lineFeed) + } + ]); + } + })); } From 234f0eae4b2a77a73f45b97b73ca5cecc6d9b35f Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 23 Aug 2019 15:40:49 -0700 Subject: [PATCH 596/613] fix console errors when notification is removed --- .../contrib/remote/electron-browser/remote.contribution.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index 4c3db2da9aad0..9415ec4f051fe 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -331,7 +331,7 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { // Show dialog progressService!.withProgress( { location: ProgressLocation.Dialog, buttons }, - (progress) => { progressReporter = new ProgressReporter(progress); return promise; }, + (progress) => { if (progressReporter) { progressReporter.currentProgress = progress; } return promise; }, (choice?) => { // Handle choice from dialog if (choice === 0 && buttons && reconnectWaitEvent) { @@ -351,7 +351,6 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { // Handle choice from notification if (choice === 0 && buttons && reconnectWaitEvent) { reconnectWaitEvent.skipWait(); - progressReporter!.report(); } else { hideProgress(); } @@ -365,7 +364,6 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { } currentProgressPromiseResolve = null; - progressReporter = null; } connection.onDidStateChange((e) => { @@ -376,6 +374,7 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { switch (e.type) { case PersistentConnectionEventType.ConnectionLost: if (!currentProgressPromiseResolve) { + progressReporter = new ProgressReporter(null); showProgress(ProgressLocation.Dialog, [nls.localize('reconnectNow', "Reconnect Now")]); } @@ -394,6 +393,7 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { break; case PersistentConnectionEventType.ReconnectionPermanentFailure: hideProgress(); + progressReporter = null; dialogService.show(Severity.Error, nls.localize('reconnectionPermanentFailure', "Cannot reconnect. Please reload the window."), [nls.localize('reloadWindow', "Reload Window"), nls.localize('cancel', "Cancel")], { cancelId: 1 }).then(choice => { // Reload the window @@ -404,6 +404,7 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { break; case PersistentConnectionEventType.ConnectionGain: hideProgress(); + progressReporter = null; break; } }); From 3a08a39d9b18be478f38d148f5f337b67cf1763c Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 23 Aug 2019 16:59:35 -0700 Subject: [PATCH 597/613] allow quick input to dismiss modal progress --- .../electron-browser/remote.contribution.ts | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index 9415ec4f051fe..857e42cd54c5c 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -309,7 +309,8 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { @IRemoteAgentService remoteAgentService: IRemoteAgentService, @IProgressService progressService: IProgressService, @IDialogService dialogService: IDialogService, - @ICommandService commandService: ICommandService + @ICommandService commandService: ICommandService, + @IContextKeyService contextKeyService: IContextKeyService ) { const connection = remoteAgentService.getConnection(); if (connection) { @@ -318,6 +319,7 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { let lastLocation: ProgressLocation | null = null; let currentTimer: ReconnectionTimer | null = null; let reconnectWaitEvent: ReconnectionWaitEvent | null = null; + let disposableListener: IDisposable | null = null; function showProgress(location: ProgressLocation, buttons?: string[]) { if (currentProgressPromiseResolve) { @@ -371,6 +373,11 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { currentTimer.dispose(); currentTimer = null; } + + if (disposableListener) { + disposableListener.dispose(); + disposableListener = null; + } switch (e.type) { case PersistentConnectionEventType.ConnectionLost: if (!currentProgressPromiseResolve) { @@ -390,6 +397,20 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { hideProgress(); showProgress(lastLocation || ProgressLocation.Notification); progressReporter!.report(nls.localize('reconnectionRunning', "Attempting to reconnect...")); + + // Register to listen for quick input is opened + disposableListener = contextKeyService.onDidChangeContext((contextKeyChangeEvent) => { + const reconnectInteraction = new Set(['inQuickOpen']); + if (contextKeyChangeEvent.affectsSome(reconnectInteraction)) { + // Need to move from dialog if being shown and user needs to type in a prompt + if (lastLocation === ProgressLocation.Dialog && progressReporter !== null) { + hideProgress(); + showProgress(ProgressLocation.Notification); + progressReporter.report(); + } + } + }); + break; case PersistentConnectionEventType.ReconnectionPermanentFailure: hideProgress(); From 0f54208cfb5d4ae968e34a4188004c3349b77099 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 23 Aug 2019 17:51:36 -0700 Subject: [PATCH 598/613] copy single line without line feed. --- src/vs/editor/browser/widget/inlineDiffMargin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/inlineDiffMargin.ts b/src/vs/editor/browser/widget/inlineDiffMargin.ts index fa5b94f0d7d72..2bca61666b42e 100644 --- a/src/vs/editor/browser/widget/inlineDiffMargin.ts +++ b/src/vs/editor/browser/widget/inlineDiffMargin.ts @@ -65,7 +65,7 @@ export class InlineDiffMargin extends Disposable { undefined, true, async () => { - await this._clipboardService.writeText(diff.originalContent[currentLineNumberOffset] + lineFeed); + await this._clipboardService.writeText(diff.originalContent[currentLineNumberOffset]); } ); From dbfca928e01872391cd7acebfd21011f5e0e0b4b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Aug 2019 18:16:56 -0700 Subject: [PATCH 599/613] Extend dispoable --- .../editor/contrib/parameterHints/parameterHints.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/parameterHints/parameterHints.ts b/src/vs/editor/contrib/parameterHints/parameterHints.ts index eb846502b6383..0401170929299 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHints.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHints.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -18,7 +18,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import * as modes from 'vs/editor/common/modes'; import { TriggerContext } from 'vs/editor/contrib/parameterHints/parameterHintsModel'; -class ParameterHintsController implements IEditorContribution { +class ParameterHintsController extends Disposable implements IEditorContribution { private static readonly ID = 'editor.controller.parameterHints'; @@ -30,8 +30,9 @@ class ParameterHintsController implements IEditorContribution { private readonly widget: ParameterHintsWidget; constructor(editor: ICodeEditor, @IInstantiationService instantiationService: IInstantiationService) { + super(); this.editor = editor; - this.widget = instantiationService.createInstance(ParameterHintsWidget, this.editor); + this.widget = this._register(instantiationService.createInstance(ParameterHintsWidget, this.editor)); } getId(): string { @@ -53,10 +54,6 @@ class ParameterHintsController implements IEditorContribution { trigger(context: TriggerContext): void { this.widget.trigger(context); } - - dispose(): void { - dispose(this.widget); - } } export class TriggerParameterHintsAction extends EditorAction { @@ -76,7 +73,7 @@ export class TriggerParameterHintsAction extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - let controller = ParameterHintsController.get(editor); + const controller = ParameterHintsController.get(editor); if (controller) { controller.trigger({ triggerKind: modes.SignatureHelpTriggerKind.Invoke From 4b93194b76b09ce4dc122740e1373ec1d17228bc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 22 Aug 2019 15:41:37 -0700 Subject: [PATCH 600/613] Make sure we notify webview of view state changes in a consistent order Send events in the following order: - Non-visible - Visible - Active This matches the old notification order for webviews --- src/vs/workbench/api/common/extHostWebview.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index d0eea9511b9ae..168e49abca734 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -299,7 +299,24 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { } public $onDidChangeWebviewPanelViewStates(newStates: WebviewPanelViewStateData): void { - for (const handle of Object.keys(newStates)) { + const handles = Object.keys(newStates); + // Notify webviews of state changes in the following order: + // - Non-visible + // - Visible + // - Active + handles.sort((a, b) => { + const stateA = newStates[a]; + const stateB = newStates[b]; + if (stateA.active) { + return 1; + } + if (stateB.active) { + return -1; + } + return (+stateA.visible) - (+stateB.visible); + }); + + for (const handle of handles) { const panel = this.getWebviewPanel(handle); if (!panel || panel._isDisposed) { continue; From dcb3f60cda959df58365360895469aa7a35d08e0 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 23 Aug 2019 20:39:16 -0700 Subject: [PATCH 601/613] Update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 74fe953e15589..037a6cf41a69c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "d2a0db67334421d01633b3bec425a9e8812d3b9a", + "distro": "b8b5d79d26bc7b35031b45ab31961959c6c199d2", "author": { "name": "Microsoft Corporation" }, From e3b9b8eefc062d68ba8a4b6a817162d132f3b533 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 23 Aug 2019 23:05:49 -0500 Subject: [PATCH 602/613] Re-check opened files while executing refactoring Fixes #79650 --- .../src/features/refactor.ts | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index 6b0d33a2bb99e..f4ed0f6726f64 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -14,7 +14,7 @@ import { VersionDependentRegistration } from '../utils/dependentRegistration'; import TelemetryReporter from '../utils/telemetry'; import * as typeConverters from '../utils/typeConverters'; import FormattingOptionsManager from './fileConfigurationManager'; -import { file } from '../utils/fileSchemes'; +import * as fileSchemes from '../utils/fileSchemes'; const localize = nls.loadMessageBundle(); @@ -30,11 +30,15 @@ class ApplyRefactoringCommand implements Command { public async execute( document: vscode.TextDocument, - file: string, refactor: string, action: string, range: vscode.Range ): Promise { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return false; + } + /* __GDPR__ "refactor.execute" : { "action" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, @@ -81,7 +85,7 @@ class ApplyRefactoringCommand implements Command { const workspaceEdit = new vscode.WorkspaceEdit(); for (const edit of body.edits) { const resource = this.client.toResource(edit.fileName); - if (resource.scheme === file) { + if (resource.scheme === fileSchemes.file) { workspaceEdit.createFile(resource, { ignoreIfExists: true }); } } @@ -95,15 +99,19 @@ class SelectRefactorCommand implements Command { public readonly id = SelectRefactorCommand.ID; constructor( + private readonly client: ITypeScriptServiceClient, private readonly doRefactoring: ApplyRefactoringCommand ) { } public async execute( document: vscode.TextDocument, - file: string, info: Proto.ApplicableRefactorInfo, range: vscode.Range ): Promise { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return false; + } const selected = await vscode.window.showQuickPick(info.actions.map((action): vscode.QuickPickItem => ({ label: action.name, description: action.description, @@ -111,7 +119,7 @@ class SelectRefactorCommand implements Command { if (!selected) { return false; } - return this.doRefactoring.execute(document, file, info.name, selected.label, range); + return this.doRefactoring.execute(document, info.name, selected.label, range); } } @@ -130,7 +138,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { telemetryReporter: TelemetryReporter ) { const doRefactoringCommand = commandManager.register(new ApplyRefactoringCommand(this.client, telemetryReporter)); - commandManager.register(new SelectRefactorCommand(doRefactoringCommand)); + commandManager.register(new SelectRefactorCommand(this.client, doRefactoringCommand)); } public static readonly metadata: vscode.CodeActionProviderMetadata = { @@ -146,29 +154,30 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { if (!this.shouldTrigger(rangeOrSelection, context)) { return undefined; } - - const file = this.client.toOpenedFilePath(document); - if (!file) { + if (!this.client.toOpenedFilePath(document)) { return undefined; } - const args: Proto.GetApplicableRefactorsRequestArgs = typeConverters.Range.toFileRangeRequestArgs(file, rangeOrSelection); + const args: Proto.GetApplicableRefactorsRequestArgs = typeConverters.Range.toFileRangeRequestArgs(fileSchemes.file, rangeOrSelection); const response = await this.client.interruptGetErr(() => { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return undefined; + } this.formattingOptionsManager.ensureConfigurationForDocument(document, token); return this.client.execute('getApplicableRefactors', args, token); }); - if (response.type !== 'response' || !response.body) { + if (!response || response.type !== 'response' || !response.body) { return undefined; } - return this.convertApplicableRefactors(response.body, document, file, rangeOrSelection); + return this.convertApplicableRefactors(response.body, document, rangeOrSelection); } private convertApplicableRefactors( body: Proto.ApplicableRefactorInfo[], document: vscode.TextDocument, - file: string, rangeOrSelection: vscode.Range | vscode.Selection ) { const actions: vscode.CodeAction[] = []; @@ -178,12 +187,12 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { codeAction.command = { title: info.description, command: SelectRefactorCommand.ID, - arguments: [document, file, info, rangeOrSelection] + arguments: [document, info, rangeOrSelection] }; actions.push(codeAction); } else { for (const action of info.actions) { - actions.push(this.refactorActionToCodeAction(action, document, file, info, rangeOrSelection)); + actions.push(this.refactorActionToCodeAction(action, document, info, rangeOrSelection)); } } } @@ -193,7 +202,6 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { private refactorActionToCodeAction( action: Proto.RefactorActionInfo, document: vscode.TextDocument, - file: string, info: Proto.ApplicableRefactorInfo, rangeOrSelection: vscode.Range | vscode.Selection ) { @@ -201,7 +209,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { codeAction.command = { title: action.description, command: ApplyRefactoringCommand.ID, - arguments: [document, file, info.name, action.name, rangeOrSelection], + arguments: [document, info.name, action.name, rangeOrSelection], }; codeAction.isPreferred = TypeScriptRefactorProvider.isPreferred(action); return codeAction; From 17807fa5bfa9bb090513bd89a86381560ad37a8b Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 24 Aug 2019 08:35:01 +0200 Subject: [PATCH 603/613] fix tests --- src/vs/workbench/services/label/common/labelService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 2e9e9e9a1da69..18f171745765f 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -167,7 +167,7 @@ export class LabelService implements ILabelService { return paths.win32.basename(label); } - return paths.posix.basename(label); + return paths.basename(label); } getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string { From b8728661c4c156623325451592b1fd1880849bba Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 24 Aug 2019 16:40:33 +0200 Subject: [PATCH 604/613] labels - restore original functionality of getUriBasenameLabel() --- .../services/label/common/labelService.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 18f171745765f..f383c481e295e 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -129,7 +129,10 @@ export class LabelService implements ILabelService { } getUriLabel(resource: URI, options: { relative?: boolean, noPrefix?: boolean, endWithSeparator?: boolean } = {}): string { - const formatting = this.findFormatting(resource); + return this.doGetUriLabel(resource, this.findFormatting(resource), options); + } + + private doGetUriLabel(resource: URI, formatting?: ResourceLabelFormatting, options: { relative?: boolean, noPrefix?: boolean, endWithSeparator?: boolean } = {}): string { if (!formatting) { return getPathLabel(resource.path, this.environmentService, options.relative ? this.contextService : undefined); } @@ -161,10 +164,13 @@ export class LabelService implements ILabelService { } getUriBasenameLabel(resource: URI): string { - const label = this.getUriLabel(resource); const formatting = this.findFormatting(resource); - if (formatting && formatting.separator === '\\') { - return paths.win32.basename(label); + const label = this.doGetUriLabel(resource, formatting); + if (formatting) { + switch (formatting.separator) { + case paths.win32.sep: return paths.win32.basename(label); + case paths.posix.sep: return paths.posix.basename(label); + } } return paths.basename(label); From f3d976216e6d8fb051a4a7424bf45e1c792d0a56 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 24 Aug 2019 16:53:20 +0200 Subject: [PATCH 605/613] debt - allow to use request helper from base --- src/vs/base/parts/request/browser/request.ts | 74 +++++++++++++++++++ src/vs/base/parts/request/common/request.ts | 30 ++++++++ .../common/extensionGalleryService.ts | 3 +- .../request/browser/requestService.ts | 73 ++---------------- src/vs/platform/request/common/request.ts | 26 +------ src/vs/platform/request/common/requestIpc.ts | 3 +- .../request/electron-main/requestService.ts | 2 +- .../platform/request/node/requestService.ts | 3 +- .../request/browser/requestService.ts | 2 +- 9 files changed, 120 insertions(+), 96 deletions(-) create mode 100644 src/vs/base/parts/request/browser/request.ts create mode 100644 src/vs/base/parts/request/common/request.ts diff --git a/src/vs/base/parts/request/browser/request.ts b/src/vs/base/parts/request/browser/request.ts new file mode 100644 index 0000000000000..f8532bbe6085e --- /dev/null +++ b/src/vs/base/parts/request/browser/request.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { canceled } from 'vs/base/common/errors'; +import { assign } from 'vs/base/common/objects'; +import { VSBuffer, bufferToStream } from 'vs/base/common/buffer'; +import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; + +export function request(options: IRequestOptions, token: CancellationToken): Promise { + if (options.proxyAuthorization) { + options.headers = assign(options.headers || {}, { 'Proxy-Authorization': options.proxyAuthorization }); + } + + const xhr = new XMLHttpRequest(); + return new Promise((resolve, reject) => { + + xhr.open(options.type || 'GET', options.url || '', true, options.user, options.password); + setRequestHeaders(xhr, options); + + xhr.responseType = 'arraybuffer'; + xhr.onerror = e => reject(new Error(xhr.statusText && ('XHR failed: ' + xhr.statusText))); + xhr.onload = (e) => { + resolve({ + res: { + statusCode: xhr.status, + headers: getResponseHeaders(xhr) + }, + stream: bufferToStream(VSBuffer.wrap(new Uint8Array(xhr.response))) + }); + }; + xhr.ontimeout = e => reject(new Error(`XHR timeout: ${options.timeout}ms`)); + + if (options.timeout) { + xhr.timeout = options.timeout; + } + + xhr.send(options.data); + + // cancel + token.onCancellationRequested(() => { + xhr.abort(); + reject(canceled()); + }); + }); +} + +function setRequestHeaders(xhr: XMLHttpRequest, options: IRequestOptions): void { + if (options.headers) { + outer: for (let k in options.headers) { + switch (k) { + case 'User-Agent': + case 'Accept-Encoding': + case 'Content-Length': + // unsafe headers + continue outer; + } + xhr.setRequestHeader(k, options.headers[k]); + } + } +} + +function getResponseHeaders(xhr: XMLHttpRequest): { [name: string]: string } { + const headers: { [name: string]: string } = Object.create(null); + for (const line of xhr.getAllResponseHeaders().split(/\r\n|\n|\r/g)) { + if (line) { + const idx = line.indexOf(':'); + headers[line.substr(0, idx).trim().toLowerCase()] = line.substr(idx + 1).trim(); + } + } + return headers; +} diff --git a/src/vs/base/parts/request/common/request.ts b/src/vs/base/parts/request/common/request.ts new file mode 100644 index 0000000000000..b8f75d72cf6c3 --- /dev/null +++ b/src/vs/base/parts/request/common/request.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBufferReadableStream } from 'vs/base/common/buffer'; + +export interface IHeaders { + [header: string]: string; +} + +export interface IRequestOptions { + type?: string; + url?: string; + user?: string; + password?: string; + headers?: IHeaders; + timeout?: number; + data?: string; + followRedirects?: number; + proxyAuthorization?: string; +} + +export interface IRequestContext { + res: { + headers: IHeaders; + statusCode?: number; + }; + stream: VSBufferReadableStream; +} diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 2d39c51297528..e33d7a5c55d5a 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -9,7 +9,8 @@ import { getGalleryExtensionId, getGalleryExtensionTelemetryData, adoptToGallery import { assign, getOrDefault } from 'vs/base/common/objects'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IPager } from 'vs/base/common/paging'; -import { IRequestService, IRequestOptions, IRequestContext, asJson, asText, IHeaders } from 'vs/platform/request/common/request'; +import { IRequestService, asJson, asText } from 'vs/platform/request/common/request'; +import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request'; import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { generateUuid, isUUID } from 'vs/base/common/uuid'; diff --git a/src/vs/platform/request/browser/requestService.ts b/src/vs/platform/request/browser/requestService.ts index 1cae6c03b3105..0e6e79acfd439 100644 --- a/src/vs/platform/request/browser/requestService.ts +++ b/src/vs/platform/request/browser/requestService.ts @@ -3,13 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRequestOptions, IRequestContext } from 'vs/platform/request/common/request'; +import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { canceled } from 'vs/base/common/errors'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; -import { assign } from 'vs/base/common/objects'; -import { VSBuffer, bufferToStream } from 'vs/base/common/buffer'; +import { request } from 'vs/base/parts/request/browser/request'; /** * This service exposes the `request` API, while using the global @@ -28,69 +26,10 @@ export class RequestService { request(options: IRequestOptions, token: CancellationToken): Promise { this.logService.trace('RequestService#request', options.url); - const authorization = this.configurationService.getValue('http.proxyAuthorization'); - if (authorization) { - options.headers = assign(options.headers || {}, { 'Proxy-Authorization': authorization }); + if (!options.proxyAuthorization) { + options.proxyAuthorization = this.configurationService.getValue('http.proxyAuthorization'); } - const xhr = new XMLHttpRequest(); - return new Promise((resolve, reject) => { - - xhr.open(options.type || 'GET', options.url || '', true, options.user, options.password); - this.setRequestHeaders(xhr, options); - - xhr.responseType = 'arraybuffer'; - xhr.onerror = e => reject(new Error(xhr.statusText && ('XHR failed: ' + xhr.statusText))); - xhr.onload = (e) => { - resolve({ - res: { - statusCode: xhr.status, - headers: this.getResponseHeaders(xhr) - }, - stream: bufferToStream(VSBuffer.wrap(new Uint8Array(xhr.response))) - }); - }; - xhr.ontimeout = e => reject(new Error(`XHR timeout: ${options.timeout}ms`)); - - if (options.timeout) { - xhr.timeout = options.timeout; - } - - xhr.send(options.data); - - // cancel - token.onCancellationRequested(() => { - xhr.abort(); - reject(canceled()); - }); - }); + return request(options, token); } - - private setRequestHeaders(xhr: XMLHttpRequest, options: IRequestOptions): void { - if (options.headers) { - outer: for (let k in options.headers) { - switch (k) { - case 'User-Agent': - case 'Accept-Encoding': - case 'Content-Length': - // unsafe headers - continue outer; - } - xhr.setRequestHeader(k, options.headers[k]); - - } - } - } - - private getResponseHeaders(xhr: XMLHttpRequest): { [name: string]: string } { - const headers: { [name: string]: string } = Object.create(null); - for (const line of xhr.getAllResponseHeaders().split(/\r\n|\n|\r/g)) { - if (line) { - const idx = line.indexOf(':'); - headers[line.substr(0, idx).trim().toLowerCase()] = line.substr(idx + 1).trim(); - } - } - return headers; - } - -} \ No newline at end of file +} diff --git a/src/vs/platform/request/common/request.ts b/src/vs/platform/request/common/request.ts index 31e3c314242c1..7ec2f5f3742f3 100644 --- a/src/vs/platform/request/common/request.ts +++ b/src/vs/platform/request/common/request.ts @@ -8,33 +8,11 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { VSBufferReadableStream, streamToBuffer } from 'vs/base/common/buffer'; +import { streamToBuffer } from 'vs/base/common/buffer'; +import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; export const IRequestService = createDecorator('requestService'); -export interface IHeaders { - [header: string]: string; -} - -export interface IRequestOptions { - type?: string; - url?: string; - user?: string; - password?: string; - headers?: IHeaders; - timeout?: number; - data?: string; - followRedirects?: number; -} - -export interface IRequestContext { - res: { - headers: IHeaders; - statusCode?: number; - }; - stream: VSBufferReadableStream; -} - export interface IRequestService { _serviceBrand: any; diff --git a/src/vs/platform/request/common/requestIpc.ts b/src/vs/platform/request/common/requestIpc.ts index 8f55326170771..22acbce9dee06 100644 --- a/src/vs/platform/request/common/requestIpc.ts +++ b/src/vs/platform/request/common/requestIpc.ts @@ -5,7 +5,8 @@ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; -import { IRequestService, IRequestOptions, IRequestContext, IHeaders } from 'vs/platform/request/common/request'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request'; import { CancellationToken } from 'vs/base/common/cancellation'; import { VSBuffer, bufferToStream, streamToBuffer } from 'vs/base/common/buffer'; diff --git a/src/vs/platform/request/electron-main/requestService.ts b/src/vs/platform/request/electron-main/requestService.ts index cd27b9fbf5d43..de9eacfd44794 100644 --- a/src/vs/platform/request/electron-main/requestService.ts +++ b/src/vs/platform/request/electron-main/requestService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRequestOptions, IRequestContext } from 'vs/platform/request/common/request'; +import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; import { RequestService as NodeRequestService, IRawRequestFunction } from 'vs/platform/request/node/requestService'; import { assign } from 'vs/base/common/objects'; import { net } from 'electron'; diff --git a/src/vs/platform/request/node/requestService.ts b/src/vs/platform/request/node/requestService.ts index 8ca875f6fa9fd..07a32d78f448c 100644 --- a/src/vs/platform/request/node/requestService.ts +++ b/src/vs/platform/request/node/requestService.ts @@ -13,7 +13,8 @@ import { assign } from 'vs/base/common/objects'; import { isBoolean, isNumber } from 'vs/base/common/types'; import { canceled } from 'vs/base/common/errors'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IRequestOptions, IRequestContext, IRequestService, IHTTPConfiguration } from 'vs/platform/request/common/request'; +import { IRequestService, IHTTPConfiguration } from 'vs/platform/request/common/request'; +import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; import { getProxyAgent, Agent } from 'vs/platform/request/node/proxy'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; diff --git a/src/vs/workbench/services/request/browser/requestService.ts b/src/vs/workbench/services/request/browser/requestService.ts index cebf1654ecaa8..13450f58132cb 100644 --- a/src/vs/workbench/services/request/browser/requestService.ts +++ b/src/vs/workbench/services/request/browser/requestService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRequestOptions, IRequestContext } from 'vs/platform/request/common/request'; +import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; From b4679a37f6d67cba8aeb0522eae5f7e544bb54b1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 24 Aug 2019 17:12:30 +0200 Subject: [PATCH 606/613] debt - move some environment to workbench --- .../environment/common/environment.ts | 15 +----------- .../environment/node/environmentService.ts | 14 ----------- .../contrib/update/electron-browser/update.ts | 2 +- .../contrib/webview/browser/webviewElement.ts | 8 +++---- .../electron-browser/gettingStarted.ts | 4 ++-- .../environment/browser/environmentService.ts | 8 ++----- .../environment/common/environmentService.ts | 14 +++++++++-- .../environment/node/environmentService.ts | 23 +++++++++++++------ .../common/extensionHostProcessManager.ts | 4 ++-- .../common/remoteExtensionHostClient.ts | 4 ++-- .../services/search/node/searchService.ts | 7 +++--- 11 files changed, 46 insertions(+), 57 deletions(-) diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index b13a82f4447ef..3b45568e1b8f4 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -140,24 +140,15 @@ export interface IEnvironmentService { extensionTestsLocationURI?: URI; debugExtensionHost: IExtensionHostDebugParams; - debugSearch: IDebugParams; - - logExtensionHostCommunication: boolean; isBuilt: boolean; wait: boolean; status: boolean; - // logging log?: string; logsPath: string; verbose: boolean; - skipGettingStarted: boolean | undefined; - skipReleaseNotes: boolean | undefined; - - skipAddToRecentlyOpened: boolean; - mainIPCHandle: string; sharedIPCHandle: string; @@ -170,9 +161,5 @@ export interface IEnvironmentService { driverHandle?: string; driverVerbose: boolean; - webviewEndpoint?: string; - readonly webviewResourceRoot: string; - readonly webviewCspSource: string; - - readonly galleryMachineIdResource?: URI; + galleryMachineIdResource?: URI; } diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index c5c4e66272c48..a07c4c8f483e6 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -236,26 +236,15 @@ export class EnvironmentService implements IEnvironmentService { return false; } - get skipGettingStarted(): boolean { return !!this._args['skip-getting-started']; } - - get skipReleaseNotes(): boolean { return !!this._args['skip-release-notes']; } - - get skipAddToRecentlyOpened(): boolean { return !!this._args['skip-add-to-recently-opened']; } - @memoize get debugExtensionHost(): IExtensionHostDebugParams { return parseExtensionHostPort(this._args, this.isBuilt); } - @memoize - get debugSearch(): IDebugParams { return parseSearchPort(this._args, this.isBuilt); } - get isBuilt(): boolean { return !process.env['VSCODE_DEV']; } get verbose(): boolean { return !!this._args.verbose; } get log(): string | undefined { return this._args.log; } get wait(): boolean { return !!this._args.wait; } - get logExtensionHostCommunication(): boolean { return !!this._args.logExtensionHostCommunication; } - get status(): boolean { return !!this._args.status; } @memoize @@ -276,9 +265,6 @@ export class EnvironmentService implements IEnvironmentService { get driverHandle(): string | undefined { return this._args['driver']; } get driverVerbose(): boolean { return !!this._args['driver-verbose']; } - readonly webviewResourceRoot = 'vscode-resource:{{resource}}'; - readonly webviewCspSource = 'vscode-resource:'; - constructor(private _args: ParsedArgs, private _execPath: string) { if (!process.env['VSCODE_LOGS']) { const key = toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''); diff --git a/src/vs/workbench/contrib/update/electron-browser/update.ts b/src/vs/workbench/contrib/update/electron-browser/update.ts index 64d4320961276..d429d21347def 100644 --- a/src/vs/workbench/contrib/update/electron-browser/update.ts +++ b/src/vs/workbench/contrib/update/electron-browser/update.ts @@ -119,7 +119,7 @@ export class ProductContribution implements IWorkbenchContribution { @IStorageService storageService: IStorageService, @IInstantiationService instantiationService: IInstantiationService, @INotificationService notificationService: INotificationService, - @IEnvironmentService environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IOpenerService openerService: IOpenerService, @IConfigurationService configurationService: IConfigurationService, @IWindowService windowService: IWindowService, diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index b18f47f0f9690..9364cf5c6b47f 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -7,7 +7,7 @@ import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileService } from 'vs/platform/files/common/files'; import { Disposable } from 'vs/base/common/lifecycle'; import { areWebviewInputOptionsEqual } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; @@ -40,14 +40,14 @@ export class IFrameWebview extends Disposable implements Webview { contentOptions: WebviewContentOptions, @IThemeService themeService: IThemeService, @ITunnelService tunnelService: ITunnelService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly _configurationService: IConfigurationService, ) { super(); const useExternalEndpoint = this._configurationService.getValue('webview.experimental.useExternalEndpoint'); - if (typeof environmentService.webviewEndpoint !== 'string' && !useExternalEndpoint) { + if (!useExternalEndpoint && (!environmentService.options || typeof environmentService.options.webviewEndpoint !== 'string')) { throw new Error('To use iframe based webviews, you must configure `environmentService.webviewEndpoint`'); } @@ -145,7 +145,7 @@ export class IFrameWebview extends Disposable implements Webview { private get endpoint(): string { const useExternalEndpoint = this._configurationService.getValue('webview.experimental.useExternalEndpoint'); - const baseEndpoint = useExternalEndpoint ? 'https://{{uuid}}.vscode-webview-test.com/8fa811108f0f0524c473020ef57b6620f6c201e1' : this.environmentService.webviewEndpoint!; + const baseEndpoint = useExternalEndpoint ? 'https://{{uuid}}.vscode-webview-test.com/8fa811108f0f0524c473020ef57b6620f6c201e1' : this.environmentService.options!.webviewEndpoint!; const endpoint = baseEndpoint.replace('{{uuid}}', this.id); if (endpoint[endpoint.length - 1] === '/') { return endpoint.slice(0, endpoint.length - 1); diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts index 200de4fa5c9b7..6bcad15f718c7 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts @@ -6,7 +6,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import * as platform from 'vs/base/common/platform'; import product from 'vs/platform/product/node/product'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -21,7 +21,7 @@ export class GettingStarted implements IWorkbenchContribution { constructor( @IStorageService private readonly storageService: IStorageService, - @IEnvironmentService environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IOpenerService private readonly openerService: IOpenerService ) { diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index c61e1974692e4..7c3b6ae53e0a5 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -96,11 +96,9 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment break: false }; - this.webviewEndpoint = options.webviewEndpoint; this.untitledWorkspacesHome = URI.from({ scheme: Schemas.untitled, path: 'Workspaces' }); if (document && document.location && document.location.search) { - const map = new Map(); const query = document.location.search.substring(1); const vars = query.split('&'); @@ -170,7 +168,6 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment verbose: boolean; skipGettingStarted: boolean; skipReleaseNotes: boolean; - skipAddToRecentlyOpened: boolean; mainIPCHandle: string; sharedIPCHandle: string; nodeCachedDataDir?: string; @@ -179,16 +176,15 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment disableCrashReporter: boolean; driverHandle?: string; driverVerbose: boolean; - webviewEndpoint?: string; galleryMachineIdResource?: URI; readonly logFile: URI; get webviewResourceRoot(): string { - return this.webviewEndpoint ? this.webviewEndpoint + '/vscode-resource{{resource}}' : 'vscode-resource:{{resource}}'; + return this.options.webviewEndpoint ? `${this.options.webviewEndpoint}/vscode-resource{{resource}}` : 'vscode-resource:{{resource}}'; } get webviewCspSource(): string { - return this.webviewEndpoint ? this.webviewEndpoint : 'vscode-resource:'; + return this.options.webviewEndpoint ? this.options.webviewEndpoint : 'vscode-resource:'; } } diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index 967ad5e43cfd4..4ed892d7b0277 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -5,7 +5,7 @@ import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService, IDebugParams } from 'vs/platform/environment/common/environment'; import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; import { URI } from 'vs/base/common/uri'; @@ -17,7 +17,17 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { readonly configuration: IWindowConfiguration; + readonly options?: IWorkbenchConstructionOptions; + readonly logFile: URI; + readonly logExtensionHostCommunication: boolean; + + readonly debugSearch: IDebugParams; + + readonly webviewResourceRoot: string; + readonly webviewCspSource: string; + + readonly skipGettingStarted: boolean | undefined; + readonly skipReleaseNotes: boolean | undefined; - readonly options?: IWorkbenchConstructionOptions; } diff --git a/src/vs/workbench/services/environment/node/environmentService.ts b/src/vs/workbench/services/environment/node/environmentService.ts index 0a46f3fa8e574..bedcfdc999524 100644 --- a/src/vs/workbench/services/environment/node/environmentService.ts +++ b/src/vs/workbench/services/environment/node/environmentService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { EnvironmentService, parseSearchPort } from 'vs/platform/environment/node/environmentService'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { memoize } from 'vs/base/common/decorators'; @@ -12,27 +12,36 @@ import { Schemas } from 'vs/base/common/network'; import { toBackupWorkspaceResource } from 'vs/workbench/services/backup/common/backup'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { join } from 'vs/base/common/path'; +import { IDebugParams } from 'vs/platform/environment/common/environment'; export class WorkbenchEnvironmentService extends EnvironmentService implements IWorkbenchEnvironmentService { _serviceBrand!: ServiceIdentifier; + readonly webviewResourceRoot = 'vscode-resource:{{resource}}'; + readonly webviewCspSource = 'vscode-resource:'; + constructor( - private _configuration: IWindowConfiguration, + readonly configuration: IWindowConfiguration, execPath: string ) { - super(_configuration, execPath); + super(configuration, execPath); - this._configuration.backupWorkspaceResource = this._configuration.backupPath ? toBackupWorkspaceResource(this._configuration.backupPath, this) : undefined; + this.configuration.backupWorkspaceResource = this.configuration.backupPath ? toBackupWorkspaceResource(this.configuration.backupPath, this) : undefined; } - get configuration(): IWindowConfiguration { - return this._configuration; - } + get skipGettingStarted(): boolean { return !!this.args['skip-getting-started']; } + + get skipReleaseNotes(): boolean { return !!this.args['skip-release-notes']; } @memoize get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.userData }); } @memoize get logFile(): URI { return URI.file(join(this.logsPath, `renderer${this.configuration.windowId}.log`)); } + + get logExtensionHostCommunication(): boolean { return !!this.args.logExtensionHostCommunication; } + + @memoize + get debugSearch(): IDebugParams { return parseSearchPort(this.args, this.isBuilt); } } diff --git a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts index f8067e33e0d1c..44f76d95fd03e 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ExtHostCustomersRegistry } from 'vs/workbench/api/common/extHostCustomers'; import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/common/extHost.protocol'; @@ -59,7 +59,7 @@ export class ExtensionHostProcessManager extends Disposable { private readonly _remoteAuthority: string, initialActivationEvents: string[], @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, ) { super(); this._extensionHostProcessFinishedActivateEvents = Object.create(null); diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts index f2e25b99d6612..bcb46463c2c8b 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts @@ -5,7 +5,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILogService } from 'vs/platform/log/common/log'; import { connectRemoteAgentExtensionHost, IRemoteExtensionHostStartParams, IConnectionOptions, ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; @@ -49,7 +49,7 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH private readonly _initDataProvider: IInitDataProvider, private readonly _socketFactory: ISocketFactory, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, @ILogService private readonly _logService: ILogService, diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 53de732e0111b..9b00ee68b6356 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -12,7 +12,8 @@ import { URI as uri } from 'vs/base/common/uri'; import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IDebugParams, IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IDebugParams } from 'vs/platform/environment/common/environment'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { FileMatch, IFileMatch, IFileQuery, IProgressMessage, IRawSearchService, ISearchComplete, ISearchConfiguration, ISearchProgressItem, ISearchResultProvider, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess, ITextQuery, ISearchService } from 'vs/workbench/services/search/common/search'; @@ -35,7 +36,7 @@ export class LocalSearchService extends SearchService { @ILogService logService: ILogService, @IExtensionService extensionService: IExtensionService, @IFileService fileService: IFileService, - @IEnvironmentService readonly environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService readonly environmentService: IWorkbenchEnvironmentService, @IInstantiationService readonly instantiationService: IInstantiationService ) { super(modelService, untitledEditorService, editorService, telemetryService, logService, extensionService, fileService); @@ -204,4 +205,4 @@ export class DiskSearch implements ISearchResultProvider { } } -registerSingleton(ISearchService, LocalSearchService, true); \ No newline at end of file +registerSingleton(ISearchService, LocalSearchService, true); From 21ce78cf25a7a3b82502f0fc9e764e7840b315b3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 24 Aug 2019 17:14:14 +0200 Subject: [PATCH 607/613] web - lift product config to be proper API --- src/vs/code/browser/workbench/workbench.html | 3 +- src/vs/workbench/browser/web.main.ts | 82 +++++++++++--------- src/vs/workbench/workbench.web.api.ts | 6 ++ 3 files changed, 51 insertions(+), 40 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 9c7defe5af3a7..663cc643f97fd 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -16,9 +16,8 @@ - + - diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 6deb092695426..f5944ce974af1 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -18,7 +18,7 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteA import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { IFileService, IFileSystemProvider } from 'vs/platform/files/common/files'; +import { IFileService } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; import { Schemas } from 'vs/base/common/network'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -124,7 +124,7 @@ class CodeRendererMain extends Disposable { const logService = new BufferLogService(); serviceCollection.set(ILogService, logService); - const payload = await this.resolveWorkspaceInitializationPayload(); + const payload = this.resolveWorkspaceInitializationPayload(); // Environment const environmentService = new BrowserWorkbenchEnvironmentService({ workspaceId: payload.id, logsPath, ...this.configuration }); @@ -149,6 +149,34 @@ class CodeRendererMain extends Disposable { // Files const fileService = this._register(new FileService(logService)); serviceCollection.set(IFileService, fileService); + this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath); + + // Long running services (workspace, config, storage) + const services = await Promise.all([ + this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, logService).then(service => { + + // Workspace + serviceCollection.set(IWorkspaceContextService, service); + + // Configuration + serviceCollection.set(IConfigurationService, service); + + return service; + }), + + this.createStorageService(payload, environmentService, fileService, logService).then(service => { + + // Storage + serviceCollection.set(IStorageService, service); + + return service; + }) + ]); + + return { serviceCollection, logService, storageService: services[1] }; + } + + private registerFileSystemProviders(environmentService: IWorkbenchEnvironmentService, fileService: IFileService, remoteAgentService: IRemoteAgentService, logService: BufferLogService, logsPath: URI): void { // Logger const indexedDBLogProvider = new IndexedDBLogProvider(logsPath.scheme); @@ -169,61 +197,38 @@ class CodeRendererMain extends Disposable { logService.logger = new MultiplexLogService([consoleLogService, fileLogService]); })(); - // User Data Provider - let userDataProvider: IFileSystemProvider | undefined = this.configuration.userDataProvider; const connection = remoteAgentService.getConnection(); if (connection) { + + // Remote file system const channel = connection.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME); const remoteFileSystemProvider = this._register(new RemoteExtensionsFileSystemProvider(channel, remoteAgentService.getEnvironment())); - fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider); - if (!userDataProvider) { + if (!this.configuration.userDataProvider) { const remoteUserDataUri = this.getRemoteUserDataUri(); if (remoteUserDataUri) { - userDataProvider = this._register(new FileUserDataProvider(remoteUserDataUri, joinPath(remoteUserDataUri, BACKUPS), remoteFileSystemProvider, environmentService)); + this.configuration.userDataProvider = this._register(new FileUserDataProvider(remoteUserDataUri, joinPath(remoteUserDataUri, BACKUPS), remoteFileSystemProvider, environmentService)); } } } - if (!userDataProvider) { - userDataProvider = this._register(new InMemoryUserDataProvider()); - } - fileService.registerProvider(Schemas.userData, userDataProvider); - - // Long running services (workspace, config, storage) - const services = await Promise.all([ - this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, logService).then(service => { - - // Workspace - serviceCollection.set(IWorkspaceContextService, service); - - // Configuration - serviceCollection.set(IConfigurationService, service); - - return service; - }), - this.createStorageService(payload, environmentService, fileService, logService).then(service => { - - // Storage - serviceCollection.set(IStorageService, service); - - return service; - }) - ]); - - return { serviceCollection, logService, storageService: services[1] }; + // User data + if (!this.configuration.userDataProvider) { + this.configuration.userDataProvider = this._register(new InMemoryUserDataProvider()); + } + fileService.registerProvider(Schemas.userData, this.configuration.userDataProvider); } private createProductService(): IProductService { - const element = document.getElementById('vscode-remote-product-configuration'); - const productConfiguration: IProductConfiguration = { - ...element ? JSON.parse(element.getAttribute('data-settings')!) : { + const productConfiguration = { + ...this.configuration.productConfiguration ? this.configuration.productConfiguration : { version: '1.38.0-unknown', nameLong: 'Unknown', extensionAllowedProposedApi: [], }, ...{ urlProtocol: '' } - }; + } as IProductConfiguration; + return { _serviceBrand: undefined, ...productConfiguration }; } @@ -280,6 +285,7 @@ class CodeRendererMain extends Disposable { return joinPath(URI.revive(JSON.parse(remoteUserDataPath)), 'User'); } } + return null; } } diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 844ff1da56a73..4bdfb542d9f3b 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -11,6 +11,7 @@ import { IWebSocketFactory } from 'vs/platform/remote/browser/browserSocketFacto import { ICredentialsProvider } from 'vs/workbench/services/credentials/browser/credentialsService'; import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService'; +import { IProductConfiguration } from 'vs/platform/product/common/product'; export interface IWorkbenchConstructionOptions { @@ -71,6 +72,11 @@ export interface IWorkbenchConstructionOptions { * Experimental: Support for URL callbacks. */ urlCallbackProvider?: IURLCallbackProvider; + + /** + * Experimental: Support for product configuration. + */ + productConfiguration?: IProductConfiguration; } /** From 2b37012c475314879e9980a2cc85e4cd1a1a58ac Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 24 Aug 2019 17:23:18 +0200 Subject: [PATCH 608/613] :up: distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 037a6cf41a69c..e489e9b063185 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.38.0", - "distro": "b8b5d79d26bc7b35031b45ab31961959c6c199d2", + "distro": "30060f8ffc31f3de68146de3df305a71973ae1db", "author": { "name": "Microsoft Corporation" }, From aa02f856bfce711dd2aa83309b97de0cf2b5722f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 24 Aug 2019 17:51:24 +0200 Subject: [PATCH 609/613] fix tests --- .../configurationResolverService.test.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts index dd668c74f6d07..66236f88de864 100644 --- a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts @@ -20,7 +20,6 @@ import * as Types from 'vs/base/common/types'; import { EditorType } from 'vs/editor/common/editorCommon'; import { Selection } from 'vs/editor/common/core/selection'; import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; -import { parseArgs } from 'vs/platform/environment/node/argv'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -632,11 +631,7 @@ class MockInputsConfigurationService extends TestConfigurationService { class MockWorkbenchEnvironmentService extends WorkbenchEnvironmentService { - constructor(private env: platform.IProcessEnvironment) { - super(parseArgs(process.argv) as IWindowConfiguration, process.execPath); - } - - get configuration(): IWindowConfiguration { - return { userEnv: this.env } as IWindowConfiguration; + constructor(env: platform.IProcessEnvironment) { + super({ userEnv: env } as IWindowConfiguration, process.execPath); } } From 453aed2249ea3c730f639770147cd8d2b1681a91 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 26 Aug 2019 08:07:52 +0200 Subject: [PATCH 610/613] fix #79784 --- .../contrib/files/browser/views/openEditorsView.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 96be5e2659b93..774d54ef6269c 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -28,7 +28,7 @@ import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListDragAn import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; @@ -349,9 +349,9 @@ export class OpenEditorsView extends ViewletPanel { const preserveActivateGroup = options.sideBySide && options.preserveFocus; // needed for https://github.com/Microsoft/vscode/issues/42399 if (!preserveActivateGroup) { - this.editorGroupService.activateGroup(element.groupId); // needed for https://github.com/Microsoft/vscode/issues/6672 + this.editorGroupService.activateGroup(element.group); // needed for https://github.com/Microsoft/vscode/issues/6672 } - this.editorService.openEditor(element.editor, options, options.sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => { + this.editorService.openEditor(element.editor, options, options.sideBySide ? SIDE_GROUP : element.group).then(editor => { if (editor && !preserveActivateGroup && editor.group) { this.editorGroupService.activateGroup(editor.group); } From fc0e2e23528c098a77cd47afbfda0d89a644de7d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 26 Aug 2019 08:10:06 +0200 Subject: [PATCH 611/613] fix #79633 --- .../workbench/browser/parts/editor/editor.ts | 1 + .../browser/parts/editor/editorGroupView.ts | 33 ++++++----- .../browser/parts/editor/editorPart.ts | 25 ++++++--- .../services/editor/browser/editorService.ts | 56 ++++++++----------- .../preferences/browser/preferencesService.ts | 2 +- .../workbench/test/workbenchTestServices.ts | 4 ++ 6 files changed, 65 insertions(+), 56 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 634812bfa8415..b2e46d001d6b3 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -94,6 +94,7 @@ export interface IEditorGroupsAccessor { getGroups(order: GroupsOrder): IEditorGroupView[]; activateGroup(identifier: IEditorGroupView | GroupIdentifier): IEditorGroupView; + restoreGroup(identifier: IEditorGroupView | GroupIdentifier): IEditorGroupView; addGroup(location: IEditorGroupView | GroupIdentifier, direction: GroupDirection, options?: IAddGroupOptions): IEditorGroupView; mergeGroup(group: IEditorGroupView | GroupIdentifier, target: IEditorGroupView | GroupIdentifier, options?: IMergeGroupOptions): IEditorGroupView; diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index dcfc2b8e358e6..fb1e07119c70f 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -481,7 +481,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private onDidEditorOpen(editor: EditorInput): void { - // Telemetry /* __GDPR__ "editorOpened" : { "${include}": [ @@ -520,14 +519,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } }); - // Telemetry /* __GDPR__ - "editorClosed" : { - "${include}": [ - "${EditorTelemetryDescriptor}" - ] - } - */ + "editorClosed" : { + "${include}": [ + "${EditorTelemetryDescriptor}" + ] + } + */ this.telemetryService.publicLog('editorClosed', this.toEditorTelemetryDescriptor(event.editor)); // Update container @@ -833,12 +831,19 @@ export class EditorGroupView extends Themable implements IEditorGroupView { openEditorOptions.active = true; } - // Set group active unless we open inactive or preserve focus - // Do this before we open the editor in the group to prevent a false - // active editor change event before the editor is loaded - // (see https://github.com/Microsoft/vscode/issues/51679) - if (openEditorOptions.active && (!options || !options.preserveFocus)) { - this.accessor.activateGroup(this); + if (openEditorOptions.active) { + // Set group active unless we are instructed to preserveFocus. Always + // make sure to restore a minimized group though in order to fix + // https://github.com/microsoft/vscode/issues/79633 + // + // Do this before we open the editor in the group to prevent a false + // active editor change event before the editor is loaded + // (see https://github.com/Microsoft/vscode/issues/51679) + if (options && options.preserveFocus) { + this.accessor.restoreGroup(this); + } else { + this.accessor.activateGroup(this); + } } // Actually move the editor if a specific index is provided and we figure diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 8bb14cc38a3c3..5607b641184eb 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -325,6 +325,13 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro return groupView; } + restoreGroup(group: IEditorGroupView | GroupIdentifier): IEditorGroupView { + const groupView = this.assertGroupView(group); + this.doRestoreGroup(groupView); + + return groupView; + } + getSize(group: IEditorGroupView | GroupIdentifier): { width: number, height: number } { const groupView = this.assertGroupView(group); @@ -337,7 +344,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro this.gridWidget.resizeView(groupView, size); } - arrangeGroups(arrangement: GroupsArrangement): void { + arrangeGroups(arrangement: GroupsArrangement, target = this.activeGroup): void { if (this.count < 2) { return; // require at least 2 groups to show } @@ -351,10 +358,10 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro this.gridWidget.distributeViewSizes(); break; case GroupsArrangement.MINIMIZE_OTHERS: - this.gridWidget.maximizeViewSize(this.activeGroup); + this.gridWidget.maximizeViewSize(target); break; case GroupsArrangement.TOGGLE: - if (this.isGroupMaximized(this.activeGroup)) { + if (this.isGroupMaximized(target)) { this.arrangeGroups(GroupsArrangement.EVEN); } else { this.arrangeGroups(GroupsArrangement.MINIMIZE_OTHERS); @@ -576,15 +583,19 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro group.setActive(true); // Maximize the group if it is currently minimized + this.doRestoreGroup(group); + + // Event + this._onDidActiveGroupChange.fire(group); + } + + private doRestoreGroup(group: IEditorGroupView): void { if (this.gridWidget) { const viewSize = this.gridWidget.getViewSize(group); if (viewSize.width === group.minimumWidth || viewSize.height === group.minimumHeight) { - this.arrangeGroups(GroupsArrangement.MINIMIZE_OTHERS); + this.arrangeGroups(GroupsArrangement.MINIMIZE_OTHERS, group); } } - - // Event - this._onDidActiveGroupChange.fire(group); } private doUpdateMostRecentActive(group: IEditorGroupView, makeMostRecentlyActive?: boolean): void { diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 22375af730ee0..7842223793642 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -19,7 +19,7 @@ import { basename } from 'vs/base/common/resources'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { localize } from 'vs/nls'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, GroupChangeKind, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IResourceEditor, ACTIVE_GROUP_TYPE, SIDE_GROUP_TYPE, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IVisibleEditor, IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IResourceEditor, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IVisibleEditor, IEditorService, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { coalesce } from 'vs/base/common/arrays'; @@ -29,13 +29,14 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; -type ICachedEditorInput = ResourceEditorInput | IFileEditorInput | DataUriEditorInput; +type CachedEditorInput = ResourceEditorInput | IFileEditorInput | DataUriEditorInput; +type OpenInEditorGroup = IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE; export class EditorService extends Disposable implements EditorServiceImpl { _serviceBrand!: ServiceIdentifier; - private static CACHE: ResourceMap = new ResourceMap(); + private static CACHE: ResourceMap = new ResourceMap(); //#region events @@ -216,11 +217,11 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#region openEditor() - openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceDiffInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IResourceSideBySideInput, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditor(editor: IEditorInput | IResourceEditor, optionsOrGroup?: IEditorOptions | ITextEditorOptions | IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, group?: GroupIdentifier): Promise { + openEditor(editor: IEditorInput, options?: IEditorOptions | ITextEditorOptions, group?: OpenInEditorGroup): Promise; + openEditor(editor: IResourceInput | IUntitledResourceInput, group?: OpenInEditorGroup): Promise; + openEditor(editor: IResourceDiffInput, group?: OpenInEditorGroup): Promise; + openEditor(editor: IResourceSideBySideInput, group?: OpenInEditorGroup): Promise; + async openEditor(editor: IEditorInput | IResourceEditor, optionsOrGroup?: IEditorOptions | ITextEditorOptions | OpenInEditorGroup, group?: OpenInEditorGroup): Promise { // Typed Editor Support if (editor instanceof EditorInput) { @@ -240,14 +241,14 @@ export class EditorService extends Disposable implements EditorServiceImpl { return this.doOpenEditor(targetGroup, typedInput, editorOptions); } - return Promise.resolve(undefined); + return undefined; } - protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { - return group.openEditor(editor, options).then(withNullAsUndefined); + protected async doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { + return withNullAsUndefined(await group.openEditor(editor, options)); } - private findTargetGroup(input: IEditorInput, options?: IEditorOptions, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): IEditorGroup { + private findTargetGroup(input: IEditorInput, options?: IEditorOptions, group?: OpenInEditorGroup): IEditorGroup { let targetGroup: IEditorGroup | undefined; // Group: Instance of Group @@ -344,9 +345,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#region openEditors() - openEditors(editors: IEditorInputWithOptions[], group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditors(editors: IResourceEditor[], group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - async openEditors(editors: Array, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise { + openEditors(editors: IEditorInputWithOptions[], group?: OpenInEditorGroup): Promise; + openEditors(editors: IResourceEditor[], group?: OpenInEditorGroup): Promise; + async openEditors(editors: Array, group?: OpenInEditorGroup): Promise { // Convert to typed editors and options const typedEditors: IEditorInputWithOptions[] = []; @@ -391,7 +392,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#region isOpen() - isOpen(editor: IEditorInput | IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier): boolean { + isOpen(editor: IEditorInput | IResourceInput | IUntitledResourceInput): boolean { return !!this.doGetOpened(editor); } @@ -399,11 +400,11 @@ export class EditorService extends Disposable implements EditorServiceImpl { //#region getOpend() - getOpened(editor: IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier): IEditorInput | undefined { + getOpened(editor: IResourceInput | IUntitledResourceInput): IEditorInput | undefined { return this.doGetOpened(editor); } - private doGetOpened(editor: IEditorInput | IResourceInput | IUntitledResourceInput, group?: IEditorGroup | GroupIdentifier): IEditorInput | undefined { + private doGetOpened(editor: IEditorInput | IResourceInput | IUntitledResourceInput): IEditorInput | undefined { if (!(editor instanceof EditorInput)) { const resourceInput = editor as IResourceInput | IUntitledResourceInput; if (!resourceInput.resource) { @@ -411,20 +412,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { } } - let groups: IEditorGroup[] = []; - if (typeof group === 'number') { - const groupView = this.editorGroupService.getGroup(group); - if (groupView) { - groups.push(groupView); - } - } else if (group) { - groups.push(group); - } else { - groups = [...this.editorGroupService.groups]; - } - // For each editor group - for (const group of groups) { + for (const group of this.editorGroupService.groups) { // Typed editor if (editor instanceof EditorInput) { @@ -566,7 +555,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { throw new Error('Unknown input type'); } - private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string | undefined, description: string | undefined, encoding: string | undefined, mode: string | undefined, forceFile: boolean | undefined): ICachedEditorInput { + private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string | undefined, description: string | undefined, encoding: string | undefined, mode: string | undefined, forceFile: boolean | undefined): CachedEditorInput { if (EditorService.CACHE.has(resource)) { const input = EditorService.CACHE.get(resource)!; if (input instanceof ResourceEditorInput) { @@ -594,9 +583,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { return input; } - let input: ICachedEditorInput; - // File + let input: CachedEditorInput; if (forceFile /* fix for https://github.com/Microsoft/vscode/issues/48275 */ || this.fileService.canHandleResource(resource)) { input = this.fileInputFactory.createFileInput(resource, encoding, mode, instantiationService); } diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 1e579f2968b1d..c95a613854514 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -214,7 +214,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic private openSettings2(options?: ISettingsEditorOptions): Promise { const input = this.settingsEditor2Input; - return this.editorGroupService.activeGroup.openEditor(input, options) + return this.editorService.openEditor(input, options) .then(() => this.editorGroupService.activeGroup.activeControl!); } diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index d182e4ca73c43..e25eccdd4ad0e 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -710,6 +710,10 @@ export class TestEditorGroupsService implements IEditorGroupsService { throw new Error('not implemented'); } + restoreGroup(_group: number | IEditorGroup): IEditorGroup { + throw new Error('not implemented'); + } + getSize(_group: number | IEditorGroup): { width: number, height: number } { return { width: 100, height: 100 }; } From b032304497183709c76861b19bf405b17afc992a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 26 Aug 2019 08:21:03 +0200 Subject: [PATCH 612/613] workaround #79786 --- .../contrib/files/browser/views/openEditorsView.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 774d54ef6269c..1aa98445d91be 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -263,15 +263,21 @@ export class OpenEditorsView extends ViewletPanel { let openToSide = false; let isSingleClick = false; let isDoubleClick = false; + let isMiddleClick = false; if (browserEvent instanceof MouseEvent) { isSingleClick = browserEvent.detail === 1; isDoubleClick = browserEvent.detail === 2; + isMiddleClick = browserEvent.button === 1; openToSide = this.list.useAltAsMultipleSelectionModifier ? (browserEvent.ctrlKey || browserEvent.metaKey) : browserEvent.altKey; } const focused = this.list.getFocusedElements(); const element = focused.length ? focused[0] : undefined; if (element instanceof OpenEditor) { + if (isMiddleClick) { + return; // already handled above: closes the editor + } + this.openEditor(element, { preserveFocus: isSingleClick, pinned: isDoubleClick, sideBySide: openToSide }); } else if (element) { this.editorGroupService.activateGroup(element); From 8b96058c1fdccd506d895461ea93b5a009698600 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 26 Aug 2019 08:42:56 +0200 Subject: [PATCH 613/613] fix #79763 (also fixes #79787) --- .../browser/parts/editor/editorActions.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 0464d3131be23..7b4cab33ed860 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -618,12 +618,24 @@ export abstract class BaseCloseAllAction extends Action { async run(): Promise { - // Just close all if there are no or one dirty editor - if (this.textFileService.getDirty().length < 2) { + // Just close all if there are no dirty editors + if (!this.textFileService.isDirty()) { return this.doCloseAll(); } - // Otherwise ask for combined confirmation + // Otherwise ask for combined confirmation and make sure + // to bring each dirty editor to the front so that the user + // can review if the files should be changed or not. + await Promise.all(this.groupsToClose.map(async groupToClose => { + for (const editor of groupToClose.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)) { + if (editor.isDirty()) { + return groupToClose.openEditor(editor); + } + } + + return undefined; + })); + const confirm = await this.textFileService.confirmSave(); if (confirm === ConfirmResult.CANCEL) { return;