From e24fefe0a89205ce519efd8bd4ee53bde4bfe90c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 09:34:30 +0100 Subject: [PATCH 01/88] add URI.from(url)-overload --- src/vs/base/common/uri.ts | 9 ++++++++- src/vs/monaco.d.ts | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index f6a8959a04c..29cd09a479e 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -323,7 +323,14 @@ export class URI implements UriComponents { return new _URI('file', authority, path, _empty, _empty); } - static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): URI { + static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string } | URL): URI { + + if (components instanceof URL) { + const value = <_URI>_URI.parse(components.href); + value._formatted = components.href; + return value; + } + return new _URI( components.scheme, components.authority, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 2fa74f0ac35..ff535f4bfbc 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -160,7 +160,7 @@ declare namespace monaco { path?: string; query?: string; fragment?: string; - }): Uri; + } | URL): Uri; /** * Creates a string representation for this Uri. It's guaranteed that calling * `Uri.parse` with the result of this function creates an Uri which is equal From 9106843dc2f3a6d419c19c191e16a1da80299700 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 10:08:03 +0100 Subject: [PATCH 02/88] IOpener must accept URI and URL --- .../editor/browser/services/openerService.ts | 174 ++++++++++-------- .../browser/services/openerService.test.ts | 26 +-- src/vs/platform/opener/common/opener.ts | 3 +- .../url/electron-browser/urlService.ts | 11 +- 4 files changed, 124 insertions(+), 90 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 48175adfb42..ff7c67ebbbb 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { 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'; @@ -16,46 +16,123 @@ import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/c import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener } from 'vs/platform/opener/common/opener'; import { EditorOpenContext } from 'vs/platform/editor/common/editor'; -export class OpenerService extends Disposable implements IOpenerService { +function hasScheme(target: URI | URL, scheme: string) { + if (URI.isUri(target)) { + return equalsIgnoreCase(target.scheme, scheme); + } else { + return equalsIgnoreCase(target.protocol, scheme + ':'); + } +} + +export class OpenerService implements IOpenerService { _serviceBrand: undefined; private readonly _openers = new LinkedList(); private readonly _validators = new LinkedList(); private readonly _resolvers = new LinkedList(); + private _externalOpener: IExternalOpener; + private _openerAsExternal: IOpener; + private _openerAsCommand: IOpener; + private _openerAsEditor: IOpener; constructor( - @ICodeEditorService private readonly _editorService: ICodeEditorService, - @ICommandService private readonly _commandService: ICommandService, + @ICodeEditorService editorService: ICodeEditorService, + @ICommandService commandService: ICommandService, ) { - super(); - // Default external opener is going through window.open() this._externalOpener = { openExternal: href => { dom.windowOpenNoOpener(href); - return Promise.resolve(true); } }; + + // Default opener: maito, http(s), command, and catch-all-editors + this._openerAsExternal = { + open: async (target: URI | URL, options?: OpenOptions) => { + if (options?.openExternal || hasScheme(target, Schemas.mailto) || hasScheme(target, Schemas.http) || hasScheme(target, Schemas.https)) { + // open externally + await this._doOpenExternal(target, options); + return true; + } + return false; + } + }; + + this._openerAsCommand = { + open: async (target) => { + if (!hasScheme(target, Schemas.command)) { + return false; + } + // run command or bail out if command isn't known + if (!URI.isUri(target)) { + target = URI.from(target); + } + if (!CommandsRegistry.getCommand(target.path)) { + throw new Error(`command '${target.path}' NOT known`); + } + // execute as command + let args: any = []; + try { + args = parse(target.query); + if (!Array.isArray(args)) { + args = [args]; + } + } catch (e) { + // ignore error + } + await commandService.executeCommand(target.path, ...args); + return true; + } + }; + + this._openerAsEditor = { + open: async (target, options: OpenOptions) => { + if (!URI.isUri(target)) { + target = URI.from(target); + } + let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined; + const match = /^L?(\d+)(?:,(\d+))?/.exec(target.fragment); + if (match) { + // support file:///some/file.js#73,84 + // support file:///some/file.js#L73 + selection = { + startLineNumber: parseInt(match[1]), + startColumn: match[2] ? parseInt(match[2]) : 1 + }; + // remove fragment + target = target.with({ fragment: '' }); + } + + if (target.scheme === Schemas.file) { + target = resources.normalizePath(target); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) + } + + await editorService.openCodeEditor( + { resource: target, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } }, + editorService.getFocusedCodeEditor(), + options?.openToSide + ); + + return true; + } + }; } registerOpener(opener: IOpener): IDisposable { const remove = this._openers.push(opener); - return { dispose: remove }; } registerValidator(validator: IValidator): IDisposable { const remove = this._validators.push(validator); - return { dispose: remove }; } registerExternalUriResolver(resolver: IExternalUriResolver): IDisposable { const remove = this._resolvers.push(resolver); - return { dispose: remove }; } @@ -86,7 +163,12 @@ export class OpenerService extends Disposable implements IOpenerService { } // use default openers - return this._doOpen(resource, options); + for (const opener of [this._openerAsExternal, this._openerAsCommand, this._openerAsEditor]) { + if (await opener.open(resource, options)) { + break; + } + } + return true; } async resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise { @@ -100,68 +182,16 @@ export class OpenerService extends Disposable implements IOpenerService { return { resolved: resource, dispose: () => { } }; } - private async _doOpen(resource: URI, options: OpenOptions | undefined): Promise { - const { scheme, path, query, fragment } = resource; - - if (options?.openExternal || equalsIgnoreCase(scheme, Schemas.mailto) || equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) { - // open externally - return this._doOpenExternal(resource, options); + private async _doOpenExternal(resource: URI | URL, options: OpenOptions | undefined): Promise { + if (URI.isUri(resource)) { + const { resolved } = await this.resolveExternalUri(resource, options); + // TODO@Jo neither encodeURI nor toString(true) should be needed + // once we go with URL and not URI + return this._externalOpener.openExternal(encodeURI(resolved.toString(true))); + } else { + //todo@joh what about resolveExternalUri? + return this._externalOpener.openExternal(resource.href); } - - if (equalsIgnoreCase(scheme, Schemas.command)) { - // run command or bail out if command isn't known - if (!CommandsRegistry.getCommand(path)) { - throw new Error(`command '${path}' NOT known`); - } - // execute as command - let args: any = []; - try { - args = parse(query); - if (!Array.isArray(args)) { - args = [args]; - } - } catch (e) { - // ignore error - } - - await this._commandService.executeCommand(path, ...args); - - return true; - } - - // finally open in editor - let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined; - const match = /^L?(\d+)(?:,(\d+))?/.exec(fragment); - if (match) { - // support file:///some/file.js#73,84 - // support file:///some/file.js#L73 - selection = { - startLineNumber: parseInt(match[1]), - startColumn: match[2] ? parseInt(match[2]) : 1 - }; - // remove fragment - resource = resource.with({ fragment: '' }); - } - - if (resource.scheme === Schemas.file) { - resource = resources.normalizePath(resource); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) - } - - await this._editorService.openCodeEditor( - { resource, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } }, - this._editorService.getFocusedCodeEditor(), - options?.openToSide - ); - - return true; - } - - private async _doOpenExternal(resource: URI, options: OpenOptions | undefined): Promise { - const { resolved } = await this.resolveExternalUri(resource, options); - - // TODO@Jo neither encodeURI nor toString(true) should be needed - // once we go with URL and not URI - return this._externalOpener.openExternal(encodeURI(resolved.toString(true))); } dispose() { diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index 6f03b911e37..4081b6fc5f8 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -28,27 +28,27 @@ suite('OpenerService', function () { lastCommand = undefined; }); - test('delegate to editorService, scheme:///fff', function () { + test('delegate to editorService, scheme:///fff', async function () { const openerService = new OpenerService(editorService, NullCommandService); - openerService.open(URI.parse('another:///somepath')); + await openerService.open(URI.parse('another:///somepath')); assert.equal(editorService.lastInput!.options!.selection, undefined); }); - test('delegate to editorService, scheme:///fff#L123', function () { + test('delegate to editorService, scheme:///fff#L123', async function () { const openerService = new OpenerService(editorService, NullCommandService); - openerService.open(URI.parse('file:///somepath#L23')); + await openerService.open(URI.parse('file:///somepath#L23')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); assert.equal(editorService.lastInput!.options!.selection!.startColumn, 1); assert.equal(editorService.lastInput!.options!.selection!.endLineNumber, undefined); assert.equal(editorService.lastInput!.options!.selection!.endColumn, undefined); assert.equal(editorService.lastInput!.resource.fragment, ''); - openerService.open(URI.parse('another:///somepath#L23')); + await openerService.open(URI.parse('another:///somepath#L23')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); assert.equal(editorService.lastInput!.options!.selection!.startColumn, 1); - openerService.open(URI.parse('another:///somepath#L23,45')); + await openerService.open(URI.parse('another:///somepath#L23,45')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); assert.equal(editorService.lastInput!.options!.selection!.startColumn, 45); assert.equal(editorService.lastInput!.options!.selection!.endLineNumber, undefined); @@ -56,17 +56,17 @@ suite('OpenerService', function () { assert.equal(editorService.lastInput!.resource.fragment, ''); }); - test('delegate to editorService, scheme:///fff#123,123', function () { + test('delegate to editorService, scheme:///fff#123,123', async function () { const openerService = new OpenerService(editorService, NullCommandService); - openerService.open(URI.parse('file:///somepath#23')); + await openerService.open(URI.parse('file:///somepath#23')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); assert.equal(editorService.lastInput!.options!.selection!.startColumn, 1); assert.equal(editorService.lastInput!.options!.selection!.endLineNumber, undefined); assert.equal(editorService.lastInput!.options!.selection!.endColumn, undefined); assert.equal(editorService.lastInput!.resource.fragment, ''); - openerService.open(URI.parse('file:///somepath#23,45')); + await openerService.open(URI.parse('file:///somepath#23,45')); assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23); assert.equal(editorService.lastInput!.options!.selection!.startColumn, 45); assert.equal(editorService.lastInput!.options!.selection!.endLineNumber, undefined); @@ -74,22 +74,22 @@ suite('OpenerService', function () { assert.equal(editorService.lastInput!.resource.fragment, ''); }); - test('delegate to commandsService, command:someid', function () { + test('delegate to commandsService, command:someid', async function () { const openerService = new OpenerService(editorService, commandService); const id = `aCommand${Math.random()}`; CommandsRegistry.registerCommand(id, function () { }); - openerService.open(URI.parse('command:' + id)); + await openerService.open(URI.parse('command:' + id)); assert.equal(lastCommand!.id, id); assert.equal(lastCommand!.args.length, 0); - openerService.open(URI.parse('command:' + id).with({ query: '123' })); + await openerService.open(URI.parse('command:' + id).with({ query: '123' })); assert.equal(lastCommand!.id, id); assert.equal(lastCommand!.args.length, 1); assert.equal(lastCommand!.args[0], '123'); - openerService.open(URI.parse('command:' + id).with({ query: JSON.stringify([12, true]) })); + await openerService.open(URI.parse('command:' + id).with({ query: JSON.stringify([12, true]) })); assert.equal(lastCommand!.id, id); assert.equal(lastCommand!.args.length, 2); assert.equal(lastCommand!.args[0], 12); diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 3b60521677a..8b9ce48f621 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -35,8 +35,7 @@ export interface IResolvedExternalUri extends IDisposable { } export interface IOpener { - open(resource: URI, options?: OpenInternalOptions): Promise; - open(resource: URI, options?: OpenExternalOptions): Promise; + open(resource: URI | URL, options?: OpenInternalOptions | OpenExternalOptions): Promise; } export interface IExternalOpener { diff --git a/src/vs/workbench/services/url/electron-browser/urlService.ts b/src/vs/workbench/services/url/electron-browser/urlService.ts index 73fa15e1f8e..c46c1948c45 100644 --- a/src/vs/workbench/services/url/electron-browser/urlService.ts +++ b/src/vs/workbench/services/url/electron-browser/urlService.ts @@ -8,7 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { URLHandlerChannel } from 'vs/platform/url/common/urlIpc'; import { URLService } from 'vs/platform/url/node/urlService'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IOpenerService, IOpener } from 'vs/platform/opener/common/opener'; import product from 'vs/platform/product/common/product'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService'; @@ -20,7 +20,7 @@ export interface IRelayOpenURLOptions extends IOpenURLOptions { openExternal?: boolean; } -export class RelayURLService extends URLService implements IURLHandler { +export class RelayURLService extends URLService implements IURLHandler, IOpener { private urlService: IURLService; @@ -51,7 +51,12 @@ export class RelayURLService extends URLService implements IURLHandler { return uri.with({ query }); } - async open(resource: URI, options?: IRelayOpenURLOptions): Promise { + async open(resource: URI | URL, options?: IRelayOpenURLOptions): Promise { + + if (resource instanceof URL) { + resource = URI.from(resource); + } + if (resource.scheme !== product.urlProtocol) { return false; } From 3816ecfe24d7835c319d89f2b9aeb9e5836a9f2c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 10:29:58 +0100 Subject: [PATCH 03/88] IOpenerService#open accepts URI and URL --- src/vs/editor/browser/services/openerService.ts | 9 ++++++--- src/vs/platform/opener/common/opener.ts | 3 +-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index ff7c67ebbbb..dab6794e8ca 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -140,13 +140,16 @@ export class OpenerService implements IOpenerService { this._externalOpener = externalOpener; } - async open(resource: URI, options?: OpenOptions): Promise { + async open(target: URI | URL, options?: OpenOptions): Promise { + + const resource = URI.isUri(target) ? target : URI.from(target); // no scheme ?!? if (!resource.scheme) { return Promise.resolve(false); } + //todo@joh adopt validator // check with contributed validators for (const validator of this._validators.toArray()) { if (!(await validator.shouldOpen(resource))) { @@ -156,7 +159,7 @@ export class OpenerService implements IOpenerService { // check with contributed openers for (const opener of this._openers.toArray()) { - const handled = await opener.open(resource, options); + const handled = await opener.open(target, options); if (handled) { return true; } @@ -164,7 +167,7 @@ export class OpenerService implements IOpenerService { // use default openers for (const opener of [this._openerAsExternal, this._openerAsCommand, this._openerAsEditor]) { - if (await opener.open(resource, options)) { + if (await opener.open(target, options)) { break; } } diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 8b9ce48f621..a0caf1463e2 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -82,8 +82,7 @@ export interface IOpenerService { * @param resource A resource * @return A promise that resolves when the opening is done. */ - open(resource: URI, options?: OpenInternalOptions): Promise; - open(resource: URI, options?: OpenExternalOptions): Promise; + open(resource: URI | URL, options?: OpenInternalOptions | OpenExternalOptions): Promise; /** * Resolve a resource to its external form. From d891b0d3493a3180cfdf2ea53f46b41445151bb4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 10:52:36 +0100 Subject: [PATCH 04/88] editor link detector uses URL --- src/vs/editor/contrib/links/getLinks.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/links/getLinks.ts b/src/vs/editor/contrib/links/getLinks.ts index df34eed5600..e7a66f48234 100644 --- a/src/vs/editor/contrib/links/getLinks.ts +++ b/src/vs/editor/contrib/links/getLinks.ts @@ -14,6 +14,18 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { isDisposable, Disposable } from 'vs/base/common/lifecycle'; import { coalesce } from 'vs/base/common/arrays'; +// in IE11 there is URL but a constructor +// https://developer.mozilla.org/en-US/docs/Web/API/URL/URL#Browser_compatibility +const canUseUrl = (function () { + try { + // tslint:disable-next-line: no-unused-expression + new URL('some://thing'); + return true; + } catch { + return false; + } +})(); + export class Link implements ILink { private _link: ILink; @@ -44,13 +56,15 @@ export class Link implements ILink { return this._link.tooltip; } - resolve(token: CancellationToken): Promise { + async resolve(token: CancellationToken): Promise { if (this._link.url) { try { - if (typeof this._link.url === 'string') { - return Promise.resolve(URI.parse(this._link.url)); + if (URI.isUri(this._link.url)) { + return this._link.url; + } else if (!canUseUrl) { + return URI.parse(this._link.url); } else { - return Promise.resolve(this._link.url); + return new URL(this._link.url); } } catch (e) { return Promise.reject(new Error('invalid')); From 1603d3e6e52c5e6d93d274e3b322097df1afb1c1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 11:03:05 +0100 Subject: [PATCH 05/88] validator must support URI and URL, also extract utils for re-use --- src/vs/base/common/resources.ts | 22 ++++++++++++++++ .../editor/browser/services/openerService.ts | 26 ++++--------------- src/vs/editor/contrib/links/getLinks.ts | 15 ++--------- src/vs/platform/opener/common/opener.ts | 2 +- .../url/common/trustedDomainsValidator.ts | 13 ++++++---- 5 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index bea40bbdc62..6a9dcd13095 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -361,3 +361,25 @@ export function toLocalResource(resource: URI, authority: string | undefined): U return resource.with({ scheme: Schemas.file }); } + +export function matchesScheme(target: URI | URL, scheme: string) { + if (URI.isUri(target)) { + return equalsIgnoreCase(target.scheme, scheme); + } else { + return equalsIgnoreCase(target.protocol, scheme + ':'); + } +} + +/** + * `true` when urls can be constructed via `new URL("string")`, should only be `false` in IE: + * https://developer.mozilla.org/en-US/docs/Web/API/URL/URL#Browser_compatibility + */ +export const hasURLCtor = (function () { + try { + // tslint:disable-next-line: no-unused-expression + new URL('some://thing'); + return true; + } catch { + return false; + } +})(); diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index dab6794e8ca..be0bcb1dcf8 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -8,21 +8,13 @@ import { 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 { matchesScheme, normalizePath } from 'vs/base/common/resources'; 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 { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener } from 'vs/platform/opener/common/opener'; import { EditorOpenContext } from 'vs/platform/editor/common/editor'; -function hasScheme(target: URI | URL, scheme: string) { - if (URI.isUri(target)) { - return equalsIgnoreCase(target.scheme, scheme); - } else { - return equalsIgnoreCase(target.protocol, scheme + ':'); - } -} export class OpenerService implements IOpenerService { @@ -52,7 +44,7 @@ export class OpenerService implements IOpenerService { // Default opener: maito, http(s), command, and catch-all-editors this._openerAsExternal = { open: async (target: URI | URL, options?: OpenOptions) => { - if (options?.openExternal || hasScheme(target, Schemas.mailto) || hasScheme(target, Schemas.http) || hasScheme(target, Schemas.https)) { + if (options?.openExternal || matchesScheme(target, Schemas.mailto) || matchesScheme(target, Schemas.http) || matchesScheme(target, Schemas.https)) { // open externally await this._doOpenExternal(target, options); return true; @@ -63,7 +55,7 @@ export class OpenerService implements IOpenerService { this._openerAsCommand = { open: async (target) => { - if (!hasScheme(target, Schemas.command)) { + if (!matchesScheme(target, Schemas.command)) { return false; } // run command or bail out if command isn't known @@ -107,7 +99,7 @@ export class OpenerService implements IOpenerService { } if (target.scheme === Schemas.file) { - target = resources.normalizePath(target); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) + target = normalizePath(target); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) } await editorService.openCodeEditor( @@ -142,17 +134,9 @@ export class OpenerService implements IOpenerService { async open(target: URI | URL, options?: OpenOptions): Promise { - const resource = URI.isUri(target) ? target : URI.from(target); - - // no scheme ?!? - if (!resource.scheme) { - return Promise.resolve(false); - } - - //todo@joh adopt validator // check with contributed validators for (const validator of this._validators.toArray()) { - if (!(await validator.shouldOpen(resource))) { + if (!(await validator.shouldOpen(target))) { return false; } } diff --git a/src/vs/editor/contrib/links/getLinks.ts b/src/vs/editor/contrib/links/getLinks.ts index e7a66f48234..081d8b4adde 100644 --- a/src/vs/editor/contrib/links/getLinks.ts +++ b/src/vs/editor/contrib/links/getLinks.ts @@ -13,18 +13,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { isDisposable, Disposable } from 'vs/base/common/lifecycle'; import { coalesce } from 'vs/base/common/arrays'; - -// in IE11 there is URL but a constructor -// https://developer.mozilla.org/en-US/docs/Web/API/URL/URL#Browser_compatibility -const canUseUrl = (function () { - try { - // tslint:disable-next-line: no-unused-expression - new URL('some://thing'); - return true; - } catch { - return false; - } -})(); +import { hasURLCtor } from 'vs/base/common/resources'; export class Link implements ILink { @@ -61,7 +50,7 @@ export class Link implements ILink { try { if (URI.isUri(this._link.url)) { return this._link.url; - } else if (!canUseUrl) { + } else if (!hasURLCtor) { return URI.parse(this._link.url); } else { return new URL(this._link.url); diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index a0caf1463e2..5cdaebc93ce 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -43,7 +43,7 @@ export interface IExternalOpener { } export interface IValidator { - shouldOpen(resource: URI): Promise; + shouldOpen(resource: URI | URL): Promise; } export interface IExternalUriResolver { diff --git a/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts b/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts index f930d68bccf..aabcd4405b2 100644 --- a/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts +++ b/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts @@ -5,7 +5,6 @@ import { Schemas } from 'vs/base/common/network'; import Severity from 'vs/base/common/severity'; -import { equalsIgnoreCase } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; @@ -20,6 +19,7 @@ import { } from 'vs/workbench/contrib/url/common/trustedDomains'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { matchesScheme } from 'vs/base/common/resources'; export class OpenerValidatorContributions implements IWorkbenchContribution { constructor( @@ -34,13 +34,16 @@ export class OpenerValidatorContributions implements IWorkbenchContribution { this._openerService.registerValidator({ shouldOpen: r => this.validateLink(r) }); } - async validateLink(resource: URI): Promise { - const { scheme, authority, path, query, fragment } = resource; - - if (!equalsIgnoreCase(scheme, Schemas.http) && !equalsIgnoreCase(scheme, Schemas.https)) { + async validateLink(resource: URI | URL): Promise { + if (!matchesScheme(resource, Schemas.http) && !matchesScheme(resource, Schemas.https)) { return true; } + if (!URI.isUri(resource)) { + resource = URI.from(resource); + } + const { scheme, authority, path, query, fragment } = resource; + const domainToOpen = `${scheme}://${authority}`; const { defaultTrustedDomains, trustedDomains } = readTrustedDomains(this._storageService, this._productService); const allTrustedDomains = [...defaultTrustedDomains, ...trustedDomains]; From 65159d001a60c01985666dbdde7b14ba24262399 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 11:55:52 +0100 Subject: [PATCH 06/88] terminal link handler using URL, #74604 --- .../workbench/contrib/terminal/browser/terminalLinkHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts index 544cabcde0d..2699cf8ab8a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts @@ -266,8 +266,8 @@ export class TerminalLinkHandler { } private _handleHypertextLink(url: string): void { - const uri = URI.parse(url); - this._openerService.open(uri, { allowTunneling: !!(this._processManager && this._processManager.remoteAuthority) }); + const urlObj = new URL(url); + this._openerService.open(urlObj, { allowTunneling: !!(this._processManager && this._processManager.remoteAuthority) }); } private _isLinkActivationModifierDown(event: MouseEvent): boolean { From 481485b354b6a32eac31101daaf8cd1cc81787da Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 15:05:10 +0100 Subject: [PATCH 07/88] use string instead of URL, help with IE11 and with unwanted URL magic --- src/vs/base/common/resources.ts | 22 ------------------- .../editor/browser/services/openerService.ts | 20 ++++++++--------- src/vs/editor/contrib/links/getLinks.ts | 15 ++----------- .../browser/services/openerService.test.ts | 15 +++++++++++++ src/vs/platform/opener/common/opener.ts | 15 ++++++++++--- .../terminal/browser/terminalLinkHandler.ts | 3 +-- .../url/common/trustedDomainsValidator.ts | 10 ++++----- .../url/electron-browser/urlService.ts | 13 +++++------ 8 files changed, 51 insertions(+), 62 deletions(-) diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 6a9dcd13095..bea40bbdc62 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -361,25 +361,3 @@ export function toLocalResource(resource: URI, authority: string | undefined): U return resource.with({ scheme: Schemas.file }); } - -export function matchesScheme(target: URI | URL, scheme: string) { - if (URI.isUri(target)) { - return equalsIgnoreCase(target.scheme, scheme); - } else { - return equalsIgnoreCase(target.protocol, scheme + ':'); - } -} - -/** - * `true` when urls can be constructed via `new URL("string")`, should only be `false` in IE: - * https://developer.mozilla.org/en-US/docs/Web/API/URL/URL#Browser_compatibility - */ -export const hasURLCtor = (function () { - try { - // tslint:disable-next-line: no-unused-expression - new URL('some://thing'); - return true; - } catch { - return false; - } -})(); diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index be0bcb1dcf8..7e1d50546aa 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -8,11 +8,11 @@ import { 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 { matchesScheme, normalizePath } from 'vs/base/common/resources'; +import { normalizePath } from 'vs/base/common/resources'; 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 { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener } from 'vs/platform/opener/common/opener'; +import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener, matchesScheme } from 'vs/platform/opener/common/opener'; import { EditorOpenContext } from 'vs/platform/editor/common/editor'; @@ -43,7 +43,7 @@ export class OpenerService implements IOpenerService { // Default opener: maito, http(s), command, and catch-all-editors this._openerAsExternal = { - open: async (target: URI | URL, options?: OpenOptions) => { + open: async (target: URI | string, options?: OpenOptions) => { if (options?.openExternal || matchesScheme(target, Schemas.mailto) || matchesScheme(target, Schemas.http) || matchesScheme(target, Schemas.https)) { // open externally await this._doOpenExternal(target, options); @@ -59,8 +59,8 @@ export class OpenerService implements IOpenerService { return false; } // run command or bail out if command isn't known - if (!URI.isUri(target)) { - target = URI.from(target); + if (typeof target === 'string') { + target = URI.parse(target); } if (!CommandsRegistry.getCommand(target.path)) { throw new Error(`command '${target.path}' NOT known`); @@ -82,8 +82,8 @@ export class OpenerService implements IOpenerService { this._openerAsEditor = { open: async (target, options: OpenOptions) => { - if (!URI.isUri(target)) { - target = URI.from(target); + if (typeof target === 'string') { + target = URI.parse(target); } let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined; const match = /^L?(\d+)(?:,(\d+))?/.exec(target.fragment); @@ -132,7 +132,7 @@ export class OpenerService implements IOpenerService { this._externalOpener = externalOpener; } - async open(target: URI | URL, options?: OpenOptions): Promise { + async open(target: URI | string, options?: OpenOptions): Promise { // check with contributed validators for (const validator of this._validators.toArray()) { @@ -169,7 +169,7 @@ export class OpenerService implements IOpenerService { return { resolved: resource, dispose: () => { } }; } - private async _doOpenExternal(resource: URI | URL, options: OpenOptions | undefined): Promise { + private async _doOpenExternal(resource: URI | string, options: OpenOptions | undefined): Promise { if (URI.isUri(resource)) { const { resolved } = await this.resolveExternalUri(resource, options); // TODO@Jo neither encodeURI nor toString(true) should be needed @@ -177,7 +177,7 @@ export class OpenerService implements IOpenerService { return this._externalOpener.openExternal(encodeURI(resolved.toString(true))); } else { //todo@joh what about resolveExternalUri? - return this._externalOpener.openExternal(resource.href); + return this._externalOpener.openExternal(resource); } } diff --git a/src/vs/editor/contrib/links/getLinks.ts b/src/vs/editor/contrib/links/getLinks.ts index 081d8b4adde..a85e7f8c942 100644 --- a/src/vs/editor/contrib/links/getLinks.ts +++ b/src/vs/editor/contrib/links/getLinks.ts @@ -13,7 +13,6 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { isDisposable, Disposable } from 'vs/base/common/lifecycle'; import { coalesce } from 'vs/base/common/arrays'; -import { hasURLCtor } from 'vs/base/common/resources'; export class Link implements ILink { @@ -45,19 +44,9 @@ export class Link implements ILink { return this._link.tooltip; } - async resolve(token: CancellationToken): Promise { + async resolve(token: CancellationToken): Promise { if (this._link.url) { - try { - if (URI.isUri(this._link.url)) { - return this._link.url; - } else if (!hasURLCtor) { - return URI.parse(this._link.url); - } else { - return new URL(this._link.url); - } - } catch (e) { - return Promise.reject(new Error('invalid')); - } + return this._link.url; } if (typeof this._provider.resolveLink === 'function') { diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index 4081b6fc5f8..697dc99402d 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -8,6 +8,7 @@ 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 { matchesScheme } from 'vs/platform/opener/common/opener'; suite('OpenerService', function () { const editorService = new TestCodeEditorService(); @@ -199,4 +200,18 @@ suite('OpenerService', function () { assert.equal(v1, 2); assert.equal(v2, 0); }); + + test('matchesScheme', function () { + assert.ok(matchesScheme('https://microsoft.com', 'https')); + assert.ok(matchesScheme('http://microsoft.com', 'http')); + assert.ok(matchesScheme('hTTPs://microsoft.com', 'https')); + assert.ok(matchesScheme('httP://microsoft.com', 'http')); + assert.ok(matchesScheme(URI.parse('https://microsoft.com'), 'https')); + assert.ok(matchesScheme(URI.parse('http://microsoft.com'), 'http')); + assert.ok(matchesScheme(URI.parse('hTTPs://microsoft.com'), 'https')); + assert.ok(matchesScheme(URI.parse('httP://microsoft.com'), 'http')); + assert.ok(!matchesScheme(URI.parse('https://microsoft.com'), 'http')); + assert.ok(!matchesScheme(URI.parse('htt://microsoft.com'), 'http')); + assert.ok(!matchesScheme(URI.parse('z://microsoft.com'), 'http')); + }); }); diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 5cdaebc93ce..8f7921d1581 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -6,6 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { equalsIgnoreCase, startsWithIgnoreCase } from 'vs/base/common/strings'; export const IOpenerService = createDecorator('openerService'); @@ -35,7 +36,7 @@ export interface IResolvedExternalUri extends IDisposable { } export interface IOpener { - open(resource: URI | URL, options?: OpenInternalOptions | OpenExternalOptions): Promise; + open(resource: URI | string, options?: OpenInternalOptions | OpenExternalOptions): Promise; } export interface IExternalOpener { @@ -43,7 +44,7 @@ export interface IExternalOpener { } export interface IValidator { - shouldOpen(resource: URI | URL): Promise; + shouldOpen(resource: URI | string): Promise; } export interface IExternalUriResolver { @@ -82,7 +83,7 @@ export interface IOpenerService { * @param resource A resource * @return A promise that resolves when the opening is done. */ - open(resource: URI | URL, options?: OpenInternalOptions | OpenExternalOptions): Promise; + open(resource: URI | string, options?: OpenInternalOptions | OpenExternalOptions): Promise; /** * Resolve a resource to its external form. @@ -99,3 +100,11 @@ export const NullOpenerService: IOpenerService = Object.freeze({ async open() { return false; }, async resolveExternalUri(uri: URI) { return { resolved: uri, dispose() { } }; }, }); + +export function matchesScheme(target: URI | string, scheme: string) { + if (URI.isUri(target)) { + return equalsIgnoreCase(target.scheme, scheme); + } else { + return startsWithIgnoreCase(target, scheme + ':'); + } +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts index 2699cf8ab8a..6cb08ae0b26 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts @@ -266,8 +266,7 @@ export class TerminalLinkHandler { } private _handleHypertextLink(url: string): void { - const urlObj = new URL(url); - this._openerService.open(urlObj, { allowTunneling: !!(this._processManager && this._processManager.remoteAuthority) }); + this._openerService.open(url, { allowTunneling: !!(this._processManager && this._processManager.remoteAuthority) }); } private _isLinkActivationModifierDown(event: MouseEvent): boolean { diff --git a/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts b/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts index aabcd4405b2..d8b03dc04d0 100644 --- a/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts +++ b/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts @@ -8,7 +8,7 @@ import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -19,7 +19,7 @@ import { } from 'vs/workbench/contrib/url/common/trustedDomains'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { matchesScheme } from 'vs/base/common/resources'; + export class OpenerValidatorContributions implements IWorkbenchContribution { constructor( @@ -34,13 +34,13 @@ export class OpenerValidatorContributions implements IWorkbenchContribution { this._openerService.registerValidator({ shouldOpen: r => this.validateLink(r) }); } - async validateLink(resource: URI | URL): Promise { + async validateLink(resource: URI | string): Promise { if (!matchesScheme(resource, Schemas.http) && !matchesScheme(resource, Schemas.https)) { return true; } - if (!URI.isUri(resource)) { - resource = URI.from(resource); + if (typeof resource === 'string') { + resource = URI.parse(resource); } const { scheme, authority, path, query, fragment } = resource; diff --git a/src/vs/workbench/services/url/electron-browser/urlService.ts b/src/vs/workbench/services/url/electron-browser/urlService.ts index c46c1948c45..c1485a6ec59 100644 --- a/src/vs/workbench/services/url/electron-browser/urlService.ts +++ b/src/vs/workbench/services/url/electron-browser/urlService.ts @@ -8,7 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { URLHandlerChannel } from 'vs/platform/url/common/urlIpc'; import { URLService } from 'vs/platform/url/node/urlService'; -import { IOpenerService, IOpener } from 'vs/platform/opener/common/opener'; +import { IOpenerService, IOpener, matchesScheme } from 'vs/platform/opener/common/opener'; import product from 'vs/platform/product/common/product'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService'; @@ -51,16 +51,15 @@ export class RelayURLService extends URLService implements IURLHandler, IOpener return uri.with({ query }); } - async open(resource: URI | URL, options?: IRelayOpenURLOptions): Promise { + async open(resource: URI | string, options?: IRelayOpenURLOptions): Promise { - if (resource instanceof URL) { - resource = URI.from(resource); - } - - if (resource.scheme !== product.urlProtocol) { + if (!matchesScheme(resource, product.urlProtocol)) { return false; } + if (typeof resource === 'string') { + resource = URI.parse(resource); + } return await this.urlService.open(resource, options); } From 953cd2e6a21c9750456023928ba4ccea224f549a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 15:47:32 +0100 Subject: [PATCH 08/88] use string in markdown rendering - in most cases --- src/vs/base/browser/markdownRenderer.ts | 10 +++++----- src/vs/editor/contrib/hover/modesContentHover.ts | 2 +- src/vs/editor/contrib/hover/modesGlyphHover.ts | 2 +- src/vs/editor/contrib/markdown/markdownRenderer.ts | 13 ++----------- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 57144d67ea6..971addf8f00 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -50,19 +50,19 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende const _href = function (href: string, isDomUri: boolean): string { const data = markdown.uris && markdown.uris[href]; if (!data) { - return href; + return href; // no uri exists } let uri = URI.revive(data); + if (URI.parse(href).toString() === uri.toString()) { + return href; // no tranformation performed + } if (isDomUri) { uri = DOM.asDomUri(uri); } if (uri.query) { uri = uri.with({ query: _uriMassage(uri.query) }); } - if (data) { - href = uri.toString(true); - } - return href; + return uri.toString(); }; // signal to code-block render that the diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index f37254d776f..ca710263b24 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -207,7 +207,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { private readonly _themeService: IThemeService, private readonly _keybindingService: IKeybindingService, private readonly _modeService: IModeService, - private readonly _openerService: IOpenerService | null = NullOpenerService, + private readonly _openerService: IOpenerService = NullOpenerService, ) { super(ModesContentHoverWidget.ID, editor); diff --git a/src/vs/editor/contrib/hover/modesGlyphHover.ts b/src/vs/editor/contrib/hover/modesGlyphHover.ts index 85dece77b64..32c6cf14656 100644 --- a/src/vs/editor/contrib/hover/modesGlyphHover.ts +++ b/src/vs/editor/contrib/hover/modesGlyphHover.ts @@ -97,7 +97,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { constructor( editor: ICodeEditor, modeService: IModeService, - openerService: IOpenerService | null = NullOpenerService, + openerService: IOpenerService = NullOpenerService, ) { super(ModesGlyphHoverWidget.ID, editor); diff --git a/src/vs/editor/contrib/markdown/markdownRenderer.ts b/src/vs/editor/contrib/markdown/markdownRenderer.ts index eb17228cfa8..b4bfe8ddd9e 100644 --- a/src/vs/editor/contrib/markdown/markdownRenderer.ts +++ b/src/vs/editor/contrib/markdown/markdownRenderer.ts @@ -7,7 +7,6 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; 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'; import { onUnexpectedError } from 'vs/base/common/errors'; import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -29,7 +28,7 @@ export class MarkdownRenderer extends Disposable { constructor( private readonly _editor: ICodeEditor, @IModeService private readonly _modeService: IModeService, - @optional(IOpenerService) private readonly _openerService: IOpenerService | null = NullOpenerService, + @optional(IOpenerService) private readonly _openerService: IOpenerService = NullOpenerService, ) { super(); } @@ -64,15 +63,7 @@ export class MarkdownRenderer extends Disposable { codeBlockRenderCallback: () => this._onDidRenderCodeBlock.fire(), actionHandler: { callback: (content) => { - let uri: URI | undefined; - try { - uri = URI.parse(content); - } catch { - // ignore - } - if (uri && this._openerService) { - this._openerService.open(uri, { fromUserGesture: true }).catch(onUnexpectedError); - } + this._openerService.open(content, { fromUserGesture: true }).catch(onUnexpectedError); }, disposeables } From 705973999095bc2c67e9d03cf0341409135915fc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 15:52:12 +0100 Subject: [PATCH 09/88] simpler markdown link handling --- .../contrib/comments/browser/commentsTreeViewer.ts | 8 +------- .../contrib/preferences/browser/settingsTree.ts | 11 +---------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 299a7271b2b..c499a52051e 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -8,7 +8,6 @@ import * as nls from 'vs/nls'; 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'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { CommentNode, CommentsModel, ResourceWithCommentThreads } from 'vs/workbench/contrib/comments/common/commentModel'; @@ -127,12 +126,7 @@ export class CommentNodeRenderer implements IListRenderer inline: true, actionHandler: { callback: (content) => { - try { - const uri = URI.parse(content); - this.openerService.open(uri).catch(onUnexpectedError); - } catch (err) { - // ignore - } + this.openerService.open(content).catch(onUnexpectedError); }, disposeables: disposables } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 329d660bca6..d026d0da526 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -28,7 +28,6 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ISpliceable } from 'vs/base/common/sequence'; import { escapeRegExpCharacters, startsWith } from 'vs/base/common/strings'; -import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -485,15 +484,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre }; this._onDidClickSettingLink.fire(e); } else { - let uri: URI | undefined; - try { - uri = URI.parse(content); - } catch (err) { - // ignore - } - if (uri) { - this._openerService.open(uri).catch(onUnexpectedError); - } + this._openerService.open(content).catch(onUnexpectedError); } }, disposeables From 67ac71161cc2295441b70d3d772af77e9b0bdfaa Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 16:42:35 +0100 Subject: [PATCH 10/88] openExternal uses resolver for URI and string --- src/vs/editor/browser/services/openerService.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 7e1d50546aa..b92275c5cf0 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -170,14 +170,17 @@ export class OpenerService implements IOpenerService { } private async _doOpenExternal(resource: URI | string, options: OpenOptions | undefined): Promise { - if (URI.isUri(resource)) { - const { resolved } = await this.resolveExternalUri(resource, options); - // TODO@Jo neither encodeURI nor toString(true) should be needed - // once we go with URL and not URI - return this._externalOpener.openExternal(encodeURI(resolved.toString(true))); - } else { - //todo@joh what about resolveExternalUri? + + //todo@joh IExternalUriResolver should support `uri: URI | string` + const uri = typeof resource === 'string' ? URI.parse(resource) : resource; + const { resolved } = await this.resolveExternalUri(uri, options); + + if (typeof resource === 'string' && uri.toString() === resolved.toString()) { + // open the url-string AS IS return this._externalOpener.openExternal(resource); + } else { + // open URI using the toString(noEncode)+encodeURI-trick + return this._externalOpener.openExternal(encodeURI(resolved.toString(true))); } } From baea605e2df6ba6dddcd6160b632bfbe2e9e2d33 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 17:25:27 +0100 Subject: [PATCH 11/88] peek - fix rendering glitches with meta title --- src/vs/editor/contrib/peekView/media/peekViewWidget.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/peekView/media/peekViewWidget.css b/src/vs/editor/contrib/peekView/media/peekViewWidget.css index 889d1148298..d0e9bc3c552 100644 --- a/src/vs/editor/contrib/peekView/media/peekViewWidget.css +++ b/src/vs/editor/contrib/peekView/media/peekViewWidget.css @@ -21,7 +21,11 @@ margin-left: 0.5em; } -.monaco-editor .peekview-widget .head .peekview-title .meta::before { +.monaco-editor .peekview-widget .head .peekview-title .meta { + white-space: nowrap; +} + +.monaco-editor .peekview-widget .head .peekview-title .meta:not(:empty)::before { content: '-'; padding: 0 0.3em; } From 71107e37e8bb494f9223ed6490b24cf2fba6bff8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 14 Nov 2019 17:25:07 +0100 Subject: [PATCH 12/88] title - seems to be broken when put on separate layer --- .../browser/parts/titlebar/media/titlebarpart.css | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index f67701b1081..265d6518c40 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -19,21 +19,6 @@ display: flex; } -.monaco-workbench.windows .part.titlebar, -.monaco-workbench.linux .part.titlebar, -.monaco-workbench.web .part.titlebar { - /* - * Explicitly put the part onto its own layer to help Chrome to - * render the content with LCD-anti-aliasing. By partioning the - * workbench into multiple layers, we can ensure that a bad - * behaving part is not making another part fallback to greyscale - * rendering. - * - * macOS: does not render LCD-anti-aliased. - */ - transform: translate3d(0px, 0px, 0px); -} - .monaco-workbench .part.titlebar > .titlebar-drag-region { top: 0; left: 0; From 042c660f7cd9b86ef897299b10cdeff9bf7c6e6d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 14 Nov 2019 17:42:55 +0100 Subject: [PATCH 13/88] :up: sudo-prompt@9.1.1 --- package.json | 2 +- .../textfile/electron-browser/nativeTextFileService.ts | 3 +-- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 66caa48db65..ec7cd7ba956 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "onigasm-umd": "^2.2.4", "semver-umd": "^5.5.3", "spdlog": "^0.11.1", - "sudo-prompt": "9.1.0", + "sudo-prompt": "9.1.1", "v8-inspect-profiler": "^0.0.20", "vscode-minimist": "^1.2.2", "vscode-nsfw": "1.2.8", diff --git a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts index 7caae05d5ec..e105588731a 100644 --- a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts @@ -286,8 +286,7 @@ export class NativeTextFileService extends AbstractTextFileService { private async sudoPromptCopy(source: string, target: string, options?: IWriteTextFileOptions): Promise { // load sudo-prompt module lazy - // @ts-ignore TODO@ben wait for update of sudo-prompt - const sudoPrompt: { exec(cmd: string, options: { name?: string, icns?: string }, callback: (error: string, stdout: string, stderr: string) => void): void } = await import('sudo-prompt'); + const sudoPrompt = await import('sudo-prompt'); return new Promise((resolve, reject) => { const promptOptions = { diff --git a/yarn.lock b/yarn.lock index 78889420ad5..e9078a4f13f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8150,10 +8150,10 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -sudo-prompt@9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.1.0.tgz#9618823e748ce19e2d9e481feaf3ada7d52df52f" - integrity sha512-bJigY3ELFd2ZA7gfyQ4wMZIp1EICPFQcMe3RgSz5OQTzrPPaeryhgaxbInO/G62vpiqJs37qlGdb9TaqHeF2yA== +sudo-prompt@9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.1.1.tgz#73853d729770392caec029e2470db9c221754db0" + integrity sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA== supports-color@1.2.0: version "1.2.0" From 9c9b8a9043f4f8eabf9143bbf029743493dd48d3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 17:52:11 +0100 Subject: [PATCH 14/88] drop all view zones via setModel(null) to make closing peek fast, workaround for #84726 --- src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts index 82f86384de4..9d7b3fa0b19 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts @@ -227,6 +227,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { this.setModel(undefined); this._callOnDispose.dispose(); this._disposeOnNewModel.dispose(); + this._preview.setModel(null); // drop all view-zones, workaround for https://github.com/microsoft/vscode/issues/84726 dispose(this._preview); dispose(this._previewNotAvailableMessage); dispose(this._tree); From 485fdf8beac1c3acf87d3cbe1db147ae875541da Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Nov 2019 17:56:41 +0100 Subject: [PATCH 15/88] :lipstick: when disposing all lenses, #84726 --- src/vs/editor/contrib/codelens/codelensController.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index e61b9acafb1..b50d4508fe3 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -222,8 +222,10 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { } private _disposeAllLenses(decChangeAccessor: IModelDecorationsChangeAccessor | undefined, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor | undefined): void { - let helper = new CodeLensHelper(); - this._lenses.forEach((lens) => lens.dispose(helper, viewZoneChangeAccessor)); + const helper = new CodeLensHelper(); + for (const lens of this._lenses) { + lens.dispose(helper, viewZoneChangeAccessor); + } if (decChangeAccessor) { helper.commit(decChangeAccessor); } From d5bc3ab7ee3f7f2a974bf4e76793b0a0560db623 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 14 Nov 2019 18:19:56 +0100 Subject: [PATCH 16/88] Remove gray attributes in launch.json since they are confusing fixes #80026 --- .../configuration-editing/src/extension.ts | 57 ------------------- 1 file changed, 57 deletions(-) diff --git a/extensions/configuration-editing/src/extension.ts b/extensions/configuration-editing/src/extension.ts index 72e2fcf627f..644e9bb1192 100644 --- a/extensions/configuration-editing/src/extension.ts +++ b/extensions/configuration-editing/src/extension.ts @@ -4,23 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { getLocation, parse, visit } from 'jsonc-parser'; -import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { SettingsDocument } from './settingsDocumentHelper'; const localize = nls.loadMessageBundle(); -const fadedDecoration = vscode.window.createTextEditorDecorationType({ - light: { - color: '#757575' - }, - dark: { - color: '#878787' - } -}); - -let pendingLaunchJsonDecoration: NodeJS.Timer; - export function activate(context: vscode.ExtensionContext): void { //settings.json suggestions context.subscriptions.push(registerSettingsCompletions()); @@ -33,18 +21,6 @@ export function activate(context: vscode.ExtensionContext): void { // task.json variable suggestions context.subscriptions.push(registerVariableCompletions('**/tasks.json')); - - // launch.json decorations - context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => updateLaunchJsonDecorations(editor), null, context.subscriptions)); - context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(event => { - if (vscode.window.activeTextEditor && event.document === vscode.window.activeTextEditor.document) { - if (pendingLaunchJsonDecoration) { - clearTimeout(pendingLaunchJsonDecoration); - } - pendingLaunchJsonDecoration = setTimeout(() => updateLaunchJsonDecorations(vscode.window.activeTextEditor), 1000); - } - }, null, context.subscriptions)); - updateLaunchJsonDecorations(vscode.window.activeTextEditor); } function registerSettingsCompletions(): vscode.Disposable { @@ -153,39 +129,6 @@ function provideInstalledExtensionProposals(extensionsContent: IExtensionsConten return undefined; } -function updateLaunchJsonDecorations(editor: vscode.TextEditor | undefined): void { - if (!editor || path.basename(editor.document.fileName) !== 'launch.json') { - return; - } - - const ranges: vscode.Range[] = []; - let addPropertyAndValue = false; - let depthInArray = 0; - visit(editor.document.getText(), { - onObjectProperty: (property, offset, length) => { - // Decorate attributes which are unlikely to be edited by the user. - // Only decorate "configurations" if it is not inside an array (compounds have a configurations property which should not be decorated). - addPropertyAndValue = property === 'version' || property === 'type' || property === 'request' || property === 'compounds' || (property === 'configurations' && depthInArray === 0); - if (addPropertyAndValue) { - ranges.push(new vscode.Range(editor.document.positionAt(offset), editor.document.positionAt(offset + length))); - } - }, - onLiteralValue: (_value, offset, length) => { - if (addPropertyAndValue) { - ranges.push(new vscode.Range(editor.document.positionAt(offset), editor.document.positionAt(offset + length))); - } - }, - onArrayBegin: (_offset: number, _length: number) => { - depthInArray++; - }, - onArrayEnd: (_offset: number, _length: number) => { - depthInArray--; - } - }); - - editor.setDecorations(fadedDecoration, ranges); -} - vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', language: 'jsonc' }, { provideDocumentSymbols(document: vscode.TextDocument, _token: vscode.CancellationToken): vscode.ProviderResult { const result: vscode.SymbolInformation[] = []; From 0616326888e22a6e928a172f9d73a2cb7ffea56d Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 14 Nov 2019 18:36:41 +0100 Subject: [PATCH 17/88] fixes #83736 --- .../files/browser/views/explorerViewer.ts | 19 +++++++++++-------- .../textfile/browser/textFileService.ts | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 1186a19c258..f88860ab91b 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -456,11 +456,13 @@ export class FileSorter implements ITreeSorter { } } -const fileOverwriteConfirm: IConfirmation = { - message: localize('confirmOverwrite', "A file or folder with the same name already exists in the destination folder. Do you want to replace it?"), - detail: localize('irreversible', "This action is irreversible!"), - primaryButton: localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), - type: 'warning' +const fileOverwriteConfirm = (name: string) => { + return { + message: localize('confirmOverwrite', "A file or folder with the name '{0}' already exists in the destination folder. Do you want to replace it?", name), + detail: localize('irreversible', "This action is irreversible!"), + primaryButton: localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), + type: 'warning' + }; }; export class FileDragAndDrop implements ITreeDragAndDrop { @@ -643,7 +645,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const name = file.name; if (typeof name === 'string' && event.target?.result instanceof ArrayBuffer) { if (target.getChild(name)) { - const { confirmed } = await this.dialogService.confirm(fileOverwriteConfirm); + const { confirmed } = await this.dialogService.confirm(fileOverwriteConfirm(name)); if (!confirmed) { return; } @@ -717,9 +719,10 @@ export class FileDragAndDrop implements ITreeDragAndDrop { }); } - const resourceExists = resources.some(resource => targetNames.has(!hasToIgnoreCase(resource) ? basename(resource) : basename(resource).toLowerCase())); + const filtered = resources.filter(resource => targetNames.has(!hasToIgnoreCase(resource) ? basename(resource) : basename(resource).toLowerCase())); + const resourceExists = filtered.length >= 1; if (resourceExists) { - const confirmationResult = await this.dialogService.confirm(fileOverwriteConfirm); + const confirmationResult = await this.dialogService.confirm(fileOverwriteConfirm(basename(filtered[0]))); if (!confirmationResult.confirmed) { return; } diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index 7961fcdb09d..fe4ddff014d 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -809,7 +809,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex private async confirmOverwrite(resource: URI): Promise { const confirm: IConfirmation = { message: nls.localize('confirmOverwrite', "'{0}' already exists. Do you want to replace it?", basename(resource)), - detail: nls.localize('irreversible', "A file or folder with the same name already exists in the folder {0}. Replacing it will overwrite its current contents.", basename(dirname(resource))), + detail: nls.localize('irreversible', "A file or folder with the name '{0}' already exists in the folder '{1}'. Replacing it will overwrite its current contents.", basename(resource), basename(dirname(resource))), primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), type: 'warning' }; From 74e3cf237753ecb3d60b9eae890cd06061bf2ca3 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 14 Nov 2019 10:28:23 -0800 Subject: [PATCH 18/88] Flip triggeredOnType logic --- src/vs/workbench/contrib/search/browser/patternInputWidget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts index 014d39c99bd..01ae7e23081 100644 --- a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts @@ -152,7 +152,7 @@ export class PatternInputWidget extends Widget { this._register(this.inputBox.onDidChange(() => { if (this.searchConfig.searchOnType) { this._onCancel.fire(); - this.searchOnTypeDelayer.trigger(() => this._onSubmit.fire(false), this.searchConfig.searchOnTypeDebouncePeriod); + this.searchOnTypeDelayer.trigger(() => this._onSubmit.fire(true), this.searchConfig.searchOnTypeDebouncePeriod); } })); @@ -170,7 +170,7 @@ export class PatternInputWidget extends Widget { private onInputKeyUp(keyboardEvent: IKeyboardEvent) { switch (keyboardEvent.keyCode) { case KeyCode.Enter: - this._onSubmit.fire(true); + this._onSubmit.fire(false); return; case KeyCode.Escape: this._onCancel.fire(); From e5e3589293fc74feed0a62a94f8a902784dd2da4 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 14 Nov 2019 10:32:43 -0800 Subject: [PATCH 19/88] Remove double search on glob enter --- src/vs/workbench/contrib/search/browser/patternInputWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts index 01ae7e23081..0d41239f527 100644 --- a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts @@ -170,7 +170,7 @@ export class PatternInputWidget extends Widget { private onInputKeyUp(keyboardEvent: IKeyboardEvent) { switch (keyboardEvent.keyCode) { case KeyCode.Enter: - this._onSubmit.fire(false); + this.searchOnTypeDelayer.trigger(() => this._onSubmit.fire(false), 0); return; case KeyCode.Escape: this._onCancel.fire(); From 8a22996a43318fff1b960c5dfbe79fa1bd18bb19 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 13 Nov 2019 20:13:18 -0800 Subject: [PATCH 20/88] Fix #83715. Handle touch to scroll in interactive playground. --- .../welcome/walkThrough/browser/walkThroughPart.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index d550a53a3c4..149c916f53d 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -5,6 +5,7 @@ import 'vs/css!./walkThroughPart'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; @@ -37,6 +38,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { Dimension, size } from 'vs/base/browser/dom'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { domEvent } from 'vs/base/browser/event'; export const WALK_THROUGH_FOCUS = new RawContextKey('interactivePlaygroundFocus', false); @@ -112,6 +114,14 @@ export class WalkThroughPart extends BaseEditor { } } + private onTouchChange(event: GestureEvent) { + event.preventDefault(); + event.stopPropagation(); + + const scrollPosition = this.scrollbar.getScrollPosition(); + this.scrollbar.setScrollPosition({ scrollTop: scrollPosition.scrollTop - event.translationY }); + } + private addEventListener(element: E, type: K, listener: (this: E, ev: HTMLElementEventMap[K]) => any, useCapture?: boolean): IDisposable; private addEventListener(element: E, type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): IDisposable; private addEventListener(element: E, type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): IDisposable { @@ -396,6 +406,8 @@ export class WalkThroughPart extends BaseEditor { this.scrollbar.scanDomNode(); this.loadTextEditorViewState(input); this.updatedScrollPosition(); + this.contentDisposables.push(Gesture.addTarget(innerContent)); + this.contentDisposables.push(domEvent(innerContent, TouchEventType.Change)(this.onTouchChange, this, this.disposables)); }); } From 50767577edbdc05abac1000b43b9a8bdbaad9cbc Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 13 Nov 2019 20:27:40 -0800 Subject: [PATCH 21/88] Fix #83721. Handle pointer events in Color Picker. --- .../contrib/colorPicker/colorPickerWidget.ts | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts index 4a74a4a5e0d..106c2a357e2 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts @@ -6,7 +6,7 @@ import 'vs/css!./colorPicker'; import { onDidChangeZoomLevel } from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; -import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; +import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger, GlobalPointerMoveMonitor } from 'vs/base/browser/globalMouseMoveMonitor'; import { Widget } from 'vs/base/browser/ui/widget'; import { Color, HSVA, RGBA } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; @@ -14,6 +14,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel'; import { editorHoverBackground } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; const $ = dom.$; @@ -150,13 +151,17 @@ class SaturationBox extends Disposable { this.layout(); - this._register(dom.addDisposableListener(this.domNode, dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e))); + this._register(dom.addDisposableListener(this.domNode, BrowserFeatures.pointerEvents ? dom.EventType.POINTER_DOWN : dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e))); this._register(this.model.onDidChangeColor(this.onDidChangeColor, this)); this.monitor = null; } private onMouseDown(e: MouseEvent): void { - this.monitor = this._register(new GlobalMouseMoveMonitor()); + this.monitor = this._register( + BrowserFeatures.pointerEvents + ? new GlobalPointerMoveMonitor() + : new GlobalMouseMoveMonitor() + ); const origin = dom.getDomNodePagePosition(this.domNode); if (e.target !== this.selection) { @@ -165,7 +170,7 @@ class SaturationBox extends Disposable { this.monitor.startMonitoring(standardMouseMoveMerger, event => this.onDidChangePosition(event.posx - origin.left, event.posy - origin.top), () => null); - const mouseUpListener = dom.addDisposableListener(document, dom.EventType.MOUSE_UP, () => { + const mouseUpListener = dom.addDisposableListener(document, BrowserFeatures.pointerEvents ? dom.EventType.POINTER_UP : dom.EventType.MOUSE_UP, () => { this._onColorFlushed.fire(); mouseUpListener.dispose(); if (this.monitor) { @@ -250,7 +255,7 @@ abstract class Strip extends Disposable { this.slider = dom.append(this.domNode, $('.slider')); this.slider.style.top = `0px`; - this._register(dom.addDisposableListener(this.domNode, dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e))); + this._register(dom.addDisposableListener(this.domNode, BrowserFeatures.pointerEvents ? dom.EventType.POINTER_DOWN : dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e))); this.layout(); } @@ -262,7 +267,11 @@ abstract class Strip extends Disposable { } private onMouseDown(e: MouseEvent): void { - const monitor = this._register(new GlobalMouseMoveMonitor()); + const monitor = this._register( + BrowserFeatures.pointerEvents + ? new GlobalPointerMoveMonitor() + : new GlobalMouseMoveMonitor() + ); const origin = dom.getDomNodePagePosition(this.domNode); dom.addClass(this.domNode, 'grabbing'); @@ -272,7 +281,7 @@ abstract class Strip extends Disposable { monitor.startMonitoring(standardMouseMoveMerger, event => this.onDidChangeTop(event.posy - origin.top), () => null); - const mouseUpListener = dom.addDisposableListener(document, dom.EventType.MOUSE_UP, () => { + const mouseUpListener = dom.addDisposableListener(document, BrowserFeatures.pointerEvents ? dom.EventType.POINTER_UP : dom.EventType.MOUSE_UP, () => { this._onColorFlushed.fire(); mouseUpListener.dispose(); monitor.stopMonitoring(true); From be772cc38f01cf408ace03a758e32b4993a724aa Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 14 Nov 2019 10:28:47 -0800 Subject: [PATCH 22/88] Fix #83719. PointerEvent handler for iOS. --- src/vs/base/browser/canIUse.ts | 3 +- src/vs/base/browser/dom.ts | 17 +++++ src/vs/base/browser/globalMouseMoveMonitor.ts | 37 ++++++++-- .../editor/browser/controller/mouseHandler.ts | 9 ++- .../browser/controller/pointerHandler.ts | 67 ++++++++++++++++++- src/vs/editor/browser/editorDom.ts | 50 +++++++++++++- 6 files changed, 168 insertions(+), 15 deletions(-) diff --git a/src/vs/base/browser/canIUse.ts b/src/vs/base/browser/canIUse.ts index 061d1bfc74f..989b11c370f 100644 --- a/src/vs/base/browser/canIUse.ts +++ b/src/vs/base/browser/canIUse.ts @@ -55,5 +55,6 @@ export const BrowserFeatures = { return KeyboardSupport.None; })(), - touch: 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0 + touch: 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0, + pointerEvents: browser.isSafari && ('ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0) }; diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index db901ed9e5e..cbdf3775398 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -281,6 +281,21 @@ export function addDisposableNonBubblingMouseOutListener(node: Element, handler: }); } +export function addDisposableNonBubblingPointerOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable { + return addDisposableListener(node, 'pointerout', (e: MouseEvent) => { + // Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements + let toElement: Node | null = (e.relatedTarget || e.target); + while (toElement && toElement !== node) { + toElement = toElement.parentNode; + } + if (toElement === node) { + return; + } + + handler(e); + }); +} + interface IRequestAnimationFrame { (callback: (time: number) => void): number; } @@ -852,6 +867,8 @@ export const EventType = { MOUSE_OUT: 'mouseout', MOUSE_ENTER: 'mouseenter', MOUSE_LEAVE: 'mouseleave', + POINTER_UP: 'pointerup', + POINTER_DOWN: 'pointerdown', CONTEXT_MENU: 'contextmenu', WHEEL: 'wheel', // Keyboard diff --git a/src/vs/base/browser/globalMouseMoveMonitor.ts b/src/vs/base/browser/globalMouseMoveMonitor.ts index 109f6f72260..c15a4a4d60f 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -38,10 +38,10 @@ export function standardMouseMoveMerger(lastEvent: IStandardMouseMoveEventData, export class GlobalMouseMoveMonitor implements IDisposable { - private readonly hooks = new DisposableStore(); - private mouseMoveEventMerger: IEventMerger | null = null; - private mouseMoveCallback: IMouseMoveCallback | null = null; - private onStopCallback: IOnStopCallback | null = null; + protected readonly hooks = new DisposableStore(); + protected mouseMoveEventMerger: IEventMerger | null = null; + protected mouseMoveCallback: IMouseMoveCallback | null = null; + protected onStopCallback: IOnStopCallback | null = null; public dispose(): void { this.stopMonitoring(false); @@ -116,3 +116,32 @@ export class GlobalMouseMoveMonitor implements IDisposable { } } } + +export class GlobalPointerMoveMonitor extends GlobalMouseMoveMonitor { + public startMonitoring( + mouseMoveEventMerger: IEventMerger, + mouseMoveCallback: IMouseMoveCallback, + onStopCallback: IOnStopCallback + ): void { + if (this.isMonitoring()) { + // I am already hooked + return; + } + this.mouseMoveEventMerger = mouseMoveEventMerger; + this.mouseMoveCallback = mouseMoveCallback; + this.onStopCallback = onStopCallback; + + let windowChain = IframeUtils.getSameOriginWindowChain(); + for (const element of windowChain) { + this.hooks.add(dom.addDisposableThrottledListener(element.window.document, 'pointermove', + (data: R) => { + this.mouseMoveCallback!(data); + }, + (lastEvent: R, currentEvent) => this.mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent) + )); + this.hooks.add(dom.addDisposableListener(element.window.document, 'pointerup', (e: MouseEvent) => this.stopMonitoring(true))); + } + + // Currently we didn't test pointer events in iframe yet. + } +} diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index d1a19841028..8c8bb743462 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -26,7 +26,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; /** * Merges mouse events when mouse move events are throttled */ -function createMouseMoveEventMerger(mouseTargetFactory: MouseTargetFactory | null) { +export function createMouseMoveEventMerger(mouseTargetFactory: MouseTargetFactory | null) { return function (lastEvent: EditorMouseEvent, currentEvent: EditorMouseEvent): EditorMouseEvent { let targetIsWidget = false; if (mouseTargetFactory) { @@ -71,8 +71,7 @@ export class MouseHandler extends ViewEventHandler { protected viewHelper: IPointerHandlerHelper; protected mouseTargetFactory: MouseTargetFactory; private readonly _asyncFocus: RunOnceScheduler; - - private readonly _mouseDownOperation: MouseDownOperation; + protected readonly _mouseDownOperation: MouseDownOperation; private lastMouseLeaveTime: number; constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { @@ -179,7 +178,7 @@ export class MouseHandler extends ViewEventHandler { }); } - private _onMouseMove(e: EditorMouseEvent): void { + public _onMouseMove(e: EditorMouseEvent): void { if (this._mouseDownOperation.isActive()) { // In selection/drag operation return; @@ -196,7 +195,7 @@ export class MouseHandler extends ViewEventHandler { }); } - private _onMouseLeave(e: EditorMouseEvent): void { + public _onMouseLeave(e: EditorMouseEvent): void { this.lastMouseLeaveTime = (new Date()).getTime(); this.viewController.emitMouseLeave({ event: e, diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 51988d09968..f5891def720 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -6,11 +6,12 @@ import * as dom from 'vs/base/browser/dom'; import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { IPointerHandlerHelper, MouseHandler } from 'vs/editor/browser/controller/mouseHandler'; +import { IPointerHandlerHelper, MouseHandler, createMouseMoveEventMerger } from 'vs/editor/browser/controller/mouseHandler'; import { IMouseTarget } from 'vs/editor/browser/editorBrowser'; -import { EditorMouseEvent } from 'vs/editor/browser/editorDom'; +import { EditorMouseEvent, EditorPointerEventFactory } from 'vs/editor/browser/editorDom'; import { ViewController } from 'vs/editor/browser/view/viewController'; import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { isSafari } from 'vs/base/browser/browser'; interface IThrottledGestureEvent { translationX: number; @@ -185,6 +186,66 @@ class StandardPointerHandler extends MouseHandler implements IDisposable { } } +/** + * Currently only tested on iOS 13/ iPadOS. + */ +export class PointerEventHandler extends MouseHandler { + private _lastPointerType: string; + constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { + super(context, viewController, viewHelper); + + this._register(Gesture.addTarget(this.viewHelper.linesContentDomNode)); + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Tap, (e) => this.onTap(e))); + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Change, (e) => this.onChange(e))); + + this._lastPointerType = 'mouse'; + + this.viewHelper.linesContentDomNode.addEventListener('pointerdown', (e: any) => { + const pointerType = e.pointerType; + if (pointerType === 'mouse') { + this._lastPointerType = 'mouse'; + return; + } else if (pointerType === 'touch') { + this._lastPointerType = 'touch'; + } else { + this._lastPointerType = 'pen'; + } + }); + + // PonterEvents + const pointerEvents = new EditorPointerEventFactory(this.viewHelper.viewDomNode); + + this._register(pointerEvents.onPointerMoveThrottled(this.viewHelper.viewDomNode, + (e) => this._onMouseMove(e), + createMouseMoveEventMerger(this.mouseTargetFactory), MouseHandler.MOUSE_MOVE_MINIMUM_TIME)); + this._register(pointerEvents.onPointerUp(this.viewHelper.viewDomNode, (e) => this._onMouseUp(e))); + this._register(pointerEvents.onPointerLeave(this.viewHelper.viewDomNode, (e) => this._onMouseLeave(e))); + this._register(pointerEvents.onPointerDown(this.viewHelper.viewDomNode, (e) => this._onMouseDown(e))); + } + + private onTap(event: GestureEvent): void { + event.preventDefault(); + this.viewHelper.focusTextArea(); + const target = this._createMouseTarget(new EditorMouseEvent(event, this.viewHelper.viewDomNode), false); + + if (target.position) { + this.viewController.moveTo(target.position); + } + } + + private onChange(e: GestureEvent): void { + if (this._lastPointerType === 'touch') { + this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY); + } + } + + public _onMouseDown(e: EditorMouseEvent): void { + if (this._lastPointerType !== 'touch') { + super._onMouseDown(e); + } + } +} + class TouchHandler extends MouseHandler { constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { @@ -221,6 +282,8 @@ export class PointerHandler extends Disposable { super(); if (window.navigator.msPointerEnabled) { this.handler = this._register(new MsPointerHandler(context, viewController, viewHelper)); + } else if (((window).PointerEvent && isSafari)) { + this.handler = this._register(new PointerEventHandler(context, viewController, viewHelper)); } else if ((window).TouchEvent) { this.handler = this._register(new TouchHandler(context, viewController, viewHelper)); } else if (window.navigator.pointerEnabled || (window).PointerEvent) { diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 92357091f08..0e9ec4b2f52 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -4,9 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { GlobalMouseMoveMonitor } from 'vs/base/browser/globalMouseMoveMonitor'; +import { GlobalMouseMoveMonitor, GlobalPointerMoveMonitor } from 'vs/base/browser/globalMouseMoveMonitor'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; +import { isSafari } from 'vs/base/browser/browser'; /** * Coordinates relative to the whole document (e.g. mouse event's pageX and pageY) @@ -131,16 +133,58 @@ export class EditorMouseEventFactory { } } +export class EditorPointerEventFactory { + + private readonly _editorViewDomNode: HTMLElement; + + constructor(editorViewDomNode: HTMLElement) { + this._editorViewDomNode = editorViewDomNode; + } + + private _create(e: MouseEvent): EditorMouseEvent { + return new EditorMouseEvent(e, this._editorViewDomNode); + } + + public onPointerUp(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { + return dom.addDisposableListener(target, 'pointerup', (e: MouseEvent) => { + callback(this._create(e)); + }); + } + + public onPointerDown(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { + return dom.addDisposableListener(target, 'pointerdown', (e: MouseEvent) => { + callback(this._create(e)); + }); + } + + public onPointerLeave(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { + return dom.addDisposableNonBubblingPointerOutListener(target, (e: MouseEvent) => { + callback(this._create(e)); + }); + } + + public onPointerMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { + const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => { + return merger(lastEvent, this._create(currentEvent)); + }; + return dom.addDisposableThrottledListener(target, 'pointermove', callback, myMerger, minimumTimeMs); + } +} + export class GlobalEditorMouseMoveMonitor extends Disposable { private readonly _editorViewDomNode: HTMLElement; - private readonly _globalMouseMoveMonitor: GlobalMouseMoveMonitor; + protected readonly _globalMouseMoveMonitor: GlobalMouseMoveMonitor; private _keydownListener: IDisposable | null; constructor(editorViewDomNode: HTMLElement) { super(); this._editorViewDomNode = editorViewDomNode; - this._globalMouseMoveMonitor = this._register(new GlobalMouseMoveMonitor()); + this._globalMouseMoveMonitor = this._register( + BrowserFeatures.pointerEvents + ? new GlobalPointerMoveMonitor() + : new GlobalMouseMoveMonitor() + ); this._keydownListener = null; } From 3180eafa4ed3cf481dcb6ed4c411a22f057b8ee0 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 14 Nov 2019 10:31:06 -0800 Subject: [PATCH 23/88] Fix #83719. Trigger Context Menu in Editor with touch. --- src/vs/editor/browser/controller/pointerHandler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index f5891def720..4c7ad07a759 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -197,6 +197,7 @@ export class PointerEventHandler extends MouseHandler { this._register(Gesture.addTarget(this.viewHelper.linesContentDomNode)); this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Tap, (e) => this.onTap(e))); this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Change, (e) => this.onChange(e))); + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Contextmenu, (e: MouseEvent) => this._onContextMenu(new EditorMouseEvent(e, this.viewHelper.viewDomNode), false))); this._lastPointerType = 'mouse'; From 8400359843cb24b3f05063132392798e21057e0a Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 14 Nov 2019 10:47:37 -0800 Subject: [PATCH 24/88] Do not hide context menu when relayout happens on iPadOS. Co-authored-by: penlv@microsoft.com Co-authored-by: sbatten@microsoft.com --- src/vs/base/browser/ui/contextview/contextview.ts | 3 ++- src/vs/workbench/browser/parts/titlebar/menubarControl.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index 098e8b03182..54c9954259b 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -7,6 +7,7 @@ import 'vs/css!./contextview'; import * as DOM from 'vs/base/browser/dom'; import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Range } from 'vs/base/common/range'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; export interface IAnchor { x: number; @@ -178,7 +179,7 @@ export class ContextView extends Disposable { return; } - if (this.delegate!.canRelayout === false) { + if (this.delegate!.canRelayout === false && !BrowserFeatures.pointerEvents) { this.hide(); return; } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index dac036e01dc..0c3d435f0aa 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -42,6 +42,7 @@ import { IElectronService } from 'vs/platform/electron/node/electron'; import { optional } from 'vs/platform/instantiation/common/instantiation'; // tslint:disable-next-line: import-patterns layering TODO@sbatten import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; export abstract class MenubarControl extends Disposable { @@ -673,7 +674,7 @@ export class CustomMenubarControl extends MenubarControl { } this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, () => { - if (this.menubar) { + if (this.menubar && !BrowserFeatures.pointerEvents) { this.menubar.blur(); } })); From 38d848caad12ce1f71fbdb6b050b5a95e086d0ed Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 14 Nov 2019 10:49:09 -0800 Subject: [PATCH 25/88] remove unsed issafari --- src/vs/editor/browser/editorDom.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 0e9ec4b2f52..f27c6b45e57 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -8,7 +8,6 @@ import { GlobalMouseMoveMonitor, GlobalPointerMoveMonitor } from 'vs/base/browse import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; -import { isSafari } from 'vs/base/browser/browser'; /** * Coordinates relative to the whole document (e.g. mouse event's pageX and pageY) From 537fcc46d724913ba8109e63ef932ead859a41b2 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Thu, 14 Nov 2019 10:50:46 -0800 Subject: [PATCH 26/88] Update css LS --- extensions/css-language-features/server/package.json | 2 +- extensions/css-language-features/server/yarn.lock | 8 ++++---- extensions/html-language-features/server/package.json | 2 +- extensions/html-language-features/server/yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 6d426522539..ed0eb4d4926 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.19", + "vscode-css-languageservice": "^4.0.3-next.20", "vscode-languageserver": "^6.0.0-next.3" }, "devDependencies": { diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index b52fde99a00..eade4e07503 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.19: - version "4.0.3-next.19" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.19.tgz#b7dc58fb8d1968877724e163b6ef20b26c0b5ff6" - integrity sha512-wWSo2MZvd8aEI9lf/Asy0MP0Ao6xAzhBeEWxGcF+Ms/zcV4lDRkyFCNzwDt0OgWRzsBNaEBXfjeWLq9rNXkREA== +vscode-css-languageservice@^4.0.3-next.20: + version "4.0.3-next.20" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.20.tgz#d0c4933c33e356617ccfaeb6b7144be196b8b3d9" + integrity sha512-9j32ppmJauPg9XYieAS6alCGazTaeEi8nhSAb11le5qfFNWep4Ngz7dNc/UMlt6hB7BESRyk2B8MM/KTlnsNBw== dependencies: vscode-languageserver-textdocument "^1.0.0-next.4" vscode-languageserver-types "^3.15.0-next.6" diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index dc078cfcb71..6ce9bce3320 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,7 +9,7 @@ }, "main": "./out/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^4.0.3-next.19", + "vscode-css-languageservice": "^4.0.3-next.20", "vscode-html-languageservice": "^3.0.4-next.8", "vscode-languageserver": "^6.0.0-next.3", "vscode-nls": "^4.1.1", diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index f96dfc5df62..4f9260128b2 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -611,10 +611,10 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^4.0.3-next.19: - version "4.0.3-next.19" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.19.tgz#b7dc58fb8d1968877724e163b6ef20b26c0b5ff6" - integrity sha512-wWSo2MZvd8aEI9lf/Asy0MP0Ao6xAzhBeEWxGcF+Ms/zcV4lDRkyFCNzwDt0OgWRzsBNaEBXfjeWLq9rNXkREA== +vscode-css-languageservice@^4.0.3-next.20: + version "4.0.3-next.20" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.20.tgz#d0c4933c33e356617ccfaeb6b7144be196b8b3d9" + integrity sha512-9j32ppmJauPg9XYieAS6alCGazTaeEi8nhSAb11le5qfFNWep4Ngz7dNc/UMlt6hB7BESRyk2B8MM/KTlnsNBw== dependencies: vscode-languageserver-textdocument "^1.0.0-next.4" vscode-languageserver-types "^3.15.0-next.6" From 88c2100793b73cebda2d7753ac4bf52060671653 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Nov 2019 00:46:01 -0800 Subject: [PATCH 27/88] Make sure we always reset bufferSyncSupport when the TS server is started Splits `reset` from `reinitialize` and makes sure we always `resset` buffer sync support when the service starts --- .../src/features/bufferSyncSupport.ts | 3 +++ .../src/typeScriptServiceClientHost.ts | 2 -- .../src/typescriptServiceClient.ts | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index 28e20135206..3cb7f24d80a 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -401,7 +401,10 @@ export default class BufferSyncSupport extends Disposable { this.pendingGetErr?.cancel(); this.pendingDiagnostics.clear(); this.synchronizer.reset(); + } + public reinitialize(): void { + this.reset(); for (const buffer of this.syncedBuffers.allBuffers) { buffer.open(); } diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index c0c1d6edc17..6eeb2ed78ab 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -182,8 +182,6 @@ export default class TypeScriptServiceClientHost extends Disposable { private populateService(): void { this.fileConfigurationManager.reset(); - this.client.bufferSyncSupport.reset(); - this.client.bufferSyncSupport.requestAllDiagnostics(); // See https://github.com/Microsoft/TypeScript/issues/5530 vscode.workspace.saveAll(false).then(() => { diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index e376922dbaf..5d8ea7f1420 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -465,6 +465,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType } private serviceStarted(resendModels: boolean): void { + this.bufferSyncSupport.reset(); + const configureOptions: Proto.ConfigureRequestArguments = { hostInfo: 'vscode', preferences: { @@ -476,6 +478,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.setCompilerOptionsForInferredProjects(this._configuration); if (resendModels) { this._onResendModelsRequested.fire(); + this.bufferSyncSupport.reinitialize(); + this.bufferSyncSupport.requestAllDiagnostics(); } // Reconfigure any plugins From 90aec68c507840481201e594ac801c7f33cc1c47 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 14 Nov 2019 11:59:34 -0800 Subject: [PATCH 28/88] fixes #84806 --- .../parts/titlebar/media/titlebarpart.css | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 265d6518c40..4492478746c 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -19,6 +19,23 @@ display: flex; } +.monaco-workbench.windows .part.titlebar, +.monaco-workbench.linux .part.titlebar, +.monaco-workbench.web .part.titlebar { + /* + * Explicitly put the part onto its own layer to help Chrome to + * render the content with LCD-anti-aliasing. By partioning the + * workbench into multiple layers, we can ensure that a bad + * behaving part is not making another part fallback to greyscale + * rendering. + * + * macOS: does not render LCD-anti-aliased. + */ + transform: translate3d(0px, 0px, 0px); + position: relative; + z-index: 1; +} + .monaco-workbench .part.titlebar > .titlebar-drag-region { top: 0; left: 0; @@ -103,6 +120,7 @@ height: 100%; width: 138px; margin-left: auto; + transform: translate3d(0px, 0px, 0px); } .monaco-workbench.fullscreen .part.titlebar > .window-controls-container { From 402acff09fce85c1a082f6c89d026fd2e4217ac9 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 14 Nov 2019 12:44:16 -0800 Subject: [PATCH 29/88] fixes #84859 --- src/vs/workbench/contrib/search/browser/searchPanel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchPanel.ts b/src/vs/workbench/contrib/search/browser/searchPanel.ts index 0c0def10162..0c50a100a55 100644 --- a/src/vs/workbench/contrib/search/browser/searchPanel.ts +++ b/src/vs/workbench/contrib/search/browser/searchPanel.ts @@ -25,7 +25,7 @@ export class SearchPanel extends Panel { @IInstantiationService instantiationService: IInstantiationService, ) { super(PANEL_ID, telemetryService, themeService, storageService); - this.searchView = this._register(instantiationService.createInstance(SearchView, { id: PANEL_ID, title: localize('search', "Search") })); + this.searchView = this._register(instantiationService.createInstance(SearchView, { id: PANEL_ID, title: localize('search', "Search"), actionRunner: this.getActionRunner() })); this._register(this.searchView.onDidChangeTitleArea(() => this.updateTitleArea())); this._register(this.onDidChangeVisibility(visible => this.searchView.setVisible(visible))); } From 524441ffc2841d9d84651e4c8144b907cb8ce1bd Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 14 Nov 2019 12:58:23 -0800 Subject: [PATCH 30/88] debug: add pwa prefix as an extension host debugger --- src/vs/workbench/contrib/debug/common/debugUtils.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/common/debugUtils.ts b/src/vs/workbench/contrib/debug/common/debugUtils.ts index ca30e45a3ac..38aebf4b083 100644 --- a/src/vs/workbench/contrib/debug/common/debugUtils.ts +++ b/src/vs/workbench/contrib/debug/common/debugUtils.ts @@ -28,7 +28,15 @@ export function isSessionAttach(session: IDebugSession): boolean { } export function isExtensionHostDebugging(config: IConfig) { - return config.type && equalsIgnoreCase(config.type === 'vslsShare' ? (config).adapterProxy.configuration.type : config.type, 'extensionhost'); + if (!config.type) { + return false; + } + + const type = config.type === 'vslsShare' + ? (config).adapterProxy.configuration.type + : config.type; + + return equalsIgnoreCase(type, 'extensionhost') || equalsIgnoreCase(type, 'pwa-extensionhost'); } // only a debugger contributions with a label, program, or runtime attribute is considered a "defining" or "main" debugger contribution From 230baa73721a5813950e7c4509443d407512f8d4 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Thu, 14 Nov 2019 14:59:31 -0800 Subject: [PATCH 31/88] Fix/84231 (#84872) * account for padding on macOS fixes #84231 * move to native context menu service and account for zoom --- .../contextmenu/electron-browser/contextmenuService.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts index cea0688e45f..e607464f902 100644 --- a/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts @@ -91,18 +91,25 @@ class NativeContextMenuService extends Disposable implements IContextMenuService const anchor = delegate.getAnchor(); let x: number, y: number; + let zoom = webFrame.getZoomFactor(); if (dom.isHTMLElement(anchor)) { let elementPosition = dom.getDomNodePagePosition(anchor); x = elementPosition.left; y = elementPosition.top + elementPosition.height; + + // Shift macOS menus by a few pixels below elements + // to account for extra padding on top of native menu + // https://github.com/microsoft/vscode/issues/84231 + if (isMacintosh) { + y += 4 / zoom; + } } else { const pos: { x: number; y: number; } = anchor; x = pos.x + 1; /* prevent first item from being selected automatically under mouse */ y = pos.y; } - let zoom = webFrame.getZoomFactor(); x *= zoom; y *= zoom; From 738d85a51e56b9d373632511b633d6884bef4d1d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Nov 2019 11:46:13 -0800 Subject: [PATCH 32/88] Don't return js/ts fix all for empty edits --- .../typescript-language-features/src/features/fixAll.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/features/fixAll.ts b/extensions/typescript-language-features/src/features/fixAll.ts index d9461442e51..c65dbf472a6 100644 --- a/extensions/typescript-language-features/src/features/fixAll.ts +++ b/extensions/typescript-language-features/src/features/fixAll.ts @@ -82,6 +82,10 @@ class TypeScriptAutoFixProvider implements vscode.CodeActionProvider { return undefined; } const { edit, fixedDiagnostics } = autoFixResponse; + if (!edit.size) { + return undefined; + } + const codeAction = new vscode.CodeAction( localize('autoFix.label', 'Auto fix'), TypeScriptAutoFixProvider.kind); @@ -105,11 +109,11 @@ class TypeScriptAutoFixProvider implements vscode.CodeActionProvider { }; const response = await this.client.execute('getCodeFixes', args, token); if (response.type !== 'response' || !response.body || response.body.length > 1) { - return undefined; + continue; } const fix = response.body[0]; - if (new Set(['fixClassIncorrectlyImplementsInterface', 'spelling']).has(fix.fixName)) { + if (['fixClassIncorrectlyImplementsInterface', 'spelling'].includes(fix.fixName)) { typeConverters.WorkspaceEdit.withFileCodeEdits(edit, this.client, fix.changes); fixedDiagnostics.push(diagnostic); } From 40b8c9d073ec19274f6376f77b879f4fcf39ab43 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Nov 2019 15:35:08 -0800 Subject: [PATCH 33/88] Allow _ in markdown word definitions --- extensions/markdown-language-features/src/extension.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 7180f0a69c8..80595ff26f7 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -54,9 +54,11 @@ function registerMarkdownLanguageFeatures( { language: 'markdown', scheme: 'untitled' } ]; + const charPattern = '(\\p{Alphabetic}|\\p{Number}|\\p{Nonspacing_Mark})'; + return vscode.Disposable.from( vscode.languages.setLanguageConfiguration('markdown', { - wordPattern: new RegExp('(\\p{Alphabetic}|\\p{Number}|\\p{Nonspacing_Mark})+', 'ug'), + wordPattern: new RegExp(`${charPattern}((${charPattern}|[_])?${charPattern})*`, 'ug'), }), vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider), vscode.languages.registerDocumentLinkProvider(selector, new LinkProvider()), From 01a1603d5139ad433968c51d20948987ab8b76f5 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 14 Nov 2019 16:30:42 -0800 Subject: [PATCH 34/88] Global mouse move handler should understand pointer move. --- src/vs/base/browser/globalMouseMoveMonitor.ts | 36 +++---------------- src/vs/editor/browser/editorDom.ts | 9 ++--- .../contrib/colorPicker/colorPickerWidget.ts | 14 ++------ 3 files changed, 10 insertions(+), 49 deletions(-) diff --git a/src/vs/base/browser/globalMouseMoveMonitor.ts b/src/vs/base/browser/globalMouseMoveMonitor.ts index c15a4a4d60f..b24a3db0e50 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -7,6 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import { IframeUtils } from 'vs/base/browser/iframe'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; export interface IStandardMouseMoveEventData { leftButton: boolean; @@ -84,12 +85,14 @@ export class GlobalMouseMoveMonitor implements IDisposable { this.onStopCallback = onStopCallback; let windowChain = IframeUtils.getSameOriginWindowChain(); + const mouseMove = BrowserFeatures.pointerEvents ? 'pointermove' : 'mousemove'; + const mouseUp = BrowserFeatures.pointerEvents ? 'pointerup' : 'mouseup'; for (const element of windowChain) { - this.hooks.add(dom.addDisposableThrottledListener(element.window.document, 'mousemove', + this.hooks.add(dom.addDisposableThrottledListener(element.window.document, mouseMove, (data: R) => this.mouseMoveCallback!(data), (lastEvent: R, currentEvent) => this.mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent) )); - this.hooks.add(dom.addDisposableListener(element.window.document, 'mouseup', (e: MouseEvent) => this.stopMonitoring(true))); + this.hooks.add(dom.addDisposableListener(element.window.document, mouseUp, (e: MouseEvent) => this.stopMonitoring(true))); } if (IframeUtils.hasDifferentOriginAncestor()) { @@ -116,32 +119,3 @@ export class GlobalMouseMoveMonitor implements IDisposable { } } } - -export class GlobalPointerMoveMonitor extends GlobalMouseMoveMonitor { - public startMonitoring( - mouseMoveEventMerger: IEventMerger, - mouseMoveCallback: IMouseMoveCallback, - onStopCallback: IOnStopCallback - ): void { - if (this.isMonitoring()) { - // I am already hooked - return; - } - this.mouseMoveEventMerger = mouseMoveEventMerger; - this.mouseMoveCallback = mouseMoveCallback; - this.onStopCallback = onStopCallback; - - let windowChain = IframeUtils.getSameOriginWindowChain(); - for (const element of windowChain) { - this.hooks.add(dom.addDisposableThrottledListener(element.window.document, 'pointermove', - (data: R) => { - this.mouseMoveCallback!(data); - }, - (lastEvent: R, currentEvent) => this.mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent) - )); - this.hooks.add(dom.addDisposableListener(element.window.document, 'pointerup', (e: MouseEvent) => this.stopMonitoring(true))); - } - - // Currently we didn't test pointer events in iframe yet. - } -} diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index f27c6b45e57..60df7e8f1b8 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { GlobalMouseMoveMonitor, GlobalPointerMoveMonitor } from 'vs/base/browser/globalMouseMoveMonitor'; +import { GlobalMouseMoveMonitor } from 'vs/base/browser/globalMouseMoveMonitor'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { BrowserFeatures } from 'vs/base/browser/canIUse'; /** * Coordinates relative to the whole document (e.g. mouse event's pageX and pageY) @@ -179,11 +178,7 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { constructor(editorViewDomNode: HTMLElement) { super(); this._editorViewDomNode = editorViewDomNode; - this._globalMouseMoveMonitor = this._register( - BrowserFeatures.pointerEvents - ? new GlobalPointerMoveMonitor() - : new GlobalMouseMoveMonitor() - ); + this._globalMouseMoveMonitor = this._register(new GlobalMouseMoveMonitor()); this._keydownListener = null; } diff --git a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts index 106c2a357e2..0c3f47322b8 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts @@ -6,7 +6,7 @@ import 'vs/css!./colorPicker'; import { onDidChangeZoomLevel } from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; -import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger, GlobalPointerMoveMonitor } from 'vs/base/browser/globalMouseMoveMonitor'; +import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; import { Widget } from 'vs/base/browser/ui/widget'; import { Color, HSVA, RGBA } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; @@ -157,11 +157,7 @@ class SaturationBox extends Disposable { } private onMouseDown(e: MouseEvent): void { - this.monitor = this._register( - BrowserFeatures.pointerEvents - ? new GlobalPointerMoveMonitor() - : new GlobalMouseMoveMonitor() - ); + this.monitor = this._register(new GlobalMouseMoveMonitor()); const origin = dom.getDomNodePagePosition(this.domNode); if (e.target !== this.selection) { @@ -267,11 +263,7 @@ abstract class Strip extends Disposable { } private onMouseDown(e: MouseEvent): void { - const monitor = this._register( - BrowserFeatures.pointerEvents - ? new GlobalPointerMoveMonitor() - : new GlobalMouseMoveMonitor() - ); + const monitor = this._register(new GlobalMouseMoveMonitor()); const origin = dom.getDomNodePagePosition(this.domNode); dom.addClass(this.domNode, 'grabbing'); From 36a8322ae8c969640b7853ce50b8d580cc35c2f1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Nov 2019 16:49:25 -0800 Subject: [PATCH 35/88] Convert SyncActionDescriptor to use a `create` function (#84878) For #81574 See #84669 for details of the problem around strict function types. This change converts `SyncActionDescriptor` to use a static `create` function. This allows us to make the `create` function generic so that it can take the correct types for strictFunctionTypes --- src/vs/platform/actions/common/actions.ts | 10 +- .../api/browser/viewsExtensionPoint.ts | 2 +- .../browser/actions/developerActions.ts | 6 +- .../workbench/browser/actions/helpActions.ts | 18 +- .../browser/actions/layoutActions.ts | 24 +-- .../browser/actions/navigationActions.ts | 8 +- .../browser/actions/windowActions.ts | 12 +- .../browser/actions/workspaceActions.ts | 10 +- .../parts/activitybar/activitybarActions.ts | 4 +- .../parts/editor/editor.contribution.ts | 154 +++++++++--------- .../browser/parts/panel/panelActions.ts | 16 +- .../parts/quickinput/quickInputActions.ts | 2 +- .../parts/quickopen/quickOpenActions.ts | 12 +- .../browser/parts/sidebar/sidebarPart.ts | 2 +- .../contrib/cli/node/cli.contribution.ts | 4 +- .../codeEditor/browser/inspectKeybindings.ts | 2 +- .../codeEditor/browser/toggleMinimap.ts | 2 +- .../browser/toggleMultiCursorModifier.ts | 4 +- .../browser/toggleRenderControlCharacter.ts | 2 +- .../browser/toggleRenderWhitespace.ts | 2 +- .../debug/browser/debug.contribution.ts | 24 +-- .../browser/extensions.contribution.ts | 46 +++--- .../extensions.contribution.ts | 4 +- .../files/browser/fileActions.contribution.ts | 26 +-- .../files/browser/files.contribution.ts | 2 +- .../fileActions.contribution.ts | 2 +- .../electron-browser/issue.contribution.ts | 6 +- .../browser/localizations.contribution.ts | 2 +- .../contrib/logs/common/logs.contribution.ts | 4 +- .../electron-browser/logs.contribution.ts | 2 +- .../markers/browser/markers.contribution.ts | 4 +- .../output/browser/output.contribution.ts | 8 +- .../browser/keyboardLayoutPicker.ts | 2 +- .../browser/preferences.contribution.ts | 16 +- .../browser/quickopen.contribution.ts | 12 +- .../contrib/remote/browser/remote.ts | 2 +- .../contrib/scm/browser/scm.contribution.ts | 2 +- .../search/browser/search.contribution.ts | 18 +- .../tasks/browser/task.contribution.ts | 2 +- .../terminal/browser/terminal.contribution.ts | 124 +++++++------- .../electron-browser/terminalRemote.ts | 2 +- .../browser/testCustomEditors.ts | 2 +- .../themes/browser/themes.contribution.ts | 6 +- .../update/browser/update.contribution.ts | 4 +- .../contrib/url/common/url.contribution.ts | 2 +- .../webview/browser/webview.contribution.ts | 2 +- .../electron-browser/webview.contribution.ts | 2 +- .../welcome/overlay/browser/welcomeOverlay.ts | 4 +- .../page/browser/welcomePage.contribution.ts | 2 +- .../browser/walkThrough.contribution.ts | 2 +- .../electron-browser/desktop.contribution.ts | 20 +-- .../extensions/browser/extensionUrlHandler.ts | 2 +- .../common/extensionHostProcessManager.ts | 2 +- .../common/keybindingsEditorModel.test.ts | 2 +- 54 files changed, 332 insertions(+), 326 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index a583f7a34f5..2cea44d8589 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -5,7 +5,7 @@ import { Action } from 'vs/base/common/actions'; import { SyncDescriptor0, createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { IConstructorSignature2, createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IConstructorSignature2, createDecorator, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -295,7 +295,13 @@ export class SyncActionDescriptor { private readonly _keybindingContext: ContextKeyExpr | undefined; private readonly _keybindingWeight: number | undefined; - constructor(ctor: IConstructorSignature2, + public static create(ctor: { new(id: string, label: string, ...services: Services): Action }, + id: string, label: string | undefined, keybindings?: IKeybindings, keybindingContext?: ContextKeyExpr, keybindingWeight?: number + ): SyncActionDescriptor { + return new SyncActionDescriptor(ctor as IConstructorSignature2, id, label, keybindings, keybindingContext, keybindingWeight); + } + + private constructor(ctor: IConstructorSignature2, id: string, label: string | undefined, keybindings?: IKeybindings, keybindingContext?: ContextKeyExpr, keybindingWeight?: number ) { this._id = id; diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 0f1fe0a1eee..efbc23c293f 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -359,7 +359,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction( - new SyncActionDescriptor(OpenCustomViewletAction, id, localize('showViewlet', "Show {0}", title)), + SyncActionDescriptor.create(OpenCustomViewletAction, id, localize('showViewlet', "Show {0}", title)), `View: Show ${title}`, localize('view', "View") ); diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index abf8bdb9d12..cf9666dc65d 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -214,9 +214,9 @@ class LogStorageAction extends Action { const developerCategory = nls.localize('developer', "Developer"); const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(InspectContextKeysAction, InspectContextKeysAction.ID, InspectContextKeysAction.LABEL), 'Developer: Inspect Context Keys', developerCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleScreencastModeAction, ToggleScreencastModeAction.ID, ToggleScreencastModeAction.LABEL), 'Developer: Toggle Screencast Mode', developerCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(LogStorageAction, LogStorageAction.ID, LogStorageAction.LABEL), 'Developer: Log Storage Database Contents', developerCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(InspectContextKeysAction, InspectContextKeysAction.ID, InspectContextKeysAction.LABEL), 'Developer: Inspect Context Keys', developerCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleScreencastModeAction, ToggleScreencastModeAction.ID, ToggleScreencastModeAction.LABEL), 'Developer: Toggle Screencast Mode', developerCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(LogStorageAction, LogStorageAction.ID, LogStorageAction.LABEL), 'Developer: Log Storage Database Contents', developerCategory); // Screencast Mode const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); diff --git a/src/vs/workbench/browser/actions/helpActions.ts b/src/vs/workbench/browser/actions/helpActions.ts index e16e460cff0..2ecc5026fc8 100644 --- a/src/vs/workbench/browser/actions/helpActions.ts +++ b/src/vs/workbench/browser/actions/helpActions.ts @@ -248,39 +248,39 @@ const registry = Registry.as(Extensions.WorkbenchActio const helpCategory = nls.localize('help', "Help"); if (KeybindingsReferenceAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(KeybindingsReferenceAction, KeybindingsReferenceAction.ID, KeybindingsReferenceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_R) }), 'Help: Keyboard Shortcuts Reference', helpCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(KeybindingsReferenceAction, KeybindingsReferenceAction.ID, KeybindingsReferenceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_R) }), 'Help: Keyboard Shortcuts Reference', helpCategory); } if (OpenDocumentationUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDocumentationUrlAction, OpenDocumentationUrlAction.ID, OpenDocumentationUrlAction.LABEL), 'Help: Documentation', helpCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenDocumentationUrlAction, OpenDocumentationUrlAction.ID, OpenDocumentationUrlAction.LABEL), 'Help: Documentation', helpCategory); } if (OpenIntroductoryVideosUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenIntroductoryVideosUrlAction, OpenIntroductoryVideosUrlAction.ID, OpenIntroductoryVideosUrlAction.LABEL), 'Help: Introductory Videos', helpCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenIntroductoryVideosUrlAction, OpenIntroductoryVideosUrlAction.ID, OpenIntroductoryVideosUrlAction.LABEL), 'Help: Introductory Videos', helpCategory); } if (OpenTipsAndTricksUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenTipsAndTricksUrlAction, OpenTipsAndTricksUrlAction.ID, OpenTipsAndTricksUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenTipsAndTricksUrlAction, OpenTipsAndTricksUrlAction.ID, OpenTipsAndTricksUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); } if (OpenNewsletterSignupUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNewsletterSignupUrlAction, OpenNewsletterSignupUrlAction.ID, OpenNewsletterSignupUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenNewsletterSignupUrlAction, OpenNewsletterSignupUrlAction.ID, OpenNewsletterSignupUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); } if (OpenTwitterUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join Us on Twitter', helpCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join Us on Twitter', helpCategory); } if (OpenRequestFeatureUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory); } if (OpenLicenseUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory); } if (OpenPrivacyStatementUrlAction.AVAILABE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory); } // --- Menu Registration diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 944c253c1b2..65a1a71dd5b 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -56,7 +56,7 @@ export class ToggleActivityBarVisibilityAction extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', @@ -91,7 +91,7 @@ class ToggleCenteredLayout extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '1_toggle_view', @@ -143,7 +143,7 @@ export class ToggleEditorLayoutAction extends Action { } const group = viewCategory; -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Toggle Vertical/Horizontal Editor Layout', group); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Toggle Vertical/Horizontal Editor Layout', group); MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { group: 'z_flip', @@ -186,7 +186,7 @@ export class ToggleSidebarPositionAction extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.LABEL), 'View: Toggle Side Bar Position', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.LABEL), 'View: Toggle Side Bar Position', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '3_workbench_layout_move', @@ -231,7 +231,7 @@ export class ToggleEditorVisibilityAction extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorVisibilityAction, ToggleEditorVisibilityAction.ID, ToggleEditorVisibilityAction.LABEL), 'View: Toggle Editor Area Visibility', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleEditorVisibilityAction, ToggleEditorVisibilityAction.ID, ToggleEditorVisibilityAction.LABEL), 'View: Toggle Editor Area Visibility', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', @@ -266,7 +266,7 @@ export class ToggleSidebarVisibilityAction extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { group: '2_appearance', @@ -313,7 +313,7 @@ export class ToggleStatusbarVisibilityAction extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', @@ -350,7 +350,7 @@ class ToggleTabsVisibilityAction extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, { +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, { primary: undefined, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, }, linux: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, } @@ -379,7 +379,7 @@ class ToggleZenMode extends Action { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', viewCategory); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '1_toggle_view', @@ -442,7 +442,7 @@ export class ToggleMenuBarAction extends Action { } if (isWindows || isLinux || isWeb) { - registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMenuBarAction, ToggleMenuBarAction.ID, ToggleMenuBarAction.LABEL), 'View: Toggle Menu Bar', viewCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMenuBarAction, ToggleMenuBarAction.ID, ToggleMenuBarAction.LABEL), 'View: Toggle Menu Bar', viewCategory); } MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { @@ -529,5 +529,5 @@ export class DecreaseViewSizeAction extends BaseResizeViewAction { } } -registry.registerWorkbenchAction(new SyncActionDescriptor(IncreaseViewSizeAction, IncreaseViewSizeAction.ID, IncreaseViewSizeAction.LABEL, undefined), 'View: Increase Current View Size', viewCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(DecreaseViewSizeAction, DecreaseViewSizeAction.ID, DecreaseViewSizeAction.LABEL, undefined), 'View: Decrease Current View Size', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(IncreaseViewSizeAction, IncreaseViewSizeAction.ID, IncreaseViewSizeAction.LABEL, undefined), 'View: Increase Current View Size', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(DecreaseViewSizeAction, DecreaseViewSizeAction.ID, DecreaseViewSizeAction.LABEL, undefined), 'View: Decrease Current View Size', viewCategory); diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts index fae9742c7bf..538edac8a28 100644 --- a/src/vs/workbench/browser/actions/navigationActions.ts +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -280,7 +280,7 @@ class NavigateDownAction extends BaseNavigationAction { const registry = Registry.as(Extensions.WorkbenchActions); const viewCategory = nls.localize('view', "View"); -registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateUpAction, NavigateUpAction.ID, NavigateUpAction.LABEL, undefined), 'View: Navigate to the View Above', viewCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateDownAction, NavigateDownAction.ID, NavigateDownAction.LABEL, undefined), 'View: Navigate to the View Below', viewCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateLeftAction, NavigateLeftAction.ID, NavigateLeftAction.LABEL, undefined), 'View: Navigate to the View on the Left', viewCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateRightAction, NavigateRightAction.ID, NavigateRightAction.LABEL, undefined), 'View: Navigate to the View on the Right', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateUpAction, NavigateUpAction.ID, NavigateUpAction.LABEL, undefined), 'View: Navigate to the View Above', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateDownAction, NavigateDownAction.ID, NavigateDownAction.LABEL, undefined), 'View: Navigate to the View Below', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateLeftAction, NavigateLeftAction.ID, NavigateLeftAction.LABEL, undefined), 'View: Navigate to the View on the Left', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateRightAction, NavigateRightAction.ID, NavigateRightAction.LABEL, undefined), 'View: Navigate to the View on the Right', viewCategory); diff --git a/src/vs/workbench/browser/actions/windowActions.ts b/src/vs/workbench/browser/actions/windowActions.ts index c4459105483..e59fdc54f75 100644 --- a/src/vs/workbench/browser/actions/windowActions.ts +++ b/src/vs/workbench/browser/actions/windowActions.ts @@ -269,18 +269,18 @@ const registry = Registry.as(Extensions.WorkbenchActio // --- Actions Registration const fileCategory = nls.localize('file', "File"); -registry.registerWorkbenchAction(new SyncActionDescriptor(NewWindowAction, NewWindowAction.ID, NewWindowAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_N }), 'New Window'); -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', fileCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NewWindowAction, NewWindowAction.ID, NewWindowAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_N }), 'New Window'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', fileCategory); const viewCategory = nls.localize('view', "View"); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleFullScreenAction, ToggleFullScreenAction.ID, ToggleFullScreenAction.LABEL, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleFullScreenAction, ToggleFullScreenAction.ID, ToggleFullScreenAction.LABEL, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory); const developerCategory = nls.localize('developer', "Developer"); -registry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL), 'Developer: Reload Window', developerCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL), 'Developer: Reload Window', developerCategory); const helpCategory = nls.localize('help', "Help"); -registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAboutDialogAction, ShowAboutDialogAction.ID, ShowAboutDialogAction.LABEL), `Help: About`, helpCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowAboutDialogAction, ShowAboutDialogAction.ID, ShowAboutDialogAction.LABEL), `Help: About`, helpCategory); // --- Commands/Keybindings Registration diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 0834621124a..b129dc792f8 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -259,11 +259,11 @@ export class DuplicateWorkspaceInNewWindowAction extends Action { const registry = Registry.as(Extensions.WorkbenchActions); const workspacesCategory = nls.localize('workspaces', "Workspaces"); -registry.registerWorkbenchAction(new SyncActionDescriptor(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'Workspaces: Close Workspace', workspacesCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(SaveWorkspaceAsAction, SaveWorkspaceAsAction.ID, SaveWorkspaceAsAction.LABEL), 'Workspaces: Save Workspace As...', workspacesCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(DuplicateWorkspaceInNewWindowAction, DuplicateWorkspaceInNewWindowAction.ID, DuplicateWorkspaceInNewWindowAction.LABEL), 'Workspaces: Duplicate Workspace in New Window', workspacesCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'Workspaces: Close Workspace', workspacesCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(SaveWorkspaceAsAction, SaveWorkspaceAsAction.ID, SaveWorkspaceAsAction.LABEL), 'Workspaces: Save Workspace As...', workspacesCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(DuplicateWorkspaceInNewWindowAction, DuplicateWorkspaceInNewWindowAction.ID, DuplicateWorkspaceInNewWindowAction.LABEL), 'Workspaces: Duplicate Workspace in New Window', workspacesCategory); // --- Menu Registration diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index bd8425e0555..15acfe38cd9 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -267,8 +267,8 @@ export class NextSideBarViewAction extends SwitchSideBarViewAction { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(PreviousSideBarViewAction, PreviousSideBarViewAction.ID, PreviousSideBarViewAction.LABEL), 'View: Previous Side Bar View', nls.localize('view', "View")); -registry.registerWorkbenchAction(new SyncActionDescriptor(NextSideBarViewAction, NextSideBarViewAction.ID, NextSideBarViewAction.LABEL), 'View: Next Side Bar View', nls.localize('view', "View")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(PreviousSideBarViewAction, PreviousSideBarViewAction.ID, PreviousSideBarViewAction.LABEL), 'View: Previous Side Bar View', nls.localize('view', "View")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NextSideBarViewAction, NextSideBarViewAction.ID, NextSideBarViewAction.LABEL), 'View: Next Side Bar View', nls.localize('view', "View")); registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index fe4d6da6b58..2e25ce67d18 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -226,11 +226,11 @@ Registry.as(WorkbenchExtensions.Workbench).regi // Register Status Actions const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeModeAction, ChangeModeAction.ID, ChangeModeAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_M) }), 'Change Language Mode'); -registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEOLAction, ChangeEOLAction.ID, ChangeEOLAction.LABEL), 'Change End of Line Sequence'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ChangeModeAction, ChangeModeAction.ID, ChangeModeAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_M) }), 'Change Language Mode'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ChangeEOLAction, ChangeEOLAction.ID, ChangeEOLAction.LABEL), 'Change End of Line Sequence'); if (Object.keys(SUPPORTED_ENCODINGS).length > 1) { - registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEncodingAction, ChangeEncodingAction.ID, ChangeEncodingAction.LABEL), 'Change File Encoding'); + registry.registerWorkbenchAction(SyncActionDescriptor.create(ChangeEncodingAction, ChangeEncodingAction.ID, ChangeEncodingAction.LABEL), 'Change File Encoding'); } export class QuickOpenActionContributor extends ActionBarContributor { @@ -312,84 +312,84 @@ Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpen // Register Editor Actions const category = nls.localize('view', "View"); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditorInGroup, OpenNextEditorInGroup.ID, OpenNextEditorInGroup.LABEL), 'View: Open Next Editor in Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorInGroup, OpenPreviousEditorInGroup.ID, OpenPreviousEditorInGroup.LABEL), 'View: Open Previous Editor in Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLastEditorInGroup, OpenLastEditorInGroup.ID, OpenLastEditorInGroup.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_0, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_9], mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_0, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_9] } }), 'View: Open Last Editor in Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFirstEditorInGroup, OpenFirstEditorInGroup.ID, OpenFirstEditorInGroup.LABEL), 'View: Open First Editor in Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorAction, OpenNextRecentlyUsedEditorAction.ID, OpenNextRecentlyUsedEditorAction.LABEL), 'View: Open Next Recently Used Editor', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction.ID, OpenPreviousRecentlyUsedEditorAction.LABEL), 'View: Open Previous Recently Used Editor', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllEditorsAction, ShowAllEditorsAction.ID, ShowAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_P), mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInActiveGroupAction, ShowEditorsInActiveGroupAction.ID, ShowEditorsInActiveGroupAction.LABEL), 'View: Show Editors in Active Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditor, OpenNextEditor.ID, OpenNextEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageDown, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET] } }), 'View: Open Next Editor', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditor, OpenPreviousEditor.ID, OpenPreviousEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET] } }), 'View: Open Previous Editor', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ReopenClosedEditorAction, ReopenClosedEditorAction.ID, ReopenClosedEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), 'View: Reopen Closed Editor', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ClearRecentFilesAction, ClearRecentFilesAction.ID, ClearRecentFilesAction.LABEL), 'File: Clear Recently Opened', nls.localize('file', "File")); -registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_W) }), 'View: Close All Editors', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorGroupsAction, CloseAllEditorGroupsAction.ID, CloseAllEditorGroupsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W) }), 'View: Close All Editor Groups', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(CloseLeftEditorsInGroupAction, CloseLeftEditorsInGroupAction.ID, CloseLeftEditorsInGroupAction.LABEL), 'View: Close Editors to the Left in Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorsInOtherGroupsAction, CloseEditorsInOtherGroupsAction.ID, CloseEditorsInOtherGroupsAction.LABEL), 'View: Close Editors in Other Groups', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorInAllGroupsAction, CloseEditorInAllGroupsAction.ID, CloseEditorInAllGroupsAction.LABEL), 'View: Close Editor in All Groups', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH }), 'View: Split Editor', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorOrthogonalAction, SplitEditorOrthogonalAction.ID, SplitEditorOrthogonalAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_BACKSLASH) }), 'View: Split Editor Orthogonal', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorLeftAction, SplitEditorLeftAction.ID, SplitEditorLeftAction.LABEL), 'View: Split Editor Left', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorRightAction, SplitEditorRightAction.ID, SplitEditorRightAction.LABEL), 'View: Split Editor Right', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorUpAction, SplitEditorUpAction.ID, SplitEditorUpAction.LABEL), 'Split Editor Up', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorDownAction, SplitEditorDownAction.ID, SplitEditorDownAction.LABEL), 'View: Split Editor Down', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(JoinTwoGroupsAction, JoinTwoGroupsAction.ID, JoinTwoGroupsAction.LABEL), 'View: Join Editor Group with Next Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(JoinAllGroupsAction, JoinAllGroupsAction.ID, JoinAllGroupsAction.LABEL), 'View: Join All Editor Groups', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBetweenGroupsAction, NavigateBetweenGroupsAction.ID, NavigateBetweenGroupsAction.LABEL), 'View: Navigate Between Editor Groups', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ResetGroupSizesAction, ResetGroupSizesAction.ID, ResetGroupSizesAction.LABEL), 'View: Reset Editor Group Sizes', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleGroupSizesAction, ToggleGroupSizesAction.ID, ToggleGroupSizesAction.LABEL), 'View: Toggle Editor Group Sizes', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Side Bar', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MinimizeOtherGroupsAction, MinimizeOtherGroupsAction.ID, MinimizeOtherGroupsAction.LABEL), 'View: Maximize Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorLeftInGroupAction, MoveEditorLeftInGroupAction.ID, MoveEditorLeftInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageUp, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow) } }), 'View: Move Editor Left', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorRightInGroupAction, MoveEditorRightInGroupAction.ID, MoveEditorRightInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow) } }), 'View: Move Editor Right', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupLeftAction, MoveGroupLeftAction.ID, MoveGroupLeftAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.LeftArrow) }), 'View: Move Editor Group Left', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupRightAction, MoveGroupRightAction.ID, MoveGroupRightAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.RightArrow) }), 'View: Move Editor Group Right', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupUpAction, MoveGroupUpAction.ID, MoveGroupUpAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.UpArrow) }), 'View: Move Editor Group Up', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupDownAction, MoveGroupDownAction.ID, MoveGroupDownAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.DownArrow) }), 'View: Move Editor Group Down', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToPreviousGroupAction, MoveEditorToPreviousGroupAction.ID, MoveEditorToPreviousGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }), 'View: Move Editor into Previous Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToNextGroupAction, MoveEditorToNextGroupAction.ID, MoveEditorToNextGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }), 'View: Move Editor into Next Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToFirstGroupAction, MoveEditorToFirstGroupAction.ID, MoveEditorToFirstGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_1, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_1 } }), 'View: Move Editor into First Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToLastGroupAction, MoveEditorToLastGroupAction.ID, MoveEditorToLastGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_9, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_9 } }), 'View: Move Editor into Last Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToLeftGroupAction, MoveEditorToLeftGroupAction.ID, MoveEditorToLeftGroupAction.LABEL), 'View: Move Editor into Left Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToRightGroupAction, MoveEditorToRightGroupAction.ID, MoveEditorToRightGroupAction.LABEL), 'View: Move Editor into Right Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToAboveGroupAction, MoveEditorToAboveGroupAction.ID, MoveEditorToAboveGroupAction.LABEL), 'View: Move Editor into Above Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToBelowGroupAction, MoveEditorToBelowGroupAction.ID, MoveEditorToBelowGroupAction.LABEL), 'View: Move Editor into Below Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveGroupAction, FocusActiveGroupAction.ID, FocusActiveGroupAction.LABEL), 'View: Focus Active Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_1 }), 'View: Focus First Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusLastGroupAction, FocusLastGroupAction.ID, FocusLastGroupAction.LABEL), 'View: Focus Last Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousGroup, FocusPreviousGroup.ID, FocusPreviousGroup.LABEL), 'View: Focus Previous Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextGroup, FocusNextGroup.ID, FocusNextGroup.LABEL), 'View: Focus Next Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusLeftGroup, FocusLeftGroup.ID, FocusLeftGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Left Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusRightGroup, FocusRightGroup.ID, FocusRightGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Right Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusAboveGroup, FocusAboveGroup.ID, FocusAboveGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.UpArrow) }), 'View: Focus Above Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusBelowGroup, FocusBelowGroup.ID, FocusBelowGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.DownArrow) }), 'View: Focus Below Editor Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupLeftAction, NewEditorGroupLeftAction.ID, NewEditorGroupLeftAction.LABEL), 'View: New Editor Group to the Left', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupRightAction, NewEditorGroupRightAction.ID, NewEditorGroupRightAction.LABEL), 'View: New Editor Group to the Right', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupAboveAction, NewEditorGroupAboveAction.ID, NewEditorGroupAboveAction.LABEL), 'View: New Editor Group Above', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(NewEditorGroupBelowAction, NewEditorGroupBelowAction.ID, NewEditorGroupBelowAction.LABEL), 'View: New Editor Group Below', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateForwardAction, NavigateForwardAction.ID, NavigateForwardAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.RightArrow }, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS } }), 'Go Forward'); -registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBackwardsAction, NavigateBackwardsAction.ID, NavigateBackwardsAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }), 'Go Back'); -registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateToLastEditLocationAction, NavigateToLastEditLocationAction.ID, NavigateToLastEditLocationAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_Q) }), 'Go to Last Edit Location'); -registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateLastAction, NavigateLastAction.ID, NavigateLastAction.LABEL), 'Go Last'); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorFromHistoryAction, OpenPreviousEditorFromHistoryAction.ID, OpenPreviousEditorFromHistoryAction.LABEL), 'Open Previous Editor from History'); -registry.registerWorkbenchAction(new SyncActionDescriptor(ClearEditorHistoryAction, ClearEditorHistoryAction.ID, ClearEditorHistoryAction.LABEL), 'Clear Editor History'); -registry.registerWorkbenchAction(new SyncActionDescriptor(RevertAndCloseEditorAction, RevertAndCloseEditorAction.ID, RevertAndCloseEditorAction.LABEL), 'View: Revert and Close Editor', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutSingleAction, EditorLayoutSingleAction.ID, EditorLayoutSingleAction.LABEL), 'View: Single Column Editor Layout', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoColumnsAction, EditorLayoutTwoColumnsAction.ID, EditorLayoutTwoColumnsAction.LABEL), 'View: Two Columns Editor Layout', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutThreeColumnsAction, EditorLayoutThreeColumnsAction.ID, EditorLayoutThreeColumnsAction.LABEL), 'View: Three Columns Editor Layout', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoRowsAction, EditorLayoutTwoRowsAction.ID, EditorLayoutTwoRowsAction.LABEL), 'View: Two Rows Editor Layout', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutThreeRowsAction, EditorLayoutThreeRowsAction.ID, EditorLayoutThreeRowsAction.LABEL), 'View: Three Rows Editor Layout', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoByTwoGridAction, EditorLayoutTwoByTwoGridAction.ID, EditorLayoutTwoByTwoGridAction.LABEL), 'View: Grid Editor Layout (2x2)', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoRowsRightAction, EditorLayoutTwoRowsRightAction.ID, EditorLayoutTwoRowsRightAction.LABEL), 'View: Two Rows Right Editor Layout', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoColumnsBottomAction.ID, EditorLayoutTwoColumnsBottomAction.LABEL), 'View: Two Columns Bottom Editor Layout', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenNextEditorInGroup, OpenNextEditorInGroup.ID, OpenNextEditorInGroup.LABEL), 'View: Open Next Editor in Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenPreviousEditorInGroup, OpenPreviousEditorInGroup.ID, OpenPreviousEditorInGroup.LABEL), 'View: Open Previous Editor in Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenLastEditorInGroup, OpenLastEditorInGroup.ID, OpenLastEditorInGroup.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_0, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_9], mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_0, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_9] } }), 'View: Open Last Editor in Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenFirstEditorInGroup, OpenFirstEditorInGroup.ID, OpenFirstEditorInGroup.LABEL), 'View: Open First Editor in Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenNextRecentlyUsedEditorAction, OpenNextRecentlyUsedEditorAction.ID, OpenNextRecentlyUsedEditorAction.LABEL), 'View: Open Next Recently Used Editor', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction.ID, OpenPreviousRecentlyUsedEditorAction.LABEL), 'View: Open Previous Recently Used Editor', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowAllEditorsAction, ShowAllEditorsAction.ID, ShowAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_P), mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowEditorsInActiveGroupAction, ShowEditorsInActiveGroupAction.ID, ShowEditorsInActiveGroupAction.LABEL), 'View: Show Editors in Active Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenNextEditor, OpenNextEditor.ID, OpenNextEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageDown, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET] } }), 'View: Open Next Editor', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenPreviousEditor, OpenPreviousEditor.ID, OpenPreviousEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET] } }), 'View: Open Previous Editor', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ReopenClosedEditorAction, ReopenClosedEditorAction.ID, ReopenClosedEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), 'View: Reopen Closed Editor', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ClearRecentFilesAction, ClearRecentFilesAction.ID, ClearRecentFilesAction.LABEL), 'File: Clear Recently Opened', nls.localize('file', "File")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_W) }), 'View: Close All Editors', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseAllEditorGroupsAction, CloseAllEditorGroupsAction.ID, CloseAllEditorGroupsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W) }), 'View: Close All Editor Groups', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseLeftEditorsInGroupAction, CloseLeftEditorsInGroupAction.ID, CloseLeftEditorsInGroupAction.LABEL), 'View: Close Editors to the Left in Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseEditorsInOtherGroupsAction, CloseEditorsInOtherGroupsAction.ID, CloseEditorsInOtherGroupsAction.LABEL), 'View: Close Editors in Other Groups', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseEditorInAllGroupsAction, CloseEditorInAllGroupsAction.ID, CloseEditorInAllGroupsAction.LABEL), 'View: Close Editor in All Groups', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH }), 'View: Split Editor', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(SplitEditorOrthogonalAction, SplitEditorOrthogonalAction.ID, SplitEditorOrthogonalAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_BACKSLASH) }), 'View: Split Editor Orthogonal', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(SplitEditorLeftAction, SplitEditorLeftAction.ID, SplitEditorLeftAction.LABEL), 'View: Split Editor Left', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(SplitEditorRightAction, SplitEditorRightAction.ID, SplitEditorRightAction.LABEL), 'View: Split Editor Right', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(SplitEditorUpAction, SplitEditorUpAction.ID, SplitEditorUpAction.LABEL), 'Split Editor Up', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(SplitEditorDownAction, SplitEditorDownAction.ID, SplitEditorDownAction.LABEL), 'View: Split Editor Down', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(JoinTwoGroupsAction, JoinTwoGroupsAction.ID, JoinTwoGroupsAction.LABEL), 'View: Join Editor Group with Next Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(JoinAllGroupsAction, JoinAllGroupsAction.ID, JoinAllGroupsAction.LABEL), 'View: Join All Editor Groups', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateBetweenGroupsAction, NavigateBetweenGroupsAction.ID, NavigateBetweenGroupsAction.LABEL), 'View: Navigate Between Editor Groups', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ResetGroupSizesAction, ResetGroupSizesAction.ID, ResetGroupSizesAction.LABEL), 'View: Reset Editor Group Sizes', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleGroupSizesAction, ToggleGroupSizesAction.ID, ToggleGroupSizesAction.LABEL), 'View: Toggle Editor Group Sizes', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Side Bar', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MinimizeOtherGroupsAction, MinimizeOtherGroupsAction.ID, MinimizeOtherGroupsAction.LABEL), 'View: Maximize Editor Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorLeftInGroupAction, MoveEditorLeftInGroupAction.ID, MoveEditorLeftInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageUp, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow) } }), 'View: Move Editor Left', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorRightInGroupAction, MoveEditorRightInGroupAction.ID, MoveEditorRightInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow) } }), 'View: Move Editor Right', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveGroupLeftAction, MoveGroupLeftAction.ID, MoveGroupLeftAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.LeftArrow) }), 'View: Move Editor Group Left', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveGroupRightAction, MoveGroupRightAction.ID, MoveGroupRightAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.RightArrow) }), 'View: Move Editor Group Right', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveGroupUpAction, MoveGroupUpAction.ID, MoveGroupUpAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.UpArrow) }), 'View: Move Editor Group Up', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveGroupDownAction, MoveGroupDownAction.ID, MoveGroupDownAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.DownArrow) }), 'View: Move Editor Group Down', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToPreviousGroupAction, MoveEditorToPreviousGroupAction.ID, MoveEditorToPreviousGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }), 'View: Move Editor into Previous Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToNextGroupAction, MoveEditorToNextGroupAction.ID, MoveEditorToNextGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }), 'View: Move Editor into Next Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToFirstGroupAction, MoveEditorToFirstGroupAction.ID, MoveEditorToFirstGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_1, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_1 } }), 'View: Move Editor into First Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToLastGroupAction, MoveEditorToLastGroupAction.ID, MoveEditorToLastGroupAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_9, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_9 } }), 'View: Move Editor into Last Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToLeftGroupAction, MoveEditorToLeftGroupAction.ID, MoveEditorToLeftGroupAction.LABEL), 'View: Move Editor into Left Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToRightGroupAction, MoveEditorToRightGroupAction.ID, MoveEditorToRightGroupAction.LABEL), 'View: Move Editor into Right Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToAboveGroupAction, MoveEditorToAboveGroupAction.ID, MoveEditorToAboveGroupAction.LABEL), 'View: Move Editor into Above Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MoveEditorToBelowGroupAction, MoveEditorToBelowGroupAction.ID, MoveEditorToBelowGroupAction.LABEL), 'View: Move Editor into Below Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusActiveGroupAction, FocusActiveGroupAction.ID, FocusActiveGroupAction.LABEL), 'View: Focus Active Editor Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_1 }), 'View: Focus First Editor Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusLastGroupAction, FocusLastGroupAction.ID, FocusLastGroupAction.LABEL), 'View: Focus Last Editor Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusPreviousGroup, FocusPreviousGroup.ID, FocusPreviousGroup.LABEL), 'View: Focus Previous Editor Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusNextGroup, FocusNextGroup.ID, FocusNextGroup.LABEL), 'View: Focus Next Editor Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusLeftGroup, FocusLeftGroup.ID, FocusLeftGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Left Editor Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusRightGroup, FocusRightGroup.ID, FocusRightGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Right Editor Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusAboveGroup, FocusAboveGroup.ID, FocusAboveGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.UpArrow) }), 'View: Focus Above Editor Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusBelowGroup, FocusBelowGroup.ID, FocusBelowGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.DownArrow) }), 'View: Focus Below Editor Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NewEditorGroupLeftAction, NewEditorGroupLeftAction.ID, NewEditorGroupLeftAction.LABEL), 'View: New Editor Group to the Left', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NewEditorGroupRightAction, NewEditorGroupRightAction.ID, NewEditorGroupRightAction.LABEL), 'View: New Editor Group to the Right', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NewEditorGroupAboveAction, NewEditorGroupAboveAction.ID, NewEditorGroupAboveAction.LABEL), 'View: New Editor Group Above', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NewEditorGroupBelowAction, NewEditorGroupBelowAction.ID, NewEditorGroupBelowAction.LABEL), 'View: New Editor Group Below', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateForwardAction, NavigateForwardAction.ID, NavigateForwardAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.RightArrow }, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS } }), 'Go Forward'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateBackwardsAction, NavigateBackwardsAction.ID, NavigateBackwardsAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }), 'Go Back'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateToLastEditLocationAction, NavigateToLastEditLocationAction.ID, NavigateToLastEditLocationAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_Q) }), 'Go to Last Edit Location'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(NavigateLastAction, NavigateLastAction.ID, NavigateLastAction.LABEL), 'Go Last'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenPreviousEditorFromHistoryAction, OpenPreviousEditorFromHistoryAction.ID, OpenPreviousEditorFromHistoryAction.LABEL), 'Open Previous Editor from History'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ClearEditorHistoryAction, ClearEditorHistoryAction.ID, ClearEditorHistoryAction.LABEL), 'Clear Editor History'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(RevertAndCloseEditorAction, RevertAndCloseEditorAction.ID, RevertAndCloseEditorAction.LABEL), 'View: Revert and Close Editor', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutSingleAction, EditorLayoutSingleAction.ID, EditorLayoutSingleAction.LABEL), 'View: Single Column Editor Layout', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoColumnsAction, EditorLayoutTwoColumnsAction.ID, EditorLayoutTwoColumnsAction.LABEL), 'View: Two Columns Editor Layout', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutThreeColumnsAction, EditorLayoutThreeColumnsAction.ID, EditorLayoutThreeColumnsAction.LABEL), 'View: Three Columns Editor Layout', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoRowsAction, EditorLayoutTwoRowsAction.ID, EditorLayoutTwoRowsAction.LABEL), 'View: Two Rows Editor Layout', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutThreeRowsAction, EditorLayoutThreeRowsAction.ID, EditorLayoutThreeRowsAction.LABEL), 'View: Three Rows Editor Layout', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoByTwoGridAction, EditorLayoutTwoByTwoGridAction.ID, EditorLayoutTwoByTwoGridAction.LABEL), 'View: Grid Editor Layout (2x2)', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoRowsRightAction, EditorLayoutTwoRowsRightAction.ID, EditorLayoutTwoRowsRightAction.LABEL), 'View: Two Rows Right Editor Layout', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoColumnsBottomAction.ID, EditorLayoutTwoColumnsBottomAction.LABEL), 'View: Two Columns Bottom Editor Layout', category); // Register Editor Picker Actions including quick navigate support const openNextEditorKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } }; const openPreviousEditorKeybinding = { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab } }; -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction.ID, OpenNextRecentlyUsedEditorInGroupAction.LABEL, openNextEditorKeybinding), 'View: Open Next Recently Used Editor in Group', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditorInGroupAction.ID, OpenPreviousRecentlyUsedEditorInGroupAction.LABEL, openPreviousEditorKeybinding), 'View: Open Previous Recently Used Editor in Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenNextRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction.ID, OpenNextRecentlyUsedEditorInGroupAction.LABEL, openNextEditorKeybinding), 'View: Open Next Recently Used Editor in Group', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenPreviousRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditorInGroupAction.ID, OpenPreviousRecentlyUsedEditorInGroupAction.LABEL, openPreviousEditorKeybinding), 'View: Open Previous Recently Used Editor in Group', category); const quickOpenNavigateNextInEditorPickerId = 'workbench.action.quickOpenNavigateNextInEditorPicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 30de9340067..7c0ba38cc48 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -243,14 +243,14 @@ export class NextPanelViewAction extends SwitchPanelViewAction { } const actionRegistry = Registry.as(WorkbenchExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TogglePanelAction, TogglePanelAction.ID, TogglePanelAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_J }), 'View: Toggle Panel', nls.localize('view', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPanelAction, FocusPanelAction.ID, FocusPanelAction.LABEL), 'View: Focus into Panel', nls.localize('view', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, ToggleMaximizedPanelAction.LABEL), 'View: Toggle Maximized Panel', nls.localize('view', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL), 'View: Close Panel', nls.localize('view', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TogglePanelPositionAction, TogglePanelPositionAction.ID, TogglePanelPositionAction.LABEL), 'View: Toggle Panel Position', nls.localize('view', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, undefined), 'View: Toggle Panel Position', nls.localize('view', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(PreviousPanelViewAction, PreviousPanelViewAction.ID, PreviousPanelViewAction.LABEL), 'View: Previous Panel View', nls.localize('view', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(NextPanelViewAction, NextPanelViewAction.ID, NextPanelViewAction.LABEL), 'View: Next Panel View', nls.localize('view', "View")); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(TogglePanelAction, TogglePanelAction.ID, TogglePanelAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_J }), 'View: Toggle Panel', nls.localize('view', "View")); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FocusPanelAction, FocusPanelAction.ID, FocusPanelAction.LABEL), 'View: Focus into Panel', nls.localize('view', "View")); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, ToggleMaximizedPanelAction.LABEL), 'View: Toggle Maximized Panel', nls.localize('view', "View")); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL), 'View: Close Panel', nls.localize('view', "View")); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(TogglePanelPositionAction, TogglePanelPositionAction.ID, TogglePanelPositionAction.LABEL), 'View: Toggle Panel Position', nls.localize('view', "View")); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, undefined), 'View: Toggle Panel Position', nls.localize('view', "View")); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(PreviousPanelViewAction, PreviousPanelViewAction.ID, PreviousPanelViewAction.LABEL), 'View: Previous Panel View', nls.localize('view', "View")); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(NextPanelViewAction, NextPanelViewAction.ID, NextPanelViewAction.LABEL), 'View: Next Panel View', nls.localize('view', "View")); MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { group: '2_workbench_layout', diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputActions.ts b/src/vs/workbench/browser/parts/quickinput/quickInputActions.ts index 04635f8493c..0e72c0e8576 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputActions.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputActions.ts @@ -14,4 +14,4 @@ import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickop KeybindingsRegistry.registerCommandAndKeybindingRule(QuickPickManyToggle); const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(BackAction, BackAction.ID, BackAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Back'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(BackAction, BackAction.ID, BackAction.LABEL, { primary: 0, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Back'); diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenActions.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenActions.ts index 6ff9d1b3496..0a8187a8759 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenActions.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenActions.ts @@ -69,11 +69,11 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: QUICKOPEN_ACTION_ID, title: { value: QUICKOPEN_ACION_LABEL, original: 'Go to File...' } } }); -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenSelectNextAction, QuickOpenSelectNextAction.ID, QuickOpenSelectNextAction.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_N } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Next in Quick Open'); -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenSelectPreviousAction, QuickOpenSelectPreviousAction.ID, QuickOpenSelectPreviousAction.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_P } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Previous in Quick Open'); -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenNavigateNextAction, QuickOpenNavigateNextAction.ID, QuickOpenNavigateNextAction.LABEL), 'Navigate Next in Quick Open'); -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenNavigatePreviousAction, QuickOpenNavigatePreviousAction.ID, QuickOpenNavigatePreviousAction.LABEL), 'Navigate Previous in Quick Open'); -registry.registerWorkbenchAction(new SyncActionDescriptor(RemoveFromEditorHistoryAction, RemoveFromEditorHistoryAction.ID, RemoveFromEditorHistoryAction.LABEL), 'Remove From History'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenSelectNextAction, QuickOpenSelectNextAction.ID, QuickOpenSelectNextAction.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_N } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Next in Quick Open'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenSelectPreviousAction, QuickOpenSelectPreviousAction.ID, QuickOpenSelectPreviousAction.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_P } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Previous in Quick Open'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenNavigateNextAction, QuickOpenNavigateNextAction.ID, QuickOpenNavigateNextAction.LABEL), 'Navigate Next in Quick Open'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenNavigatePreviousAction, QuickOpenNavigatePreviousAction.ID, QuickOpenNavigatePreviousAction.LABEL), 'Navigate Previous in Quick Open'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(RemoveFromEditorHistoryAction, RemoveFromEditorHistoryAction.ID, RemoveFromEditorHistoryAction.LABEL), 'Remove From History'); const quickOpenNavigateNextInFilePickerId = 'workbench.action.quickOpenNavigateNextInFilePicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -98,4 +98,4 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ primary: globalQuickOpenKeybinding.mac.primary | KeyMod.Shift, secondary: undefined } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 809316c6777..d9667ca6335 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -320,7 +320,7 @@ class FocusSideBarAction extends Action { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusSideBarAction, FocusSideBarAction.ID, FocusSideBarAction.LABEL, { +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusSideBarAction, FocusSideBarAction.ID, FocusSideBarAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_0 }), 'View: Focus into Side Bar', nls.localize('viewCategory', "View")); diff --git a/src/vs/workbench/contrib/cli/node/cli.contribution.ts b/src/vs/workbench/contrib/cli/node/cli.contribution.ts index b0dc1655169..5ef30f885ef 100644 --- a/src/vs/workbench/contrib/cli/node/cli.contribution.ts +++ b/src/vs/workbench/contrib/cli/node/cli.contribution.ts @@ -189,6 +189,6 @@ if (platform.isMacintosh) { const category = nls.localize('shellCommand', "Shell Command"); const workbenchActionsRegistry = Registry.as(ActionExtensions.WorkbenchActions); - workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(InstallAction, InstallAction.ID, InstallAction.LABEL), `Shell Command: Install \'${product.applicationName}\' command in PATH`, category); - workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(UninstallAction, UninstallAction.ID, UninstallAction.LABEL), `Shell Command: Uninstall \'${product.applicationName}\' command from PATH`, category); + workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.create(InstallAction, InstallAction.ID, InstallAction.LABEL), `Shell Command: Install \'${product.applicationName}\' command in PATH`, category); + workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.create(UninstallAction, UninstallAction.ID, UninstallAction.LABEL), `Shell Command: Uninstall \'${product.applicationName}\' command from PATH`, category); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts index 218ba69adf9..396b76f4fcf 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectKeybindings.ts @@ -54,4 +54,4 @@ class InspectKeyMapJSON extends Action { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(InspectKeyMapJSON, InspectKeyMapJSON.ID, InspectKeyMapJSON.LABEL), 'Developer: Inspect Key Mappings (JSON)', nls.localize('developer', "Developer")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(InspectKeyMapJSON, InspectKeyMapJSON.ID, InspectKeyMapJSON.LABEL), 'Developer: Inspect Key Mappings (JSON)', nls.localize('developer', "Developer")); diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts index e1d040feb50..5cf2381d1a2 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleMinimap.ts @@ -30,7 +30,7 @@ export class ToggleMinimapAction extends Action { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMinimapAction, ToggleMinimapAction.ID, ToggleMinimapAction.LABEL), 'View: Toggle Minimap', nls.localize('view', "View")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMinimapAction, ToggleMinimapAction.ID, ToggleMinimapAction.LABEL), 'View: Toggle Minimap', nls.localize('view', "View")); MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { group: '5_editor', diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts index 5e1b6da2163..a41bea88b34 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts @@ -66,7 +66,7 @@ Registry.as(WorkbenchExtensions.Workbench).regi const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMultiCursorModifierAction, ToggleMultiCursorModifierAction.ID, ToggleMultiCursorModifierAction.LABEL), 'Toggle Multi-Cursor Modifier'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMultiCursorModifierAction, ToggleMultiCursorModifierAction.ID, ToggleMultiCursorModifierAction.LABEL), 'Toggle Multi-Cursor Modifier'); MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { group: '3_multi', command: { @@ -88,4 +88,4 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { }, when: multiCursorModifier.isEqualTo('altKey'), order: 1 -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts index e8c72398908..fcf56ad2644 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter.ts @@ -31,7 +31,7 @@ export class ToggleRenderControlCharacterAction extends Action { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRenderControlCharacterAction, ToggleRenderControlCharacterAction.ID, ToggleRenderControlCharacterAction.LABEL), 'View: Toggle Control Characters', nls.localize('view', "View")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleRenderControlCharacterAction, ToggleRenderControlCharacterAction.ID, ToggleRenderControlCharacterAction.LABEL), 'View: Toggle Control Characters', nls.localize('view', "View")); MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { group: '5_editor', diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts index d2a6b4845d8..56aa45bc248 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace.ts @@ -39,7 +39,7 @@ export class ToggleRenderWhitespaceAction extends Action { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRenderWhitespaceAction, ToggleRenderWhitespaceAction.ID, ToggleRenderWhitespaceAction.LABEL), 'View: Toggle Render Whitespace', nls.localize('view', "View")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleRenderWhitespaceAction, ToggleRenderWhitespaceAction.ID, ToggleRenderWhitespaceAction.LABEL), 'View: Toggle Render Whitespace', nls.localize('view', "View")); MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { group: '5_editor', diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 3c68e97f711..ad44fc85547 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -117,8 +117,8 @@ registerCommands(); // register action to open viewlet const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugPanelAction, OpenDebugPanelAction.ID, OpenDebugPanelAction.LABEL, openPanelKb), 'View: Debug Console', nls.localize('view', "View")); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugViewletAction, OpenDebugViewletAction.ID, OpenDebugViewletAction.LABEL, openViewletKb), 'View: Show Debug', nls.localize('view', "View")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenDebugPanelAction, OpenDebugPanelAction.ID, OpenDebugPanelAction.LABEL, openPanelKb), 'View: Debug Console', nls.localize('view', "View")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenDebugViewletAction, OpenDebugViewletAction.ID, OpenDebugViewletAction.LABEL, openViewletKb), 'View: Show Debug', nls.localize('view', "View")); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugCallStackContribution, LifecyclePhase.Restored); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, LifecyclePhase.Restored); @@ -127,16 +127,16 @@ Registry.as(WorkbenchExtensions.Workbench).regi const debugCategory = nls.localize('debugCategory', "Debug"); -registry.registerWorkbenchAction(new SyncActionDescriptor(StartAction, StartAction.ID, StartAction.LABEL, { primary: KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE.toNegated()), 'Debug: Start Debugging', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureAction, ConfigureAction.ID, ConfigureAction.LABEL), 'Debug: Open launch.json', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(AddFunctionBreakpointAction, AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL), 'Debug: Add Function Breakpoint', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(ReapplyBreakpointsAction, ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL), 'Debug: Reapply All Breakpoints', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(RunAction, RunAction.ID, RunAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.F5, mac: { primary: KeyMod.WinCtrl | KeyCode.F5 } }), 'Debug: Start Without Debugging', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL), 'Debug: Remove All Breakpoints', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(EnableAllBreakpointsAction, EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL), 'Debug: Enable All Breakpoints', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(DisableAllBreakpointsAction, DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL), 'Debug: Disable All Breakpoints', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(SelectAndStartAction, SelectAndStartAction.ID, SelectAndStartAction.LABEL), 'Debug: Select and Start Debugging', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL), 'Debug: Clear Console', debugCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(StartAction, StartAction.ID, StartAction.LABEL, { primary: KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE.toNegated()), 'Debug: Start Debugging', debugCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ConfigureAction, ConfigureAction.ID, ConfigureAction.LABEL), 'Debug: Open launch.json', debugCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(AddFunctionBreakpointAction, AddFunctionBreakpointAction.ID, AddFunctionBreakpointAction.LABEL), 'Debug: Add Function Breakpoint', debugCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ReapplyBreakpointsAction, ReapplyBreakpointsAction.ID, ReapplyBreakpointsAction.LABEL), 'Debug: Reapply All Breakpoints', debugCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(RunAction, RunAction.ID, RunAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.F5, mac: { primary: KeyMod.WinCtrl | KeyCode.F5 } }), 'Debug: Start Without Debugging', debugCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(RemoveAllBreakpointsAction, RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL), 'Debug: Remove All Breakpoints', debugCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(EnableAllBreakpointsAction, EnableAllBreakpointsAction.ID, EnableAllBreakpointsAction.LABEL), 'Debug: Enable All Breakpoints', debugCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(DisableAllBreakpointsAction, DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL), 'Debug: Disable All Breakpoints', debugCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(SelectAndStartAction, SelectAndStartAction.ID, SelectAndStartAction.LABEL), 'Debug: Select and Start Debugging', debugCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL), 'Debug: Clear Console', debugCategory); const registerDebugCommandPaletteItem = (id: string, title: string, when?: ContextKeyExpr, precondition?: ContextKeyExpr) => { MenuRegistry.appendMenuItem(MenuId.CommandPalette, { diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index b75b365641b..acab3d418c7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -90,67 +90,67 @@ Registry.as(ViewletExtensions.Viewlets) // Global actions const actionRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); -const openViewletActionDescriptor = new SyncActionDescriptor(OpenExtensionsViewletAction, OpenExtensionsViewletAction.ID, OpenExtensionsViewletAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_X }); +const openViewletActionDescriptor = SyncActionDescriptor.create(OpenExtensionsViewletAction, OpenExtensionsViewletAction.ID, OpenExtensionsViewletAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_X }); actionRegistry.registerWorkbenchAction(openViewletActionDescriptor, 'View: Show Extensions', localize('view', "View")); -const installActionDescriptor = new SyncActionDescriptor(InstallExtensionsAction, InstallExtensionsAction.ID, InstallExtensionsAction.LABEL); +const installActionDescriptor = SyncActionDescriptor.create(InstallExtensionsAction, InstallExtensionsAction.ID, InstallExtensionsAction.LABEL); actionRegistry.registerWorkbenchAction(installActionDescriptor, 'Extensions: Install Extensions', ExtensionsLabel); -const listOutdatedActionDescriptor = new SyncActionDescriptor(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL); +const listOutdatedActionDescriptor = SyncActionDescriptor.create(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL); actionRegistry.registerWorkbenchAction(listOutdatedActionDescriptor, 'Extensions: Show Outdated Extensions', ExtensionsLabel); -const recommendationsActionDescriptor = new SyncActionDescriptor(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL); +const recommendationsActionDescriptor = SyncActionDescriptor.create(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL); actionRegistry.registerWorkbenchAction(recommendationsActionDescriptor, 'Extensions: Show Recommended Extensions', ExtensionsLabel); -const keymapRecommendationsActionDescriptor = new SyncActionDescriptor(ShowRecommendedKeymapExtensionsAction, ShowRecommendedKeymapExtensionsAction.ID, ShowRecommendedKeymapExtensionsAction.SHORT_LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_M) }); +const keymapRecommendationsActionDescriptor = SyncActionDescriptor.create(ShowRecommendedKeymapExtensionsAction, ShowRecommendedKeymapExtensionsAction.ID, ShowRecommendedKeymapExtensionsAction.SHORT_LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_M) }); actionRegistry.registerWorkbenchAction(keymapRecommendationsActionDescriptor, 'Preferences: Keymaps', PreferencesLabel); -const languageExtensionsActionDescriptor = new SyncActionDescriptor(ShowLanguageExtensionsAction, ShowLanguageExtensionsAction.ID, ShowLanguageExtensionsAction.SHORT_LABEL); +const languageExtensionsActionDescriptor = SyncActionDescriptor.create(ShowLanguageExtensionsAction, ShowLanguageExtensionsAction.ID, ShowLanguageExtensionsAction.SHORT_LABEL); actionRegistry.registerWorkbenchAction(languageExtensionsActionDescriptor, 'Preferences: Language Extensions', PreferencesLabel); -const azureExtensionsActionDescriptor = new SyncActionDescriptor(ShowAzureExtensionsAction, ShowAzureExtensionsAction.ID, ShowAzureExtensionsAction.SHORT_LABEL); +const azureExtensionsActionDescriptor = SyncActionDescriptor.create(ShowAzureExtensionsAction, ShowAzureExtensionsAction.ID, ShowAzureExtensionsAction.SHORT_LABEL); actionRegistry.registerWorkbenchAction(azureExtensionsActionDescriptor, 'Preferences: Azure Extensions', PreferencesLabel); -const popularActionDescriptor = new SyncActionDescriptor(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL); +const popularActionDescriptor = SyncActionDescriptor.create(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL); actionRegistry.registerWorkbenchAction(popularActionDescriptor, 'Extensions: Show Popular Extensions', ExtensionsLabel); -const enabledActionDescriptor = new SyncActionDescriptor(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, ShowEnabledExtensionsAction.LABEL); +const enabledActionDescriptor = SyncActionDescriptor.create(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, ShowEnabledExtensionsAction.LABEL); actionRegistry.registerWorkbenchAction(enabledActionDescriptor, 'Extensions: Show Enabled Extensions', ExtensionsLabel); -const installedActionDescriptor = new SyncActionDescriptor(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL); +const installedActionDescriptor = SyncActionDescriptor.create(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL); actionRegistry.registerWorkbenchAction(installedActionDescriptor, 'Extensions: Show Installed Extensions', ExtensionsLabel); -const disabledActionDescriptor = new SyncActionDescriptor(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, ShowDisabledExtensionsAction.LABEL); +const disabledActionDescriptor = SyncActionDescriptor.create(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, ShowDisabledExtensionsAction.LABEL); actionRegistry.registerWorkbenchAction(disabledActionDescriptor, 'Extensions: Show Disabled Extensions', ExtensionsLabel); -const builtinActionDescriptor = new SyncActionDescriptor(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, ShowBuiltInExtensionsAction.LABEL); +const builtinActionDescriptor = SyncActionDescriptor.create(ShowBuiltInExtensionsAction, ShowBuiltInExtensionsAction.ID, ShowBuiltInExtensionsAction.LABEL); actionRegistry.registerWorkbenchAction(builtinActionDescriptor, 'Extensions: Show Built-in Extensions', ExtensionsLabel); -const updateAllActionDescriptor = new SyncActionDescriptor(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL); +const updateAllActionDescriptor = SyncActionDescriptor.create(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL); actionRegistry.registerWorkbenchAction(updateAllActionDescriptor, 'Extensions: Update All Extensions', ExtensionsLabel); -const installVSIXActionDescriptor = new SyncActionDescriptor(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); +const installVSIXActionDescriptor = SyncActionDescriptor.create(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); actionRegistry.registerWorkbenchAction(installVSIXActionDescriptor, 'Extensions: Install from VSIX...', ExtensionsLabel); -const disableAllAction = new SyncActionDescriptor(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL); +const disableAllAction = SyncActionDescriptor.create(DisableAllAction, DisableAllAction.ID, DisableAllAction.LABEL); actionRegistry.registerWorkbenchAction(disableAllAction, 'Extensions: Disable All Installed Extensions', ExtensionsLabel); -const disableAllWorkspaceAction = new SyncActionDescriptor(DisableAllWorkspaceAction, DisableAllWorkspaceAction.ID, DisableAllWorkspaceAction.LABEL); +const disableAllWorkspaceAction = SyncActionDescriptor.create(DisableAllWorkspaceAction, DisableAllWorkspaceAction.ID, DisableAllWorkspaceAction.LABEL); actionRegistry.registerWorkbenchAction(disableAllWorkspaceAction, 'Extensions: Disable All Installed Extensions for this Workspace', ExtensionsLabel); -const enableAllAction = new SyncActionDescriptor(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL); +const enableAllAction = SyncActionDescriptor.create(EnableAllAction, EnableAllAction.ID, EnableAllAction.LABEL); actionRegistry.registerWorkbenchAction(enableAllAction, 'Extensions: Enable All Extensions', ExtensionsLabel); -const enableAllWorkspaceAction = new SyncActionDescriptor(EnableAllWorkspaceAction, EnableAllWorkspaceAction.ID, EnableAllWorkspaceAction.LABEL); +const enableAllWorkspaceAction = SyncActionDescriptor.create(EnableAllWorkspaceAction, EnableAllWorkspaceAction.ID, EnableAllWorkspaceAction.LABEL); actionRegistry.registerWorkbenchAction(enableAllWorkspaceAction, 'Extensions: Enable All Extensions for this Workspace', ExtensionsLabel); -const checkForUpdatesAction = new SyncActionDescriptor(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL); +const checkForUpdatesAction = SyncActionDescriptor.create(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL); actionRegistry.registerWorkbenchAction(checkForUpdatesAction, `Extensions: Check for Extension Updates`, ExtensionsLabel); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL), `Extensions: Enable Auto Updating Extensions`, ExtensionsLabel); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL), `Extensions: Disable Auto Updating Extensions`, ExtensionsLabel); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(InstallSpecificVersionOfExtensionAction, InstallSpecificVersionOfExtensionAction.ID, InstallSpecificVersionOfExtensionAction.LABEL), 'Install Specific Version of Extension...', ExtensionsLabel); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReinstallAction, ReinstallAction.ID, ReinstallAction.LABEL), 'Reinstall Extension...', localize('developer', "Developer")); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL), `Extensions: Enable Auto Updating Extensions`, ExtensionsLabel); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL), `Extensions: Disable Auto Updating Extensions`, ExtensionsLabel); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(InstallSpecificVersionOfExtensionAction, InstallSpecificVersionOfExtensionAction.ID, InstallSpecificVersionOfExtensionAction.LABEL), 'Install Specific Version of Extension...', ExtensionsLabel); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ReinstallAction, ReinstallAction.ID, ReinstallAction.LABEL), 'Reinstall Extension...', localize('developer', "Developer")); Registry.as(ConfigurationExtensions.Configuration) .registerConfiguration({ 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 19e5153bb53..4b5d7b3f7e3 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -58,7 +58,7 @@ Registry.as(EditorInputExtensions.EditorInputFactor // Global actions const actionRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowRuntimeExtensionsAction, ShowRuntimeExtensionsAction.ID, ShowRuntimeExtensionsAction.LABEL), 'Show Running Extensions', localize('developer', "Developer")); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ShowRuntimeExtensionsAction, ShowRuntimeExtensionsAction.ID, ShowRuntimeExtensionsAction.LABEL), 'Show Running Extensions', localize('developer', "Developer")); class ExtensionsContributions implements IWorkbenchContribution { @@ -66,7 +66,7 @@ class ExtensionsContributions implements IWorkbenchContribution { @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService ) { if (workbenchEnvironmentService.extensionsPath) { - const openExtensionsFolderActionDescriptor = new SyncActionDescriptor(OpenExtensionsFolderAction, OpenExtensionsFolderAction.ID, OpenExtensionsFolderAction.LABEL); + const openExtensionsFolderActionDescriptor = SyncActionDescriptor.create(OpenExtensionsFolderAction, OpenExtensionsFolderAction.ID, OpenExtensionsFolderAction.LABEL); actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Extensions: Open Extensions Folder', ExtensionsLabel); } } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 54c80ed2158..57456d500ea 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -34,26 +34,26 @@ import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; const category = { value: nls.localize('filesCategory', "File"), original: 'File' }; const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(SaveAllAction, SaveAllAction.ID, SaveAllAction.LABEL, { primary: undefined, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_S }, win: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_S) } }), 'File: Save All', category.value); -registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalCompareResourcesAction, GlobalCompareResourcesAction.ID, GlobalCompareResourcesAction.LABEL), 'File: Compare Active File With...', category.value); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusFilesExplorer, FocusFilesExplorer.ID, FocusFilesExplorer.LABEL), 'File: Focus on Files Explorer', category.value); -registry.registerWorkbenchAction(new SyncActionDescriptor(ShowActiveFileInExplorer, ShowActiveFileInExplorer.ID, ShowActiveFileInExplorer.LABEL), 'File: Reveal Active File in Side Bar', category.value); -registry.registerWorkbenchAction(new SyncActionDescriptor(CollapseExplorerView, CollapseExplorerView.ID, CollapseExplorerView.LABEL), 'File: Collapse Folders in Explorer', category.value); -registry.registerWorkbenchAction(new SyncActionDescriptor(RefreshExplorerView, RefreshExplorerView.ID, RefreshExplorerView.LABEL), 'File: Refresh Explorer', category.value); -registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalNewUntitledFileAction, GlobalNewUntitledFileAction.ID, GlobalNewUntitledFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_N }), 'File: New Untitled File', category.value); -registry.registerWorkbenchAction(new SyncActionDescriptor(CompareWithClipboardAction, CompareWithClipboardAction.ID, CompareWithClipboardAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_C) }), 'File: Compare Active File with Clipboard', category.value); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleAutoSaveAction, ToggleAutoSaveAction.ID, ToggleAutoSaveAction.LABEL), 'File: Toggle Auto Save', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.create(SaveAllAction, SaveAllAction.ID, SaveAllAction.LABEL, { primary: undefined, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_S }, win: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_S) } }), 'File: Save All', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.create(GlobalCompareResourcesAction, GlobalCompareResourcesAction.ID, GlobalCompareResourcesAction.LABEL), 'File: Compare Active File With...', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusFilesExplorer, FocusFilesExplorer.ID, FocusFilesExplorer.LABEL), 'File: Focus on Files Explorer', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowActiveFileInExplorer, ShowActiveFileInExplorer.ID, ShowActiveFileInExplorer.LABEL), 'File: Reveal Active File in Side Bar', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.create(CollapseExplorerView, CollapseExplorerView.ID, CollapseExplorerView.LABEL), 'File: Collapse Folders in Explorer', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.create(RefreshExplorerView, RefreshExplorerView.ID, RefreshExplorerView.LABEL), 'File: Refresh Explorer', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.create(GlobalNewUntitledFileAction, GlobalNewUntitledFileAction.ID, GlobalNewUntitledFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_N }), 'File: New Untitled File', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.create(CompareWithClipboardAction, CompareWithClipboardAction.ID, CompareWithClipboardAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_C) }), 'File: Compare Active File with Clipboard', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleAutoSaveAction, ToggleAutoSaveAction.ID, ToggleAutoSaveAction.LABEL), 'File: Toggle Auto Save', category.value); const workspacesCategory = nls.localize('workspaces', "Workspaces"); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory); const fileCategory = nls.localize('file', "File"); if (isMacintosh) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open...', fileCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open...', fileCategory); } else { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open File...', fileCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'File: Open Folder...', fileCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open File...', fileCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'File: Open Folder...', fileCategory); } // Commands diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index f2332dd7eb5..5633cb728fd 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -94,7 +94,7 @@ const openViewletKb: IKeybindings = { // Register Action to Open Viewlet const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction( - new SyncActionDescriptor(OpenExplorerViewletAction, OpenExplorerViewletAction.ID, OpenExplorerViewletAction.LABEL, openViewletKb), + SyncActionDescriptor.create(OpenExplorerViewletAction, OpenExplorerViewletAction.ID, OpenExplorerViewletAction.LABEL, openViewletKb), 'View: Show Explorer', nls.localize('view', "View") ); diff --git a/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts index dd9caef709a..9c63d009f8d 100644 --- a/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts @@ -86,4 +86,4 @@ const category = { value: nls.localize('filesCategory', "File"), original: 'File appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category); const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ShowOpenedFileInNewWindow, ShowOpenedFileInNewWindow.ID, ShowOpenedFileInNewWindow.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_O) }), 'File: Open Active File in New Window', category.value); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowOpenedFileInNewWindow, ShowOpenedFileInNewWindow.ID, ShowOpenedFileInNewWindow.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_O) }), 'File: Open Active File in New Window', category.value); 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 c2ac341e305..b9316bb1271 100644 --- a/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts +++ b/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts @@ -19,7 +19,7 @@ const helpCategory = { value: nls.localize('help', "Help"), original: 'Help' }; const workbenchActionsRegistry = Registry.as(Extensions.WorkbenchActions); if (!!product.reportIssueUrl) { - workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReportPerformanceIssueUsingReporterAction, ReportPerformanceIssueUsingReporterAction.ID, ReportPerformanceIssueUsingReporterAction.LABEL), 'Help: Report Performance Issue', helpCategory.value); + workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ReportPerformanceIssueUsingReporterAction, ReportPerformanceIssueUsingReporterAction.ID, ReportPerformanceIssueUsingReporterAction.LABEL), 'Help: Report Performance Issue', helpCategory.value); const OpenIssueReporterActionId = 'workbench.action.openIssueReporter'; const OpenIssueReporterActionLabel = nls.localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue"); @@ -43,10 +43,10 @@ if (!!product.reportIssueUrl) { } const developerCategory = nls.localize('developer', "Developer"); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenProcessExplorer, OpenProcessExplorer.ID, OpenProcessExplorer.LABEL), 'Developer: Open Process Explorer', developerCategory); +workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.create(OpenProcessExplorer, OpenProcessExplorer.ID, OpenProcessExplorer.LABEL), 'Developer: Open Process Explorer', developerCategory); registerSingleton(IWorkbenchIssueService, WorkbenchIssueService, true); CommandsRegistry.registerCommand('_issues.getSystemStatus', (accessor) => { return accessor.get(IIssueService).getSystemStatus(); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts index 06b20c612ce..798914273eb 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts @@ -29,7 +29,7 @@ import { ExtensionType } from 'vs/platform/extensions/common/extensions'; // Register action to configure locale and related settings const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureLocaleAction, ConfigureLocaleAction.ID, ConfigureLocaleAction.LABEL), 'Configure Display Language'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ConfigureLocaleAction, ConfigureLocaleAction.ID, ConfigureLocaleAction.LABEL), 'Configure Display Language'); export class LocalizationWorkbenchContribution extends Disposable implements IWorkbenchContribution { constructor( diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index a85738f3351..0479108f91c 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -26,7 +26,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; 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); +workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.create(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.LABEL), 'Developer: Set Log Level...', devCategory); class LogOutputChannels extends Disposable implements IWorkbenchContribution { @@ -58,7 +58,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); const devCategory = nls.localize('developer', "Developer"); - workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenWindowSessionLogFileAction, OpenWindowSessionLogFileAction.ID, OpenWindowSessionLogFileAction.LABEL), 'Developer: Open Window Log File (Session)...', devCategory); + workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.create(OpenWindowSessionLogFileAction, OpenWindowSessionLogFileAction.ID, OpenWindowSessionLogFileAction.LABEL), 'Developer: Open Window Log File (Session)...', devCategory); } private registerNativeContributions(): void { diff --git a/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts b/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts index f6ceef58f71..5aa0badb11e 100644 --- a/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/electron-browser/logs.contribution.ts @@ -11,4 +11,4 @@ import { OpenLogsFolderAction } from 'vs/workbench/contrib/logs/electron-browser 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); +workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.create(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory); diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index 57864213427..d28b9d0d716 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -107,10 +107,10 @@ workbenchRegistry.registerWorkbenchContribution(ActivityUpdater, LifecyclePhase. // actions const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL, { +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_M }), 'View: Toggle Problems (Errors, Warnings, Infos)', Messages.MARKERS_PANEL_VIEW_CATEGORY); -registry.registerWorkbenchAction(new SyncActionDescriptor(ShowProblemsPanelAction, ShowProblemsPanelAction.ID, ShowProblemsPanelAction.LABEL), 'View: Focus Problems (Errors, Warnings, Infos)', Messages.MARKERS_PANEL_VIEW_CATEGORY); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowProblemsPanelAction, ShowProblemsPanelAction.ID, ShowProblemsPanelAction.LABEL), 'View: Focus Problems (Errors, Warnings, Infos)', Messages.MARKERS_PANEL_VIEW_CATEGORY); registerAction({ id: Constants.MARKER_COPY_ACTION_ID, title: { value: localize('copyMarker', "Copy"), original: 'Copy' }, diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index 273ac9f397d..b5032f1eacc 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -74,19 +74,19 @@ Registry.as(WorkbenchExtensions.Workbench).regi // register toggle output action globally const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleOutputAction, ToggleOutputAction.ID, ToggleOutputAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleOutputAction, ToggleOutputAction.ID, ToggleOutputAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_U, linux: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_H) // On Ubuntu Ctrl+Shift+U is taken by some global OS command } }), 'View: Toggle Output', nls.localize('viewCategory', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearOutputAction, ClearOutputAction.ID, ClearOutputAction.LABEL), +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ClearOutputAction, ClearOutputAction.ID, ClearOutputAction.LABEL), 'View: Clear Output', nls.localize('viewCategory', "View")); const devCategory = nls.localize('developer', "Developer"); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowLogsOutputChannelAction, ShowLogsOutputChannelAction.ID, ShowLogsOutputChannelAction.LABEL), 'Developer: Show Logs...', devCategory); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenOutputLogFileAction, OpenOutputLogFileAction.ID, OpenOutputLogFileAction.LABEL), 'Developer: Open Log File...', devCategory); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ShowLogsOutputChannelAction, ShowLogsOutputChannelAction.ID, ShowLogsOutputChannelAction.LABEL), 'Developer: Show Logs...', devCategory); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(OpenOutputLogFileAction, OpenOutputLogFileAction.ID, OpenOutputLogFileAction.LABEL), 'Developer: Open Log File...', devCategory); // Define clear command, contribute to editor context menu registerAction({ diff --git a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts index b076def7c83..77136d27d2f 100644 --- a/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts +++ b/src/vs/workbench/contrib/preferences/browser/keyboardLayoutPicker.ts @@ -176,4 +176,4 @@ export class KeyboardLayoutPickerAction extends Action { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(KeyboardLayoutPickerAction, KeyboardLayoutPickerAction.ID, KeyboardLayoutPickerAction.LABEL, {}), 'Preferences: Change Keyboard Layout', nls.localize('preferences', "Preferences")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(KeyboardLayoutPickerAction, KeyboardLayoutPickerAction.ID, KeyboardLayoutPickerAction.LABEL, {}), 'Preferences: Change Keyboard Layout', nls.localize('preferences', "Preferences")); diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 36d2c63d1b0..4580b8d23ef 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -199,15 +199,15 @@ Registry.as(EditorInputExtensions.EditorInputFactor // Contribute Global Actions const category = nls.localize('preferences', "Preferences"); const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRawDefaultSettingsAction, OpenRawDefaultSettingsAction.ID, OpenRawDefaultSettingsAction.LABEL), 'Preferences: Open Default Settings (JSON)', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSettingsJsonAction, OpenSettingsJsonAction.ID, OpenSettingsJsonAction.LABEL), 'Preferences: Open Settings (JSON)', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSettings2Action, OpenSettings2Action.ID, OpenSettings2Action.LABEL), 'Preferences: Open Settings (UI)', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL), 'Preferences: Open User Settings', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenRawDefaultSettingsAction, OpenRawDefaultSettingsAction.ID, OpenRawDefaultSettingsAction.LABEL), 'Preferences: Open Default Settings (JSON)', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenSettingsJsonAction, OpenSettingsJsonAction.ID, OpenSettingsJsonAction.LABEL), 'Preferences: Open Settings (JSON)', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenSettings2Action, OpenSettings2Action.ID, OpenSettings2Action.LABEL), 'Preferences: Open Settings (UI)', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL), 'Preferences: Open User Settings', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsAction, OpenGlobalKeybindingsAction.ID, OpenGlobalKeybindingsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_S) }), 'Preferences: Open Keyboard Shortcuts', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDefaultKeybindingsFileAction, OpenDefaultKeybindingsFileAction.ID, OpenDefaultKeybindingsFileAction.LABEL), 'Preferences: Open Default Keyboard Shortcuts (JSON)', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsFileAction, OpenGlobalKeybindingsFileAction.ID, OpenGlobalKeybindingsFileAction.LABEL, { primary: 0 }), 'Preferences: Open Keyboard Shortcuts (JSON)', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureLanguageBasedSettingsAction, ConfigureLanguageBasedSettingsAction.ID, ConfigureLanguageBasedSettingsAction.LABEL), 'Preferences: Configure Language Specific Settings...', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenGlobalKeybindingsAction, OpenGlobalKeybindingsAction.ID, OpenGlobalKeybindingsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_S) }), 'Preferences: Open Keyboard Shortcuts', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenDefaultKeybindingsFileAction, OpenDefaultKeybindingsFileAction.ID, OpenDefaultKeybindingsFileAction.LABEL), 'Preferences: Open Default Keyboard Shortcuts (JSON)', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenGlobalKeybindingsFileAction, OpenGlobalKeybindingsFileAction.ID, OpenGlobalKeybindingsFileAction.LABEL, { primary: 0 }), 'Preferences: Open Keyboard Shortcuts (JSON)', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ConfigureLanguageBasedSettingsAction, ConfigureLanguageBasedSettingsAction.ID, ConfigureLanguageBasedSettingsAction.LABEL), 'Preferences: Configure Language Specific Settings...', category); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: SETTINGS_COMMAND_OPEN_SETTINGS, diff --git a/src/vs/workbench/contrib/quickopen/browser/quickopen.contribution.ts b/src/vs/workbench/contrib/quickopen/browser/quickopen.contribution.ts index 98a93b09c37..87639a7409a 100644 --- a/src/vs/workbench/contrib/quickopen/browser/quickopen.contribution.ts +++ b/src/vs/workbench/contrib/quickopen/browser/quickopen.contribution.ts @@ -21,18 +21,18 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co // Register Actions const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ClearCommandHistoryAction, ClearCommandHistoryAction.ID, ClearCommandHistoryAction.LABEL), 'Clear Command History'); -registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllCommandsAction, ShowAllCommandsAction.ID, ShowAllCommandsAction.LABEL, { +registry.registerWorkbenchAction(SyncActionDescriptor.create(ClearCommandHistoryAction, ClearCommandHistoryAction.ID, ClearCommandHistoryAction.LABEL), 'Clear Command History'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowAllCommandsAction, ShowAllCommandsAction.ID, ShowAllCommandsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_P, secondary: [KeyCode.F1] }), 'Show All Commands'); -registry.registerWorkbenchAction(new SyncActionDescriptor(GotoLineAction, GotoLineAction.ID, GotoLineAction.LABEL, { +registry.registerWorkbenchAction(SyncActionDescriptor.create(GotoLineAction, GotoLineAction.ID, GotoLineAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G } }), 'Go to Line...'); -registry.registerWorkbenchAction(new SyncActionDescriptor(GotoSymbolAction, GotoSymbolAction.ID, GotoSymbolAction.LABEL, { +registry.registerWorkbenchAction(SyncActionDescriptor.create(GotoSymbolAction, GotoSymbolAction.ID, GotoSymbolAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O }), 'Go to Symbol in File...'); @@ -42,8 +42,8 @@ const inViewsPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyEx const viewPickerKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.KEY_Q, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_Q }, linux: { primary: 0 } }; const viewCategory = nls.localize('view', "View"); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenViewPickerAction, OpenViewPickerAction.ID, OpenViewPickerAction.LABEL), 'View: Open View', viewCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenViewPickerAction, QuickOpenViewPickerAction.ID, QuickOpenViewPickerAction.LABEL, viewPickerKeybinding), 'View: Quick Open View', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenViewPickerAction, OpenViewPickerAction.ID, OpenViewPickerAction.LABEL), 'View: Open View', viewCategory); +registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenViewPickerAction, QuickOpenViewPickerAction.ID, QuickOpenViewPickerAction.LABEL, viewPickerKeybinding), 'View: Quick Open View', viewCategory); const quickOpenNavigateNextInViewPickerId = 'workbench.action.quickOpenNavigateNextInViewPicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 5901752fe70..6608c979466 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -294,7 +294,7 @@ class OpenRemoteViewletAction extends ShowViewletAction { // Register Action to Open Viewlet Registry.as(WorkbenchActionExtensions.WorkbenchActions).registerWorkbenchAction( - new SyncActionDescriptor(OpenRemoteViewletAction, VIEWLET_ID, nls.localize('toggleRemoteViewlet', "Show Remote Explorer"), { + SyncActionDescriptor.create(OpenRemoteViewletAction, VIEWLET_ID, nls.localize('toggleRemoteViewlet', "Show Remote Explorer"), { primary: 0 }), 'View: Show Remote Explorer', diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 8f269704ca0..ce51e05887c 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -51,7 +51,7 @@ Registry.as(WorkbenchExtensions.Workbench) // Register Action to Open Viewlet Registry.as(WorkbenchActionExtensions.WorkbenchActions).registerWorkbenchAction( - new SyncActionDescriptor(OpenSCMViewletAction, VIEWLET_ID, localize('toggleSCMViewlet', "Show SCM"), { + SyncActionDescriptor.create(OpenSCMViewletAction, VIEWLET_ID, localize('toggleSCMViewlet', "Show SCM"), { primary: 0, win: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G }, diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 7343939e042..07e88ed2034 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -564,7 +564,7 @@ const registry = Registry.as(ActionExtensions.Workbenc // Show Search and Find in Files are redundant, but we can't break keybindings by removing one. So it's the same action, same keybinding, registered to different IDs. // Show Search 'when' is redundant but if the two conflict with exactly the same keybinding and 'when' clause, then they can show up as "unbound" - #51780 -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSearchViewletAction, VIEWLET_ID, OpenSearchViewletAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, Constants.SearchViewVisibleKey.toNegated()), 'View: Show Search', nls.localize('view', "View")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenSearchViewletAction, VIEWLET_ID, OpenSearchViewletAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, Constants.SearchViewVisibleKey.toNegated()), 'View: Show Search', nls.localize('view', "View")); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.FindInFilesActionId, weight: KeybindingWeight.WorkbenchContrib, @@ -582,10 +582,10 @@ MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { order: 1 }); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextSearchResultAction, FocusNextSearchResultAction.ID, FocusNextSearchResultAction.LABEL, { primary: KeyCode.F4 }, ContextKeyExpr.and(Constants.HasSearchResults)), 'Focus Next Search Result', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousSearchResultAction, FocusPreviousSearchResultAction.ID, FocusPreviousSearchResultAction.LABEL, { primary: KeyMod.Shift | KeyCode.F4 }, ContextKeyExpr.and(Constants.HasSearchResults)), 'Focus Previous Search Result', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusNextSearchResultAction, FocusNextSearchResultAction.ID, FocusNextSearchResultAction.LABEL, { primary: KeyCode.F4 }, ContextKeyExpr.and(Constants.HasSearchResults)), 'Focus Next Search Result', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(FocusPreviousSearchResultAction, FocusPreviousSearchResultAction.ID, FocusPreviousSearchResultAction.LABEL, { primary: KeyMod.Shift | KeyCode.F4 }, ContextKeyExpr.and(Constants.HasSearchResults)), 'Focus Previous Search Result', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ReplaceInFilesAction, ReplaceInFilesAction.ID, ReplaceInFilesAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_H }), 'Replace in Files', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ReplaceInFilesAction, ReplaceInFilesAction.ID, ReplaceInFilesAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_H }), 'Replace in Files', category); MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { group: '4_find_global', command: { @@ -616,12 +616,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({ handler: toggleRegexCommand }, ToggleRegexKeybinding)); -registry.registerWorkbenchAction(new SyncActionDescriptor(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL), 'Search: Collapse All', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllSymbolsAction, ShowAllSymbolsAction.ID, ShowAllSymbolsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_T }), 'Go to Symbol in Workspace...'); +registry.registerWorkbenchAction(SyncActionDescriptor.create(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL), 'Search: Collapse All', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowAllSymbolsAction, ShowAllSymbolsAction.ID, ShowAllSymbolsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_T }), 'Go to Symbol in Workspace...'); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSearchOnTypeAction, ToggleSearchOnTypeAction.ID, ToggleSearchOnTypeAction.LABEL), 'Search: Toggle Search on Type', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(RefreshAction, RefreshAction.ID, RefreshAction.LABEL), 'Search: Refresh', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ClearSearchResultsAction, ClearSearchResultsAction.ID, ClearSearchResultsAction.LABEL), 'Search: Clear Search Results', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleSearchOnTypeAction, ToggleSearchOnTypeAction.ID, ToggleSearchOnTypeAction.LABEL), 'Search: Toggle Search on Type', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(RefreshAction, RefreshAction.ID, RefreshAction.LABEL), 'Search: Refresh', category); +registry.registerWorkbenchAction(SyncActionDescriptor.create(ClearSearchResultsAction, ClearSearchResultsAction.ID, ClearSearchResultsAction.LABEL), 'Search: Clear Search Results', category); // Register Quick Open Handler diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index ab806797f1e..9e352e4b9ce 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -45,7 +45,7 @@ const workbenchRegistry = Registry.as(Workbench workbenchRegistry.registerWorkbenchContribution(RunAutomaticTasks, LifecyclePhase.Eventually); const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ManageAutomaticTaskRunning, ManageAutomaticTaskRunning.ID, ManageAutomaticTaskRunning.LABEL), 'Tasks: Manage Automatic Tasks in Folder', tasksCategory); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ManageAutomaticTaskRunning, ManageAutomaticTaskRunning.ID, ManageAutomaticTaskRunning.LABEL), 'Tasks: Manage Automatic Tasks in Folder', tasksCategory); export class TaskStatusBarContributions extends Disposable implements IWorkbenchContribution { private runningTasksStatusItem: IStatusbarEntryAccessor | undefined; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 485539d452b..81eb57fddd6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -327,7 +327,7 @@ configurationRegistry.registerConfiguration({ }); const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenTermAction, QuickOpenTermAction.ID, QuickOpenTermAction.LABEL), 'Terminal: Switch Active Terminal', nls.localize('terminal', "Terminal")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenTermAction, QuickOpenTermAction.ID, QuickOpenTermAction.LABEL), 'Terminal: Switch Active Terminal', nls.localize('terminal', "Terminal")); const actionBarRegistry = Registry.as(ActionBarExtensions.Actionbar); actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionTermContributor); @@ -344,20 +344,20 @@ Registry.as(panel.Extensions.Panels).setDefaultPanelId(TERM // On mac cmd+` is reserved to cycle between windows, that's why the keybindings use WinCtrl const category = TERMINAL_ACTION_CATEGORY; const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.LABEL), 'Terminal: Kill the Active Terminal Instance', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.LABEL), 'Terminal: Kill the Active Terminal Instance', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKTICK, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_BACKTICK } }), 'Terminal: Create New Integrated Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearSelectionTerminalAction, ClearSelectionTerminalAction.ID, ClearSelectionTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ClearSelectionTerminalAction, ClearSelectionTerminalAction.ID, ClearSelectionTerminalAction.LABEL, { primary: KeyCode.Escape, linux: { primary: KeyCode.Escape } }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE)), 'Terminal: Clear Selection', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewInActiveWorkspaceTerminalAction, CreateNewInActiveWorkspaceTerminalAction.ID, CreateNewInActiveWorkspaceTerminalAction.LABEL), 'Terminal: Create New Integrated Terminal (In Active Workspace)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveTerminalAction, FocusActiveTerminalAction.ID, FocusActiveTerminalAction.LABEL), 'Terminal: Focus Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextTerminalAction, FocusNextTerminalAction.ID, FocusNextTerminalAction.LABEL), 'Terminal: Focus Next Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousTerminalAction, FocusPreviousTerminalAction.ID, FocusPreviousTerminalAction.LABEL), 'Terminal: Focus Previous Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(CreateNewInActiveWorkspaceTerminalAction, CreateNewInActiveWorkspaceTerminalAction.ID, CreateNewInActiveWorkspaceTerminalAction.LABEL), 'Terminal: Create New Integrated Terminal (In Active Workspace)', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FocusActiveTerminalAction, FocusActiveTerminalAction.ID, FocusActiveTerminalAction.LABEL), 'Terminal: Focus Terminal', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FocusNextTerminalAction, FocusNextTerminalAction.ID, FocusNextTerminalAction.LABEL), 'Terminal: Focus Next Terminal', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FocusPreviousTerminalAction, FocusPreviousTerminalAction.ID, FocusPreviousTerminalAction.LABEL), 'Terminal: Focus Previous Terminal', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL, { // Don't use ctrl+a by default as that would override the common go to start // of prompt shell binding primary: 0, @@ -366,82 +366,82 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectAllTermina // makes it easier for users to see how it works though. mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_A } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select All', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RunSelectedTextInTerminalAction, RunSelectedTextInTerminalAction.ID, RunSelectedTextInTerminalAction.LABEL), 'Terminal: Run Selected Text In Active Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RunActiveFileInTerminalAction, RunActiveFileInTerminalAction.ID, RunActiveFileInTerminalAction.LABEL), 'Terminal: Run Active File In Active Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTerminalAction, ToggleTerminalAction.ID, ToggleTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(RunSelectedTextInTerminalAction, RunSelectedTextInTerminalAction.ID, RunSelectedTextInTerminalAction.LABEL), 'Terminal: Run Selected Text In Active Terminal', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(RunActiveFileInTerminalAction, RunActiveFileInTerminalAction.ID, RunActiveFileInTerminalAction.LABEL), 'Terminal: Run Active File In Active Terminal', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleTerminalAction, ToggleTerminalAction.ID, ToggleTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_BACKTICK, mac: { primary: KeyMod.WinCtrl | KeyCode.US_BACKTICK } }), 'View: Toggle Integrated Terminal', nls.localize('viewCategory', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollDownTerminalAction, ScrollDownTerminalAction.ID, ScrollDownTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ScrollDownTerminalAction, ScrollDownTerminalAction.ID, ScrollDownTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageDown, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Down (Line)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollDownPageTerminalAction, ScrollDownPageTerminalAction.ID, ScrollDownPageTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ScrollDownPageTerminalAction, ScrollDownPageTerminalAction.ID, ScrollDownPageTerminalAction.LABEL, { primary: KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyCode.PageDown } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Down (Page)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToBottomTerminalAction, ScrollToBottomTerminalAction.ID, ScrollToBottomTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ScrollToBottomTerminalAction, ScrollToBottomTerminalAction.ID, ScrollToBottomTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.End, linux: { primary: KeyMod.Shift | KeyCode.End } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll to Bottom', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollUpTerminalAction, ScrollUpTerminalAction.ID, ScrollUpTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ScrollUpTerminalAction, ScrollUpTerminalAction.ID, ScrollUpTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageUp, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }, }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Up (Line)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollUpPageTerminalAction, ScrollUpPageTerminalAction.ID, ScrollUpPageTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ScrollUpPageTerminalAction, ScrollUpPageTerminalAction.ID, ScrollUpPageTerminalAction.LABEL, { primary: KeyMod.Shift | KeyCode.PageUp, mac: { primary: KeyCode.PageUp } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Up (Page)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToTopTerminalAction, ScrollToTopTerminalAction.ID, ScrollToTopTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ScrollToTopTerminalAction, ScrollToTopTerminalAction.ID, ScrollToTopTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.Home, linux: { primary: KeyMod.Shift | KeyCode.Home } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll to Top', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL, { primary: 0, 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(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, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(SelectDefaultShellWindowsTerminalAction, SelectDefaultShellWindowsTerminalAction.ID, SelectDefaultShellWindowsTerminalAction.LABEL), 'Terminal: Select Default Shell', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ManageWorkspaceShellPermissionsTerminalCommand, ManageWorkspaceShellPermissionsTerminalCommand.ID, ManageWorkspaceShellPermissionsTerminalCommand.LABEL), 'Terminal: Manage Workspace Shell Permissions', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(RenameTerminalAction, RenameTerminalAction.ID, RenameTerminalAction.LABEL), 'Terminal: Rename', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_F }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Find Widget', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_F }, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Focus Find Widget', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(HideTerminalFindWidgetAction, HideTerminalFindWidgetAction.ID, HideTerminalFindWidgetAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(HideTerminalFindWidgetAction, HideTerminalFindWidgetAction.ID, HideTerminalFindWidgetAction.LABEL, { primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE)), 'Terminal: Hide Find Widget', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordLeftTerminalAction, DeleteWordLeftTerminalAction.ID, DeleteWordLeftTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(DeleteWordLeftTerminalAction, DeleteWordLeftTerminalAction.ID, DeleteWordLeftTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.Backspace, mac: { primary: KeyMod.Alt | KeyCode.Backspace } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Left', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordRightTerminalAction, DeleteWordRightTerminalAction.ID, DeleteWordRightTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(DeleteWordRightTerminalAction, DeleteWordRightTerminalAction.ID, DeleteWordRightTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.Delete, mac: { primary: KeyMod.Alt | KeyCode.Delete } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Right', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteToLineStartTerminalAction, DeleteToLineStartTerminalAction.ID, DeleteToLineStartTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(DeleteToLineStartTerminalAction, DeleteToLineStartTerminalAction.ID, DeleteToLineStartTerminalAction.LABEL, { primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete To Line Start', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveToLineStartTerminalAction, MoveToLineStartTerminalAction.ID, MoveToLineStartTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(MoveToLineStartTerminalAction, MoveToLineStartTerminalAction.ID, MoveToLineStartTerminalAction.LABEL, { primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Move To Line Start', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveToLineEndTerminalAction, MoveToLineEndTerminalAction.ID, MoveToLineEndTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(MoveToLineEndTerminalAction, MoveToLineEndTerminalAction.ID, MoveToLineEndTerminalAction.LABEL, { primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Move To Line End', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SplitTerminalAction, SplitTerminalAction.ID, SplitTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(SplitTerminalAction, SplitTerminalAction.ID, SplitTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_5, mac: { primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH, secondary: [KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_5] } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Split Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SplitInActiveWorkspaceTerminalAction, SplitInActiveWorkspaceTerminalAction.ID, SplitInActiveWorkspaceTerminalAction.LABEL), 'Terminal: Split Terminal (In Active Workspace)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousPaneTerminalAction, FocusPreviousPaneTerminalAction.ID, FocusPreviousPaneTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(SplitInActiveWorkspaceTerminalAction, SplitInActiveWorkspaceTerminalAction.ID, SplitInActiveWorkspaceTerminalAction.LABEL), 'Terminal: Split Terminal (In Active Workspace)', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FocusPreviousPaneTerminalAction, FocusPreviousPaneTerminalAction.ID, FocusPreviousPaneTerminalAction.LABEL, { primary: KeyMod.Alt | KeyCode.LeftArrow, secondary: [KeyMod.Alt | KeyCode.UpArrow], mac: { @@ -449,7 +449,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousPan secondary: [KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.UpArrow] } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Previous Pane', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextPaneTerminalAction, FocusNextPaneTerminalAction.ID, FocusNextPaneTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FocusNextPaneTerminalAction, FocusNextPaneTerminalAction.ID, FocusNextPaneTerminalAction.LABEL, { primary: KeyMod.Alt | KeyCode.RightArrow, secondary: [KeyMod.Alt | KeyCode.DownArrow], mac: { @@ -457,96 +457,96 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextPaneTer secondary: [KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.DownArrow] } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Next Pane', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneLeftTerminalAction, ResizePaneLeftTerminalAction.ID, ResizePaneLeftTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ResizePaneLeftTerminalAction, ResizePaneLeftTerminalAction.ID, ResizePaneLeftTerminalAction.LABEL, { primary: 0, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow }, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Left', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneRightTerminalAction, ResizePaneRightTerminalAction.ID, ResizePaneRightTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ResizePaneRightTerminalAction, ResizePaneRightTerminalAction.ID, ResizePaneRightTerminalAction.LABEL, { primary: 0, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow }, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Right', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneUpTerminalAction, ResizePaneUpTerminalAction.ID, ResizePaneUpTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ResizePaneUpTerminalAction, ResizePaneUpTerminalAction.ID, ResizePaneUpTerminalAction.LABEL, { primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.UpArrow } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Up', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneDownTerminalAction, ResizePaneDownTerminalAction.ID, ResizePaneDownTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ResizePaneDownTerminalAction, ResizePaneDownTerminalAction.ID, ResizePaneDownTerminalAction.LABEL, { primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.DownArrow } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Down', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToPreviousCommandAction, ScrollToPreviousCommandAction.ID, ScrollToPreviousCommandAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ScrollToPreviousCommandAction, ScrollToPreviousCommandAction.ID, ScrollToPreviousCommandAction.LABEL, { primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow } }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate())), 'Terminal: Scroll To Previous Command', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToNextCommandAction, ScrollToNextCommandAction.ID, ScrollToNextCommandAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ScrollToNextCommandAction, ScrollToNextCommandAction.ID, ScrollToNextCommandAction.LABEL, { primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow } }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate())), 'Terminal: Scroll To Next Command', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousCommandAction, SelectToPreviousCommandAction.ID, SelectToPreviousCommandAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(SelectToPreviousCommandAction, SelectToPreviousCommandAction.ID, SelectToPreviousCommandAction.LABEL, { primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Previous Command', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextCommandAction, SelectToNextCommandAction.ID, SelectToNextCommandAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(SelectToNextCommandAction, SelectToNextCommandAction.ID, SelectToNextCommandAction.LABEL, { primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Next Command', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigationModeExitTerminalAction, NavigationModeExitTerminalAction.ID, NavigationModeExitTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(NavigationModeExitTerminalAction, NavigationModeExitTerminalAction.ID, NavigationModeExitTerminalAction.LABEL, { primary: KeyCode.Escape }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Exit Navigation Mode', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigationModeFocusPreviousTerminalAction, NavigationModeFocusPreviousTerminalAction.ID, NavigationModeFocusPreviousTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(NavigationModeFocusPreviousTerminalAction, NavigationModeFocusPreviousTerminalAction.ID, NavigationModeFocusPreviousTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.UpArrow }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Focus Previous Line (Navigation Mode)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigationModeFocusPreviousTerminalAction, NavigationModeFocusPreviousTerminalAction.ID, NavigationModeFocusPreviousTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(NavigationModeFocusPreviousTerminalAction, NavigationModeFocusPreviousTerminalAction.ID, NavigationModeFocusPreviousTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.UpArrow }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Focus Previous Line (Navigation Mode)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigationModeFocusNextTerminalAction, NavigationModeFocusNextTerminalAction.ID, NavigationModeFocusNextTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(NavigationModeFocusNextTerminalAction, NavigationModeFocusNextTerminalAction.ID, NavigationModeFocusNextTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.DownArrow }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Focus Next Line (Navigation Mode)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigationModeFocusNextTerminalAction, NavigationModeFocusNextTerminalAction.ID, NavigationModeFocusNextTerminalAction.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(NavigationModeFocusNextTerminalAction, NavigationModeFocusNextTerminalAction.ID, NavigationModeFocusNextTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.DownArrow }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Focus Next Line (Navigation Mode)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousLineAction, SelectToPreviousLineAction.ID, SelectToPreviousLineAction.LABEL), 'Terminal: Select To Previous Line', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextLineAction, SelectToNextLineAction.ID, SelectToNextLineAction.LABEL), 'Terminal: Select To Next Line', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEscapeSequenceLoggingAction, ToggleEscapeSequenceLoggingAction.ID, ToggleEscapeSequenceLoggingAction.LABEL), 'Terminal: Toggle Escape Sequence Logging', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID, ToggleRegexCommand.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(SelectToPreviousLineAction, SelectToPreviousLineAction.ID, SelectToPreviousLineAction.LABEL), 'Terminal: Select To Previous Line', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(SelectToNextLineAction, SelectToNextLineAction.ID, SelectToNextLineAction.LABEL), 'Terminal: Select To Next Line', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleEscapeSequenceLoggingAction, ToggleEscapeSequenceLoggingAction.ID, ToggleEscapeSequenceLoggingAction.LABEL), 'Terminal: Toggle Escape Sequence Logging', category); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleRegexCommand, ToggleRegexCommand.ID, ToggleRegexCommand.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_R, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } }, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find using regex', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID, ToggleRegexCommand.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleRegexCommand, ToggleRegexCommand.ID, ToggleRegexCommand.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_R, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find using regex', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID, ToggleWholeWordCommand.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleWholeWordCommand, ToggleWholeWordCommand.ID, ToggleWholeWordCommand.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_W, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } }, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find using whole word', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID, ToggleWholeWordCommand.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleWholeWordCommand, ToggleWholeWordCommand.ID, ToggleWholeWordCommand.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_W, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find using whole word', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID, ToggleCaseSensitiveCommand.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID, ToggleCaseSensitiveCommand.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_C, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } }, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find using case sensitive', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID, ToggleCaseSensitiveCommand.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID, ToggleCaseSensitiveCommand.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_C, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find using case sensitive', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID, FindNext.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FindNext, FindNext.ID, FindNext.LABEL, { primary: KeyCode.F3, mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find next', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID, FindNext.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FindNext, FindNext.ID, FindNext.LABEL, { primary: KeyCode.F3, secondary: [KeyMod.Shift | KeyCode.Enter], mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3, KeyMod.Shift | KeyCode.Enter] } }, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find next', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, { primary: KeyMod.Shift | KeyCode.F3, mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find previous', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, { +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(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, KeyCode.Enter] }, @@ -554,13 +554,13 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, Fi // Commands miht be affected by Web restrictons if (BrowserFeatures.clipboard.writeText) { - actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, CopyTerminalSelectionAction.LABEL, { + actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, CopyTerminalSelectionAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C } }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FOCUS)), 'Terminal: Copy Selection', category); } if (BrowserFeatures.clipboard.readText) { - actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.LABEL, { + actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category); diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalRemote.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalRemote.ts index 2b7d5e39af3..1aa467187c2 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalRemote.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalRemote.ts @@ -15,7 +15,7 @@ import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal export function registerRemoteContributions() { const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); - actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewLocalTerminalAction, CreateNewLocalTerminalAction.ID, CreateNewLocalTerminalAction.LABEL), 'Terminal: Create New Integrated Terminal (Local)', TERMINAL_ACTION_CATEGORY); + actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(CreateNewLocalTerminalAction, CreateNewLocalTerminalAction.ID, CreateNewLocalTerminalAction.LABEL), 'Terminal: Create New Integrated Terminal (Local)', TERMINAL_ACTION_CATEGORY); } export class CreateNewLocalTerminalAction extends Action { diff --git a/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts b/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts index 1a43978f692..358e503f4ad 100644 --- a/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts +++ b/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts @@ -219,7 +219,7 @@ if (ENABLE) { const registry = Registry.as(Extensions.WorkbenchActions); - registry.registerWorkbenchAction(new SyncActionDescriptor(TestCustomEditorsAction, TestCustomEditorsAction.ID, TestCustomEditorsAction.LABEL), 'Test Open Custom Editor'); + registry.registerWorkbenchAction(SyncActionDescriptor.create(TestCustomEditorsAction, TestCustomEditorsAction.ID, TestCustomEditorsAction.LABEL), 'Test Open Custom Editor'); class TestCustomEditorInputFactory implements IEditorInputFactory { diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 268b0fcda80..ac4567e81c4 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -285,16 +285,16 @@ class GenerateColorThemeAction extends Action { const category = localize('preferences', "Preferences"); -const colorThemeDescriptor = new SyncActionDescriptor(SelectColorThemeAction, SelectColorThemeAction.ID, SelectColorThemeAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_T) }); +const colorThemeDescriptor = SyncActionDescriptor.create(SelectColorThemeAction, SelectColorThemeAction.ID, SelectColorThemeAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_T) }); Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(colorThemeDescriptor, 'Preferences: Color Theme', category); -const iconThemeDescriptor = new SyncActionDescriptor(SelectIconThemeAction, SelectIconThemeAction.ID, SelectIconThemeAction.LABEL); +const iconThemeDescriptor = SyncActionDescriptor.create(SelectIconThemeAction, SelectIconThemeAction.ID, SelectIconThemeAction.LABEL); Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(iconThemeDescriptor, 'Preferences: File Icon Theme', category); const developerCategory = localize('developer', "Developer"); -const generateColorThemeDescriptor = new SyncActionDescriptor(GenerateColorThemeAction, GenerateColorThemeAction.ID, GenerateColorThemeAction.LABEL); +const generateColorThemeDescriptor = SyncActionDescriptor.create(GenerateColorThemeAction, GenerateColorThemeAction.ID, GenerateColorThemeAction.LABEL); Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(generateColorThemeDescriptor, 'Developer: Generate Color Theme From Current Settings', developerCategory); MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { diff --git a/src/vs/workbench/contrib/update/browser/update.contribution.ts b/src/vs/workbench/contrib/update/browser/update.contribution.ts index 6f872f2f97f..d8b0f375458 100644 --- a/src/vs/workbench/contrib/update/browser/update.contribution.ts +++ b/src/vs/workbench/contrib/update/browser/update.contribution.ts @@ -23,10 +23,10 @@ const actionRegistry = Registry.as(ActionExtensions.Wo // Editor actionRegistry - .registerWorkbenchAction(new SyncActionDescriptor(ShowCurrentReleaseNotesAction, ShowCurrentReleaseNotesAction.ID, ShowCurrentReleaseNotesAction.LABEL), `${product.nameShort}: Show Release Notes`, product.nameShort); + .registerWorkbenchAction(SyncActionDescriptor.create(ShowCurrentReleaseNotesAction, ShowCurrentReleaseNotesAction.ID, ShowCurrentReleaseNotesAction.LABEL), `${product.nameShort}: Show Release Notes`, product.nameShort); actionRegistry - .registerWorkbenchAction(new SyncActionDescriptor(CheckForVSCodeUpdateAction, CheckForVSCodeUpdateAction.ID, CheckForVSCodeUpdateAction.LABEL), `${product.nameShort}: Check for Update`, product.nameShort, CONTEXT_UPDATE_STATE.isEqualTo(StateType.Idle)); + .registerWorkbenchAction(SyncActionDescriptor.create(CheckForVSCodeUpdateAction, CheckForVSCodeUpdateAction.ID, CheckForVSCodeUpdateAction.LABEL), `${product.nameShort}: Check for Update`, product.nameShort, CONTEXT_UPDATE_STATE.isEqualTo(StateType.Idle)); // Menu if (ShowCurrentReleaseNotesAction.AVAILABE) { diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 86add42f8ce..af5564e4063 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -41,7 +41,7 @@ export class OpenUrlAction extends Action { } Registry.as(ActionExtensions.WorkbenchActions).registerWorkbenchAction( - new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), + SyncActionDescriptor.create(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), 'Open URL', localize('developer', 'Developer') ); diff --git a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts index 2189e68d81f..445f190e1b2 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts @@ -85,6 +85,6 @@ function registerWebViewCommands(editorId: string): void { registerWebViewCommands(WebviewEditor.ID); actionRegistry.registerWorkbenchAction( - new SyncActionDescriptor(ReloadWebviewAction, ReloadWebviewAction.ID, ReloadWebviewAction.LABEL), + SyncActionDescriptor.create(ReloadWebviewAction, ReloadWebviewAction.ID, ReloadWebviewAction.LABEL), 'Reload Webviews', webviewDeveloperCategory); 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 627d57bd3fd..1e47c68737b 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts @@ -22,7 +22,7 @@ registerSingleton(IWebviewService, ElectronWebviewService, true); const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); actionRegistry.registerWorkbenchAction( - new SyncActionDescriptor(webviewCommands.OpenWebviewDeveloperToolsAction, webviewCommands.OpenWebviewDeveloperToolsAction.ID, webviewCommands.OpenWebviewDeveloperToolsAction.LABEL), + SyncActionDescriptor.create(webviewCommands.OpenWebviewDeveloperToolsAction, webviewCommands.OpenWebviewDeveloperToolsAction.ID, webviewCommands.OpenWebviewDeveloperToolsAction.LABEL), webviewCommands.OpenWebviewDeveloperToolsAction.ALIAS, webviewDeveloperCategory); diff --git a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts index 9dc2d370370..81c711a0380 100644 --- a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts +++ b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts @@ -258,10 +258,10 @@ class WelcomeOverlay extends Disposable { } Registry.as(Extensions.WorkbenchActions) - .registerWorkbenchAction(new SyncActionDescriptor(WelcomeOverlayAction, WelcomeOverlayAction.ID, WelcomeOverlayAction.LABEL), 'Help: User Interface Overview', localize('help', "Help")); + .registerWorkbenchAction(SyncActionDescriptor.create(WelcomeOverlayAction, WelcomeOverlayAction.ID, WelcomeOverlayAction.LABEL), 'Help: User Interface Overview', localize('help', "Help")); Registry.as(Extensions.WorkbenchActions) - .registerWorkbenchAction(new SyncActionDescriptor(HideWelcomeOverlayAction, HideWelcomeOverlayAction.ID, HideWelcomeOverlayAction.LABEL, { primary: KeyCode.Escape }, OVERLAY_VISIBLE), 'Help: Hide Interface Overview', localize('help', "Help")); + .registerWorkbenchAction(SyncActionDescriptor.create(HideWelcomeOverlayAction, HideWelcomeOverlayAction.ID, HideWelcomeOverlayAction.LABEL, { primary: KeyCode.Escape }, OVERLAY_VISIBLE), 'Help: Hide Interface Overview', localize('help', "Help")); // theming diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts index 42978506c8c..eaebdb84205 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.contribution.ts @@ -39,7 +39,7 @@ Registry.as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(WelcomePageContribution, LifecyclePhase.Restored); Registry.as(ActionExtensions.WorkbenchActions) - .registerWorkbenchAction(new SyncActionDescriptor(WelcomePageAction, WelcomePageAction.ID, WelcomePageAction.LABEL), 'Help: Welcome', localize('help', "Help")); + .registerWorkbenchAction(SyncActionDescriptor.create(WelcomePageAction, WelcomePageAction.ID, WelcomePageAction.LABEL), 'Help: Welcome', localize('help', "Help")); Registry.as(EditorExtensions.EditorInputFactories).registerEditorInputFactory(WelcomeInputFactory.ID, WelcomeInputFactory); 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 8a91091c6b7..0538dd38b27 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts @@ -29,7 +29,7 @@ Registry.as(EditorExtensions.Editors) Registry.as(Extensions.WorkbenchActions) .registerWorkbenchAction( - new SyncActionDescriptor(EditorWalkThroughAction, EditorWalkThroughAction.ID, EditorWalkThroughAction.LABEL), + SyncActionDescriptor.create(EditorWalkThroughAction, EditorWalkThroughAction.ID, EditorWalkThroughAction.LABEL), 'Help: Interactive Playground', localize('help', "Help")); Registry.as(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(EditorWalkThroughInputFactory.ID, EditorWalkThroughInputFactory); diff --git a/src/vs/workbench/electron-browser/desktop.contribution.ts b/src/vs/workbench/electron-browser/desktop.contribution.ts index 2afbed6f4ce..af5f55407ef 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts @@ -31,16 +31,16 @@ import product from 'vs/platform/product/common/product'; (function registerZoomActions(): void { const viewCategory = nls.localize('view', "View"); - registry.registerWorkbenchAction(new SyncActionDescriptor(ZoomInAction, ZoomInAction.ID, ZoomInAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_EQUAL, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_EQUAL, KeyMod.CtrlCmd | KeyCode.NUMPAD_ADD] }), 'View: Zoom In', viewCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ZoomOutAction, ZoomOutAction.ID, ZoomOutAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_MINUS, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS, KeyMod.CtrlCmd | KeyCode.NUMPAD_SUBTRACT], linux: { primary: KeyMod.CtrlCmd | KeyCode.US_MINUS, secondary: [KeyMod.CtrlCmd | KeyCode.NUMPAD_SUBTRACT] } }), 'View: Zoom Out', viewCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ZoomResetAction, ZoomResetAction.ID, ZoomResetAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.NUMPAD_0 }), 'View: Reset Zoom', viewCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(ZoomInAction, ZoomInAction.ID, ZoomInAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_EQUAL, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_EQUAL, KeyMod.CtrlCmd | KeyCode.NUMPAD_ADD] }), 'View: Zoom In', viewCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(ZoomOutAction, ZoomOutAction.ID, ZoomOutAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_MINUS, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS, KeyMod.CtrlCmd | KeyCode.NUMPAD_SUBTRACT], linux: { primary: KeyMod.CtrlCmd | KeyCode.US_MINUS, secondary: [KeyMod.CtrlCmd | KeyCode.NUMPAD_SUBTRACT] } }), 'View: Zoom Out', viewCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(ZoomResetAction, ZoomResetAction.ID, ZoomResetAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.NUMPAD_0 }), 'View: Reset Zoom', viewCategory); })(); // Actions: Window (function registerWindowActions(): void { - registry.registerWorkbenchAction(new SyncActionDescriptor(CloseCurrentWindowAction, CloseCurrentWindowAction.ID, CloseCurrentWindowAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W }), 'Close Window'); - registry.registerWorkbenchAction(new SyncActionDescriptor(SwitchWindow, SwitchWindow.ID, SwitchWindow.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_W } }), 'Switch Window...'); - registry.registerWorkbenchAction(new SyncActionDescriptor(QuickSwitchWindow, QuickSwitchWindow.ID, QuickSwitchWindow.LABEL), 'Quick Switch Window...'); + registry.registerWorkbenchAction(SyncActionDescriptor.create(CloseCurrentWindowAction, CloseCurrentWindowAction.ID, CloseCurrentWindowAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W }), 'Close Window'); + registry.registerWorkbenchAction(SyncActionDescriptor.create(SwitchWindow, SwitchWindow.ID, SwitchWindow.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_W } }), 'Switch Window...'); + registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickSwitchWindow, QuickSwitchWindow.ID, QuickSwitchWindow.LABEL), 'Quick Switch Window...'); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: CloseCurrentWindowAction.ID, // close the window when the last editor is closed by reusing the same keybinding @@ -90,9 +90,9 @@ import product from 'vs/platform/product/common/product'; // Actions: Developer (function registerDeveloperActions(): void { const developerCategory = nls.localize('developer', "Developer"); - registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSharedProcessAction, ToggleSharedProcessAction.ID, ToggleSharedProcessAction.LABEL), 'Developer: Toggle Shared Process', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowWithExtensionsDisabledAction, ReloadWindowWithExtensionsDisabledAction.ID, ReloadWindowWithExtensionsDisabledAction.LABEL), 'Developer: Reload With Extensions Disabled', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleDevToolsAction, ToggleDevToolsAction.ID, ToggleDevToolsAction.LABEL), 'Developer: Toggle Developer Tools', developerCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleSharedProcessAction, ToggleSharedProcessAction.ID, ToggleSharedProcessAction.LABEL), 'Developer: Toggle Shared Process', developerCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(ReloadWindowWithExtensionsDisabledAction, ReloadWindowWithExtensionsDisabledAction.ID, ReloadWindowWithExtensionsDisabledAction.LABEL), 'Developer: Reload With Extensions Disabled', developerCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleDevToolsAction, ToggleDevToolsAction.ID, ToggleDevToolsAction.LABEL), 'Developer: Toggle Developer Tools', developerCategory); KeybindingsRegistry.registerKeybindingRule({ id: ToggleDevToolsAction.ID, @@ -106,7 +106,7 @@ import product from 'vs/platform/product/common/product'; // Actions: Runtime Arguments (function registerRuntimeArgumentsAction(): void { const preferencesCategory = nls.localize('preferences', "Preferences"); - registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureRuntimeArgumentsAction, ConfigureRuntimeArgumentsAction.ID, ConfigureRuntimeArgumentsAction.LABEL), 'Preferences: Configure Runtime Arguments', preferencesCategory); + registry.registerWorkbenchAction(SyncActionDescriptor.create(ConfigureRuntimeArgumentsAction, ConfigureRuntimeArgumentsAction.ID, ConfigureRuntimeArgumentsAction.LABEL), 'Preferences: Configure Runtime Arguments', preferencesCategory); })(); })(); diff --git a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts index c63f391949e..e1d8c4de6f5 100644 --- a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts @@ -420,4 +420,4 @@ export class ManageAuthorizedExtensionURIsAction extends Action { } const actionRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ManageAuthorizedExtensionURIsAction, ManageAuthorizedExtensionURIsAction.ID, ManageAuthorizedExtensionURIsAction.LABEL), `Extensions: Manage Authorized Extension URIs...`, ExtensionsLabel); +actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ManageAuthorizedExtensionURIsAction, ManageAuthorizedExtensionURIsAction.ID, ManageAuthorizedExtensionURIsAction.LABEL), `Extensions: Manage Authorized Extension URIs...`, ExtensionsLabel); diff --git a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts index dc4b8bf9690..cb638f64361 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts @@ -422,4 +422,4 @@ export class MeasureExtHostLatencyAction extends Action { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(MeasureExtHostLatencyAction, MeasureExtHostLatencyAction.ID, MeasureExtHostLatencyAction.LABEL), 'Developer: Measure Extension Host Latency', nls.localize('developer', "Developer")); +registry.registerWorkbenchAction(SyncActionDescriptor.create(MeasureExtHostLatencyAction, MeasureExtHostLatencyAction.ID, MeasureExtHostLatencyAction.LABEL), 'Developer: Measure Extension Host Latency', nls.localize('developer', "Developer")); diff --git a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts index 675c17c8b95..8a445d39a20 100644 --- a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts +++ b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts @@ -577,7 +577,7 @@ suite('KeybindingsEditorModel test', () => { function registerCommandWithTitle(command: string, title: string): void { const registry = Registry.as(ActionExtensions.WorkbenchActions); - registry.registerWorkbenchAction(new SyncActionDescriptor(AnAction, command, title, { primary: 0 }), ''); + registry.registerWorkbenchAction(SyncActionDescriptor.create(AnAction, command, title, { primary: 0 }), ''); } function assertKeybindingItems(actual: ResolvedKeybindingItem[], expected: ResolvedKeybindingItem[]) { From dfda354bb8f7e4165563b5a33c747b83d10a496a Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 14 Nov 2019 16:48:04 -0800 Subject: [PATCH 36/88] Safari on macOS has PointerEvents as well ... --- src/vs/editor/browser/controller/pointerHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 4c7ad07a759..28ebcf990af 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -11,7 +11,7 @@ import { IMouseTarget } from 'vs/editor/browser/editorBrowser'; import { EditorMouseEvent, EditorPointerEventFactory } from 'vs/editor/browser/editorDom'; import { ViewController } from 'vs/editor/browser/view/viewController'; import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { isSafari } from 'vs/base/browser/browser'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; interface IThrottledGestureEvent { translationX: number; @@ -283,7 +283,7 @@ export class PointerHandler extends Disposable { super(); if (window.navigator.msPointerEnabled) { this.handler = this._register(new MsPointerHandler(context, viewController, viewHelper)); - } else if (((window).PointerEvent && isSafari)) { + } else if (((window).PointerEvent && BrowserFeatures.pointerEvents)) { this.handler = this._register(new PointerEventHandler(context, viewController, viewHelper)); } else if ((window).TouchEvent) { this.handler = this._register(new TouchHandler(context, viewController, viewHelper)); From a7d86df09fad1efe26f14de7f21e02784e2cbacf Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 14 Nov 2019 16:51:38 -0800 Subject: [PATCH 37/88] window.PointerEvent --- src/vs/base/browser/canIUse.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/canIUse.ts b/src/vs/base/browser/canIUse.ts index 989b11c370f..a8fc3db9d80 100644 --- a/src/vs/base/browser/canIUse.ts +++ b/src/vs/base/browser/canIUse.ts @@ -56,5 +56,5 @@ export const BrowserFeatures = { })(), touch: 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0, - pointerEvents: browser.isSafari && ('ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0) + pointerEvents: browser.isSafari && window.PointerEvent && ('ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0) }; From bf9370040444655883f5070b071ddb1d64767bf1 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 14 Nov 2019 16:52:54 -0800 Subject: [PATCH 38/88] do not allow touch to scroll on body. --- src/vs/workbench/browser/style.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/browser/style.ts b/src/vs/workbench/browser/style.ts index 23b39b5f4f3..d38931efc44 100644 --- a/src/vs/workbench/browser/style.ts +++ b/src/vs/workbench/browser/style.ts @@ -175,6 +175,9 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { // allow to select text in monaco editor instances. if (isSafari) { collector.addRule(` + body.web { + touch-action: none; + } .monaco-workbench .monaco-editor .view-lines { user-select: text; -webkit-user-select: text; From 5370653cf83722783520cd864a43dd19394c0495 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Nov 2019 17:10:34 -0800 Subject: [PATCH 39/88] Use `create` functions for more descriptors For #81574 This applies the fix from #84878 to a number of other descriptor classes in our codebase --- .../workbench/api/browser/mainThreadComments.ts | 2 +- .../workbench/api/browser/viewsExtensionPoint.ts | 2 +- src/vs/workbench/browser/editor.ts | 10 +++++++++- src/vs/workbench/browser/panel.ts | 8 ++++++-- .../browser/parts/editor/editor.contribution.ts | 12 ++++++------ src/vs/workbench/browser/quickopen.ts | 12 ++++++++---- src/vs/workbench/browser/viewlet.ts | 15 +++++++++++++-- .../browser/webviewEditor.contribution.ts | 2 +- .../contrib/debug/browser/debug.contribution.ts | 6 +++--- .../extensions/browser/extensions.contribution.ts | 8 ++++---- .../electron-browser/extensions.contribution.ts | 2 +- .../contrib/files/browser/files.contribution.ts | 4 ++-- .../files/browser/files.web.contribution.ts | 2 +- .../files/electron-browser/files.contribution.ts | 2 +- .../markers/browser/markers.contribution.ts | 2 +- .../contrib/output/browser/output.contribution.ts | 4 ++-- .../browser/preferences.contribution.ts | 6 +++--- .../quickopen/browser/quickopen.contribution.ts | 10 +++++----- src/vs/workbench/contrib/remote/browser/remote.ts | 2 +- .../contrib/scm/browser/scm.contribution.ts | 2 +- .../contrib/search/browser/search.contribution.ts | 10 +++++----- .../contrib/tasks/browser/task.contribution.ts | 2 +- .../terminal/browser/terminal.contribution.ts | 2 +- .../browser/testCustomEditors.ts | 2 +- .../webview/browser/webview.contribution.ts | 2 +- .../browser/walkThrough.contribution.ts | 2 +- .../test/browser/editorGroupsService.test.ts | 2 +- .../editor/test/browser/editorService.test.ts | 2 +- .../test/browser/parts/editor/baseEditor.test.ts | 12 ++++++------ src/vs/workbench/test/browser/quickopen.test.ts | 4 ++-- src/vs/workbench/test/browser/viewlet.test.ts | 8 ++++---- 31 files changed, 94 insertions(+), 67 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index 3f18e9c1a05..bbe5edd3b6b 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -446,7 +446,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments private registerPanel(commentsPanelAlreadyConstructed: boolean) { if (!commentsPanelAlreadyConstructed) { - Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( + Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create( CommentsPanel, COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE, diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index efbc23c293f..06138112961 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -335,7 +335,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { super(id, `${id}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); } } - const viewletDescriptor = new ViewletDescriptor( + const viewletDescriptor = ViewletDescriptor.create( CustomViewlet, id, title, diff --git a/src/vs/workbench/browser/editor.ts b/src/vs/workbench/browser/editor.ts index d441fb2f0d9..e03a20900a5 100644 --- a/src/vs/workbench/browser/editor.ts +++ b/src/vs/workbench/browser/editor.ts @@ -7,7 +7,7 @@ import { EditorInput } from 'vs/workbench/common/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Registry } from 'vs/platform/registry/common/platform'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { IConstructorSignature0, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IConstructorSignature0, IInstantiationService, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { find } from 'vs/base/common/arrays'; export interface IEditorDescriptor { @@ -54,6 +54,14 @@ export interface IEditorRegistry { */ export class EditorDescriptor implements IEditorDescriptor { + public static create( + ctor: { new(...services: Services): BaseEditor }, + id: string, + name: string + ): EditorDescriptor { + return new EditorDescriptor(ctor as IConstructorSignature0, id, name); + } + constructor( private readonly ctor: IConstructorSignature0, private readonly id: string, diff --git a/src/vs/workbench/browser/panel.ts b/src/vs/workbench/browser/panel.ts index cbe865fe623..666029c78ae 100644 --- a/src/vs/workbench/browser/panel.ts +++ b/src/vs/workbench/browser/panel.ts @@ -9,7 +9,7 @@ import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/ import { Action } from 'vs/base/common/actions'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; -import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; +import { IConstructorSignature0, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { isAncestor } from 'vs/base/browser/dom'; import { assertIsDefined } from 'vs/base/common/types'; @@ -20,7 +20,11 @@ export abstract class Panel extends Composite implements IPanel { } */ export class PanelDescriptor extends CompositeDescriptor { - constructor(ctor: IConstructorSignature0, id: string, name: string, cssClass?: string, order?: number, _commandId?: string) { + public static create(ctor: { new(...services: Services): Panel }, id: string, name: string, cssClass?: string, order?: number, _commandId?: string): PanelDescriptor { + return new PanelDescriptor(ctor as IConstructorSignature0, id, name, cssClass, order, _commandId); + } + + private constructor(ctor: IConstructorSignature0, id: string, name: string, cssClass?: string, order?: number, _commandId?: string) { super(ctor, id, name, cssClass, order, _commandId); } } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 2e25ce67d18..7dee5c30a3a 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -57,7 +57,7 @@ import { IFilesConfigurationService } from 'vs/workbench/services/filesConfigura // Register String Editor Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( TextResourceEditor, TextResourceEditor.ID, nls.localize('textEditor', "Text Editor"), @@ -70,7 +70,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( // Register Text Diff Editor Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( TextDiffEditor, TextDiffEditor.ID, nls.localize('textDiffEditor', "Text Diff Editor") @@ -82,7 +82,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( // Register Binary Resource Diff Editor Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( BinaryResourceDiffEditor, BinaryResourceDiffEditor.ID, nls.localize('binaryDiffEditor', "Binary Diff Editor") @@ -93,7 +93,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( ); Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( SideBySideEditor, SideBySideEditor.ID, nls.localize('sideBySideEditor', "Side by Side Editor") @@ -279,7 +279,7 @@ const editorPickerContextKey = 'inEditorsPicker'; const editorPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(editorPickerContextKey)); Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( ActiveEditorGroupPicker, ActiveEditorGroupPicker.ID, editorCommands.NAVIGATE_IN_ACTIVE_GROUP_PREFIX, @@ -295,7 +295,7 @@ Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpen ); Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( AllEditorsPicker, AllEditorsPicker.ID, editorCommands.NAVIGATE_ALL_EDITORS_GROUP_PREFIX, diff --git a/src/vs/workbench/browser/quickopen.ts b/src/vs/workbench/browser/quickopen.ts index 0c4b67f1f29..2dda51f6ef2 100644 --- a/src/vs/workbench/browser/quickopen.ts +++ b/src/vs/workbench/browser/quickopen.ts @@ -15,7 +15,7 @@ import { QuickOpenEntry, QuickOpenEntryGroup } from 'vs/base/parts/quickopen/bro import { EditorOptions, EditorInput, IEditorInput } from 'vs/workbench/common/editor'; import { IResourceInput, IEditorOptions } from 'vs/platform/editor/common/editor'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; -import { IConstructorSignature0, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IConstructorSignature0, IInstantiationService, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -136,9 +136,13 @@ export class QuickOpenHandlerDescriptor { private id: string; private ctor: IConstructorSignature0; - constructor(ctor: IConstructorSignature0, id: string, prefix: string, contextKey: string | undefined, description: string, instantProgress?: boolean); - constructor(ctor: IConstructorSignature0, id: string, prefix: string, contextKey: string | undefined, helpEntries: QuickOpenHandlerHelpEntry[], instantProgress?: boolean); - constructor(ctor: IConstructorSignature0, id: string, prefix: string, contextKey: string | undefined, param: string | QuickOpenHandlerHelpEntry[], instantProgress: boolean = false) { + public static create(ctor: { new(...services: Services): QuickOpenHandler }, id: string, prefix: string, contextKey: string | undefined, description: string, instantProgress?: boolean): QuickOpenHandlerDescriptor; + public static create(ctor: { new(...services: Services): QuickOpenHandler }, id: string, prefix: string, contextKey: string | undefined, helpEntries: QuickOpenHandlerHelpEntry[], instantProgress?: boolean): QuickOpenHandlerDescriptor; + public static create(ctor: { new(...services: Services): QuickOpenHandler }, id: string, prefix: string, contextKey: string | undefined, param: string | QuickOpenHandlerHelpEntry[], instantProgress: boolean = false): QuickOpenHandlerDescriptor { + return new QuickOpenHandlerDescriptor(ctor as IConstructorSignature0, id, prefix, contextKey, param, instantProgress); + } + + private constructor(ctor: IConstructorSignature0, id: string, prefix: string, contextKey: string | undefined, param: string | QuickOpenHandlerHelpEntry[], instantProgress: boolean = false) { this.ctor = ctor; this.id = id; this.prefix = prefix; diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index 6ddf85eb74b..5591737dd01 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -10,7 +10,7 @@ import { Action, IAction } from 'vs/base/common/actions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite'; -import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation'; +import { IConstructorSignature0, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { ToggleSidebarVisibilityAction, ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/layoutActions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; @@ -56,7 +56,18 @@ export abstract class Viewlet extends Composite implements IViewlet { */ export class ViewletDescriptor extends CompositeDescriptor { - constructor( + public static create( + ctor: { new(...services: Services): Viewlet }, + id: string, + name: string, + cssClass?: string, + order?: number, + iconUrl?: URI + ): ViewletDescriptor { + return new ViewletDescriptor(ctor as IConstructorSignature0, id, name, cssClass, order, iconUrl); + } + + private constructor( ctor: IConstructorSignature0, id: string, name: string, diff --git a/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts b/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts index d544ec1ee51..c5aa8b431bb 100644 --- a/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts +++ b/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts @@ -26,7 +26,7 @@ Registry.as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(CustomEditorContribution, LifecyclePhase.Starting); Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( WebviewEditor, WebviewEditor.ID, 'Webview Editor', diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index ad44fc85547..a25124564ae 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -80,7 +80,7 @@ class OpenDebugPanelAction extends TogglePanelAction { } // register viewlet -Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( +Registry.as(ViewletExtensions.Viewlets).registerViewlet(ViewletDescriptor.create( DebugViewlet, VIEWLET_ID, nls.localize('debug', "Debug"), @@ -96,7 +96,7 @@ const openPanelKb: IKeybindings = { }; // register repl panel -Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( +Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create( Repl, REPL_ID, nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, 'Debug Console'), @@ -166,7 +166,7 @@ registerDebugCommandPaletteItem(TOGGLE_INLINE_BREAKPOINT_ID, nls.localize('inlin // Register Quick Open (Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( DebugQuickOpenHandler, DebugQuickOpenHandler.ID, 'debug ', diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index acab3d418c7..c624c7956b2 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -54,7 +54,7 @@ Registry.as(OutputExtensions.OutputChannels) // Quickopen Registry.as(Extensions.Quickopen).registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( ExtensionsHandler, ExtensionsHandler.ID, 'ext ', @@ -66,7 +66,7 @@ Registry.as(Extensions.Quickopen).registerQuickOpenHandler( // Editor Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( ExtensionEditor, ExtensionEditor.ID, localize('extension', "Extension") @@ -76,7 +76,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( ]); // Viewlet -const viewletDescriptor = new ViewletDescriptor( +const viewletDescriptor = ViewletDescriptor.create( ExtensionsViewlet, VIEWLET_ID, localize('extensions', "Extensions"), @@ -350,7 +350,7 @@ class ExtensionsContributions implements IWorkbenchContribution { if (canManageExtensions) { Registry.as(Extensions.Quickopen).registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( GalleryExtensionsHandler, GalleryExtensionsHandler.ID, 'ext install ', 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 4b5d7b3f7e3..7a080e779e5 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -34,7 +34,7 @@ workbenchRegistry.registerWorkbenchContribution(ExtensionsAutoProfiler, Lifecycl // Running Extensions Editor -const runtimeExtensionsEditorDescriptor = new EditorDescriptor( +const runtimeExtensionsEditorDescriptor = EditorDescriptor.create( RuntimeExtensionsEditor, RuntimeExtensionsEditor.ID, localize('runtimeExtension', "Running Extensions") diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 5633cb728fd..59fba1cfeaf 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -75,7 +75,7 @@ class FileUriLabelContribution implements IWorkbenchContribution { } // Register Viewlet -Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( +Registry.as(ViewletExtensions.Viewlets).registerViewlet(ViewletDescriptor.create( ExplorerViewlet, VIEWLET_ID, nls.localize('explore', "Explorer"), @@ -101,7 +101,7 @@ registry.registerWorkbenchAction( // Register file editors Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( BinaryFileEditor, BinaryFileEditor.ID, nls.localize('binaryFileEditor', "Binary File Editor") diff --git a/src/vs/workbench/contrib/files/browser/files.web.contribution.ts b/src/vs/workbench/contrib/files/browser/files.web.contribution.ts index 44d91d66b91..494bdac3488 100644 --- a/src/vs/workbench/contrib/files/browser/files.web.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.web.contribution.ts @@ -13,7 +13,7 @@ import { TextFileEditor } from 'vs/workbench/contrib/files/browser/editors/textF // Register file editor Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( TextFileEditor, TextFileEditor.ID, nls.localize('textFileEditor', "Text File Editor") diff --git a/src/vs/workbench/contrib/files/electron-browser/files.contribution.ts b/src/vs/workbench/contrib/files/electron-browser/files.contribution.ts index d5d18ac24ea..eda8317d40d 100644 --- a/src/vs/workbench/contrib/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/electron-browser/files.contribution.ts @@ -13,7 +13,7 @@ import { NativeTextFileEditor } from 'vs/workbench/contrib/files/electron-browse // Register file editor Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( NativeTextFileEditor, NativeTextFileEditor.ID, nls.localize('textFileEditor', "Text File Editor") diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index d28b9d0d716..42fd074794f 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -92,7 +92,7 @@ Registry.as(Extensions.Configuration).registerConfigurat // markers panel -Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( +Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create( MarkersPanel, Constants.MARKERS_PANEL_ID, Messages.MARKERS_PANEL_TITLE_PROBLEMS, diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index b5032f1eacc..ec68b3b8556 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -41,7 +41,7 @@ ModesRegistry.registerLanguage({ }); // Register Output Panel -Registry.as(Extensions.Panels).registerPanel(new PanelDescriptor( +Registry.as(Extensions.Panels).registerPanel(PanelDescriptor.create( OutputPanel, OUTPUT_PANEL_ID, nls.localize('output', "Output"), @@ -51,7 +51,7 @@ Registry.as(Extensions.Panels).registerPanel(new PanelDescriptor( )); Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( LogViewer, LogViewer.LOG_VIEWER_EDITOR_ID, nls.localize('logViewer', "Log Viewer") diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 4580b8d23ef..cd52141866d 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -42,7 +42,7 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( PreferencesEditor, PreferencesEditor.ID, nls.localize('defaultPreferencesEditor', "Default Preferences Editor") @@ -53,7 +53,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( ); Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( SettingsEditor2, SettingsEditor2.ID, nls.localize('settingsEditor2', "Settings Editor 2") @@ -64,7 +64,7 @@ Registry.as(EditorExtensions.Editors).registerEditor( ); Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( KeybindingsEditor, KeybindingsEditor.ID, nls.localize('keybindingsEditor', "Keybindings Editor") diff --git a/src/vs/workbench/contrib/quickopen/browser/quickopen.contribution.ts b/src/vs/workbench/contrib/quickopen/browser/quickopen.contribution.ts index 87639a7409a..ecac0bf8cc0 100644 --- a/src/vs/workbench/contrib/quickopen/browser/quickopen.contribution.ts +++ b/src/vs/workbench/contrib/quickopen/browser/quickopen.contribution.ts @@ -72,7 +72,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // Register Quick Open Handler Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( CommandsHandler, CommandsHandler.ID, ALL_COMMANDS_PREFIX, @@ -82,7 +82,7 @@ Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpen ); Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( GotoLineHandler, GotoLineHandler.ID, GOTO_LINE_PREFIX, @@ -98,7 +98,7 @@ Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpen ); Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( GotoSymbolHandler, GotoSymbolHandler.ID, GOTO_SYMBOL_PREFIX, @@ -119,7 +119,7 @@ Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpen ); Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( HelpHandler, HelpHandler.ID, HELP_PREFIX, @@ -129,7 +129,7 @@ Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpen ); Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( ViewPickerHandler, ViewPickerHandler.ID, VIEW_PICKER_PREFIX, diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 6608c979466..e4d5e233034 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -274,7 +274,7 @@ export class RemoteViewlet extends FilterViewContainerViewlet { } } -Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( +Registry.as(ViewletExtensions.Viewlets).registerViewlet(ViewletDescriptor.create( RemoteViewlet, VIEWLET_ID, nls.localize('remote.explorer', "Remote Explorer"), diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index ce51e05887c..0c40988389f 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -38,7 +38,7 @@ class OpenSCMViewletAction extends ShowViewletAction { Registry.as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(DirtyDiffWorkbenchController, LifecyclePhase.Restored); -Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( +Registry.as(ViewletExtensions.Viewlets).registerViewlet(ViewletDescriptor.create( SCMViewlet, VIEWLET_ID, localize('source control', "Source Control"), diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 07e88ed2034..22f4f3467de 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -502,7 +502,7 @@ class ShowAllSymbolsAction extends Action { } } -Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( +Registry.as(ViewletExtensions.Viewlets).registerViewlet(ViewletDescriptor.create( SearchViewlet, VIEWLET_ID, nls.localize('name', "Search"), @@ -510,7 +510,7 @@ Registry.as(ViewletExtensions.Viewlets).registerViewlet(new Vie 1 )); -Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( +Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create( SearchPanel, PANEL_ID, nls.localize('name', "Search"), @@ -530,7 +530,7 @@ class RegisterSearchViewContribution implements IWorkbenchContribution { const config = configurationService.getValue(); if (config.search.location === 'panel') { viewsRegistry.deregisterViews(viewsRegistry.getViews(VIEW_CONTAINER), VIEW_CONTAINER); - Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( + Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create( SearchPanel, PANEL_ID, nls.localize('name', "Search"), @@ -626,7 +626,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.create(ClearSearchResultsA // Register Quick Open Handler Registry.as(QuickOpenExtensions.Quickopen).registerDefaultQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( OpenAnythingHandler, OpenAnythingHandler.ID, '', @@ -636,7 +636,7 @@ Registry.as(QuickOpenExtensions.Quickopen).registerDefaultQu ); Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( OpenSymbolHandler, OpenSymbolHandler.ID, ShowAllSymbolsAction.ALL_SYMBOLS_PREFIX, diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index 9e352e4b9ce..a01d38e521f 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -254,7 +254,7 @@ const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Q const tasksPickerContextKey = 'inTasksPicker'; quickOpenRegistry.registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( QuickOpenHandler, QuickOpenHandler.ID, 'task ', diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 81eb57fddd6..01699c08006 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -48,7 +48,7 @@ const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Q const inTerminalsPicker = 'inTerminalPicker'; quickOpenRegistry.registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( + QuickOpenHandlerDescriptor.create( TerminalPickerHandler, TerminalPickerHandler.ID, TERMINAL_PICKER_PREFIX, diff --git a/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts b/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts index 358e503f4ad..1a716bb0180 100644 --- a/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts +++ b/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts @@ -207,7 +207,7 @@ class TestCustomEditorModel extends EditorModel { if (ENABLE) { Registry.as(EditorExtensions.Editors).registerEditor( - new EditorDescriptor( + EditorDescriptor.create( TestCustomEditor, TestCustomEditor.ID, nls.localize('testCustomEditor', "Test Custom Editor") diff --git a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts index 445f190e1b2..f7339e7f206 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts @@ -21,7 +21,7 @@ import { WebviewEditor } from '../browser/webviewEditor'; import { WebviewInput } from '../browser/webviewEditorInput'; import { IWebviewWorkbenchService, WebviewEditorService } from './webviewWorkbenchService'; -(Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor( +(Registry.as(EditorExtensions.Editors)).registerEditor(EditorDescriptor.create( WebviewEditor, WebviewEditor.ID, localize('webview.editor.label', "webview 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 0538dd38b27..dfd947a4a7f 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution.ts @@ -20,7 +20,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; Registry.as(EditorExtensions.Editors) - .registerEditor(new EditorDescriptor( + .registerEditor(EditorDescriptor.create( WalkThroughPart, WalkThroughPart.ID, localize('walkThrough.editor.label', "Interactive Playground"), diff --git a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts index 98e93a57adb..ca86ecb8744 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -78,7 +78,7 @@ suite('EditorGroupsService', () => { } (Registry.as(EditorExtensions.EditorInputFactories)).registerEditorInputFactory('testEditorInputForGroupsService', TestEditorInputFactory); - (Registry.as(Extensions.Editors)).registerEditor(new EditorDescriptor(TestEditorControl, 'MyTestEditorForGroupsService', 'My Test File Editor'), [new SyncDescriptor(TestEditorInput)]); + (Registry.as(Extensions.Editors)).registerEditor(EditorDescriptor.create(TestEditorControl, 'MyTestEditorForGroupsService', 'My Test File Editor'), [new SyncDescriptor(TestEditorInput)]); } registerTestEditorInput(); diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index 9bfe2984f58..ad798f5ce43 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -83,7 +83,7 @@ class FileServiceProvider extends Disposable { suite('EditorService', () => { function registerTestEditorInput(): void { - Registry.as(Extensions.Editors).registerEditor(new EditorDescriptor(TestEditorControl, 'MyTestEditorForEditorService', 'My Test Editor For Next Editor Service'), [new SyncDescriptor(TestEditorInput)]); + Registry.as(Extensions.Editors).registerEditor(EditorDescriptor.create(TestEditorControl, 'MyTestEditorForEditorService', 'My Test Editor For Next Editor Service'), [new SyncDescriptor(TestEditorInput)]); } registerTestEditorInput(); diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts index 8e9d984ffe5..d75e7af6977 100644 --- a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts @@ -115,14 +115,14 @@ suite('Workbench base editor', () => { }); test('EditorDescriptor', () => { - let d = new EditorDescriptor(MyEditor, 'id', 'name'); + let d = EditorDescriptor.create(MyEditor, 'id', 'name'); assert.strictEqual(d.getId(), 'id'); assert.strictEqual(d.getName(), 'name'); }); test('Editor Registration', function () { - let d1 = new EditorDescriptor(MyEditor, 'id1', 'name'); - let d2 = new EditorDescriptor(MyOtherEditor, 'id2', 'name'); + let d1 = EditorDescriptor.create(MyEditor, 'id1', 'name'); + let d2 = EditorDescriptor.create(MyOtherEditor, 'id2', 'name'); let oldEditorsCnt = EditorRegistry.getEditors().length; let oldInputCnt = (EditorRegistry).getEditorInputs().length; @@ -142,8 +142,8 @@ suite('Workbench base editor', () => { }); test('Editor Lookup favors specific class over superclass (match on specific class)', function () { - let d1 = new EditorDescriptor(MyEditor, 'id1', 'name'); - let d2 = new EditorDescriptor(MyOtherEditor, 'id2', 'name'); + let d1 = EditorDescriptor.create(MyEditor, 'id1', 'name'); + let d2 = EditorDescriptor.create(MyOtherEditor, 'id2', 'name'); let oldEditors = EditorRegistry.getEditors(); (EditorRegistry).setEditors([]); @@ -163,7 +163,7 @@ suite('Workbench base editor', () => { }); test('Editor Lookup favors specific class over superclass (match on super class)', function () { - let d1 = new EditorDescriptor(MyOtherEditor, 'id1', 'name'); + let d1 = EditorDescriptor.create(MyOtherEditor, 'id1', 'name'); let oldEditors = EditorRegistry.getEditors(); (EditorRegistry).setEditors([]); diff --git a/src/vs/workbench/test/browser/quickopen.test.ts b/src/vs/workbench/test/browser/quickopen.test.ts index 1a264a7e25c..696bb972574 100644 --- a/src/vs/workbench/test/browser/quickopen.test.ts +++ b/src/vs/workbench/test/browser/quickopen.test.ts @@ -54,7 +54,7 @@ suite('QuickOpen', () => { test('QuickOpen Handler and Registry', () => { let registry = (Registry.as(QuickOpenExtensions.Quickopen)); - let handler = new QuickOpenHandlerDescriptor( + let handler = QuickOpenHandlerDescriptor.create( TestHandler, 'testhandler', ',', @@ -77,4 +77,4 @@ suite('QuickOpen', () => { defaultAction.run(); prefixAction.run(); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/test/browser/viewlet.test.ts b/src/vs/workbench/test/browser/viewlet.test.ts index 6545d38ced2..2698de89738 100644 --- a/src/vs/workbench/test/browser/viewlet.test.ts +++ b/src/vs/workbench/test/browser/viewlet.test.ts @@ -22,7 +22,7 @@ suite('Viewlets', () => { } test('ViewletDescriptor API', function () { - let d = new ViewletDescriptor(TestViewlet, 'id', 'name', 'class', 5); + let d = ViewletDescriptor.create(TestViewlet, 'id', 'name', 'class', 5); assert.strictEqual(d.id, 'id'); assert.strictEqual(d.name, 'name'); assert.strictEqual(d.cssClass, 'class'); @@ -30,11 +30,11 @@ suite('Viewlets', () => { }); test('Editor Aware ViewletDescriptor API', function () { - let d = new ViewletDescriptor(TestViewlet, 'id', 'name', 'class', 5); + let d = ViewletDescriptor.create(TestViewlet, 'id', 'name', 'class', 5); assert.strictEqual(d.id, 'id'); assert.strictEqual(d.name, 'name'); - d = new ViewletDescriptor(TestViewlet, 'id', 'name', 'class', 5); + d = ViewletDescriptor.create(TestViewlet, 'id', 'name', 'class', 5); assert.strictEqual(d.id, 'id'); assert.strictEqual(d.name, 'name'); }); @@ -45,7 +45,7 @@ suite('Viewlets', () => { assert(Types.isFunction(Platform.Registry.as(Extensions.Viewlets).getViewlets)); let oldCount = Platform.Registry.as(Extensions.Viewlets).getViewlets().length; - let d = new ViewletDescriptor(TestViewlet, 'reg-test-id', 'name'); + let d = ViewletDescriptor.create(TestViewlet, 'reg-test-id', 'name'); Platform.Registry.as(Extensions.Viewlets).registerViewlet(d); assert(d === Platform.Registry.as(Extensions.Viewlets).getViewlet('reg-test-id')); From 23947ea1ffeafb94cab47c9cc6d21f56460eb029 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Nov 2019 17:27:18 -0800 Subject: [PATCH 40/88] Add strict function type task For #81574 Replaces the strict property task since this is no longer needed --- .vscode/tasks.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index d1a697bb41d..598d82214ce 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -33,15 +33,15 @@ }, { "type": "npm", - "script": "strict-initialization-watch", - "label": "TS - Strict Initialization", + "script": "strict-function-types-watch", + "label": "TS - Strict Function Types", "isBackground": true, "presentation": { "reveal": "never" }, "problemMatcher": { "base": "$tsc-watch", - "owner": "typescript-strict-initialization", + "owner": "typescript-function-types", "applyTo": "allDocuments" } }, diff --git a/package.json b/package.json index ec7cd7ba956..10caabb82ad 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "smoketest": "cd test/smoke && node test/index.js", "download-builtin-extensions": "node build/lib/builtInExtensions.js", "monaco-compile-check": "tsc -p src/tsconfig.monaco.json --noEmit", - "strict-initialization-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictPropertyInitialization", + "strict-function-types-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictFunctionTypes", "update-distro": "node build/npm/update-distro.js", "web": "node scripts/code-web.js" }, From d420e20fcd4b4bd9312cfd95cba2522f29b7ff27 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Nov 2019 17:35:14 -0800 Subject: [PATCH 41/88] Fix implicit casts in tests For #81574 --- .../codeAction/test/codeActionModel.test.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts index c2ef4493562..e394f6838c1 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { assertType } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Selection } from 'vs/editor/common/core/selection'; @@ -55,8 +56,10 @@ suite('CodeActionModel', () => { const contextKeys = new MockContextKeyService(); const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); - disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => { - assert.equal(e.trigger.type, 'auto'); + disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { + assertType(e.type === CodeActionsState.Type.Triggered); + + assert.strictEqual(e.trigger.type, 'auto'); assert.ok(e.actions); e.actions.then(fixes => { @@ -94,7 +97,9 @@ suite('CodeActionModel', () => { return new Promise((resolve, reject) => { const contextKeys = new MockContextKeyService(); const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); - disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => { + disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { + assertType(e.type === CodeActionsState.Type.Triggered); + assert.equal(e.trigger.type, 'auto'); assert.ok(e.actions); e.actions.then(fixes => { @@ -130,7 +135,9 @@ suite('CodeActionModel', () => { await new Promise(resolve => { const contextKeys = new MockContextKeyService(); const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); - disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => { + disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { + assertType(e.type === CodeActionsState.Type.Triggered); + assert.equal(e.trigger.type, 'auto'); const selection = e.rangeOrSelection; assert.deepEqual(selection.selectionStartLineNumber, 1); @@ -153,7 +160,9 @@ suite('CodeActionModel', () => { let triggerCount = 0; const contextKeys = new MockContextKeyService(); const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); - disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => { + disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { + assertType(e.type === CodeActionsState.Type.Triggered); + assert.equal(e.trigger.type, 'auto'); ++triggerCount; From 87387677d31efe0914ab65c19273ee13c814c108 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Nov 2019 17:37:24 -0800 Subject: [PATCH 42/88] Fix strict function types for registerEditorInputFactory #81574 --- src/vs/workbench/common/editor.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index e2dcb62669c..1764e7ed832 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IEditor as ICodeEditor, IEditorViewState, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon'; import { IEditorModel, IEditorOptions, ITextEditorOptions, IBaseResourceInput, IResourceInput, EditorActivation, EditorOpenContext } from 'vs/platform/editor/common/editor'; -import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, IConstructorSignature0, ServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITextModel } from 'vs/editor/common/model'; @@ -175,7 +175,7 @@ export interface IEditorInputFactoryRegistry { * @param editorInputId the identifier of the editor input * @param factory the editor input factory for serialization/deserialization */ - registerEditorInputFactory(editorInputId: string, ctor: IConstructorSignature0): void; + registerEditorInputFactory(editorInputId: string, ctor: { new(...Services: Services): IEditorInputFactory }): void; /** * Returns the editor input factory for the given editor input. From bf7d03bf88732af0b46e473c663c49e18e839f9c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Nov 2019 17:52:02 -0800 Subject: [PATCH 43/88] Fix one reference to new PanelDescriptor that should have been converted to create --- .../workbench/contrib/terminal/browser/terminal.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 01699c08006..f166e7aacdd 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -331,7 +331,7 @@ registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenTermAction const actionBarRegistry = Registry.as(ActionBarExtensions.Actionbar); actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionTermContributor); -(Registry.as(panel.Extensions.Panels)).registerPanel(new panel.PanelDescriptor( +(Registry.as(panel.Extensions.Panels)).registerPanel(panel.PanelDescriptor.create( TerminalPanel, TERMINAL_PANEL_ID, nls.localize('terminal', "Terminal"), From bfe5a684238b119d913f6a4b052cca9f8dc5704f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Nov 2019 08:38:25 +0100 Subject: [PATCH 44/88] use shared CSS-style element instead of repeated inline styles, only access dom when having contents, #84726 --- .../contrib/codelens/codelensController.ts | 33 ++++++++++++++----- .../contrib/codelens/codelensWidget.css | 4 --- .../editor/contrib/codelens/codelensWidget.ts | 33 ++++++------------- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index b50d4508fe3..6c259a207c0 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -18,6 +18,8 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ICodeLensCache } from 'vs/editor/contrib/codelens/codeLensCache'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { createStyleSheet } from 'vs/base/browser/dom'; +import { hash } from 'vs/base/common/hash'; export class CodeLensContribution implements editorCommon.IEditorContribution { @@ -27,6 +29,8 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { private readonly _globalToDispose = new DisposableStore(); private readonly _localToDispose = new DisposableStore(); + private readonly _styleElement: HTMLStyleElement; + private readonly _styleClassName: string; private _lenses: CodeLensWidget[] = []; private _currentFindCodeLensSymbolsPromise: CancelablePromise | undefined; private _oldCodeLensModels = new DisposableStore(); @@ -53,7 +57,16 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { } })); this._globalToDispose.add(CodeLensProviderRegistry.onDidChange(this._onModelChange, this)); + this._globalToDispose.add(this._editor.onDidChangeConfiguration(e => { + if (e.hasChanged(EditorOption.fontInfo)) { + this._updateLensStyle(); + } + })); this._onModelChange(); + + this._styleClassName = hash(this._editor.getId()).toString(16); + this._styleElement = createStyleSheet(); + this._updateLensStyle(); } dispose(): void { @@ -63,6 +76,15 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { dispose(this._currentCodeLensModel); } + private _updateLensStyle(): void { + const options = this._editor.getOptions(); + const fontInfo = options.get(EditorOption.fontInfo); + const lineHeight = options.get(EditorOption.lineHeight); + + const newStyle = `.monaco-editor .codelens-decoration.${this._styleClassName} { height: ${Math.round(lineHeight * 1.1)}px; line-height: ${lineHeight}px; font-size: ${Math.round(fontInfo.fontSize * 0.9)}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;}`; + this._styleElement.innerHTML = newStyle; + } + private _localDispose(): void { if (this._currentFindCodeLensSymbolsPromise) { this._currentFindCodeLensSymbolsPromise.cancel(); @@ -200,13 +222,6 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { this._disposeAllLenses(undefined, undefined); } })); - this._localToDispose.add(this._editor.onDidChangeConfiguration(e => { - if (e.hasChanged(EditorOption.fontInfo)) { - for (const lens of this._lenses) { - lens.updateHeight(); - } - } - })); this._localToDispose.add(this._editor.onMouseUp(e => { if (e.target.type === editorBrowser.MouseTargetType.CONTENT_WIDGET && e.target.element && e.target.element.tagName === 'A') { for (const lens of this._lenses) { @@ -278,7 +293,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 && this._detectVisibleLenses.schedule())); + this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule())); codeLensIndex++; groupsIndex++; } @@ -292,7 +307,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 && this._detectVisibleLenses.schedule())); + this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule())); groupsIndex++; } diff --git a/src/vs/editor/contrib/codelens/codelensWidget.css b/src/vs/editor/contrib/codelens/codelensWidget.css index e266cf8b6a8..031a0558f25 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.css +++ b/src/vs/editor/contrib/codelens/codelensWidget.css @@ -27,10 +27,6 @@ cursor: pointer; } -.monaco-editor .codelens-decoration.invisible-cl { - opacity: 0; -} - @keyframes fadein { 0% { opacity: 0; visibility: visible;} 100% { opacity: 1; } diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index 546cdfd028e..fe1e9f2f2e0 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -15,7 +15,6 @@ import { editorCodeLensForeground } from 'vs/editor/common/view/editorColorRegis import { CodeLensItem } from 'vs/editor/contrib/codelens/codelens'; import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; class CodeLensViewZone implements editorBrowser.IViewZone { @@ -61,33 +60,24 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { private readonly _commands = new Map(); private _widgetPosition?: editorBrowser.IContentWidgetPosition; + private _isEmpty: boolean = true; constructor( editor: editorBrowser.ICodeEditor, + className: string, symbolRange: Range, lenses: Array ) { - this._id = 'codeLensWidget' + (++CodeLensContentWidget._idPool); this._editor = editor; + this._id = (CodeLensContentWidget._idPool++).toString(); this.setSymbolRange(symbolRange); this._domNode = document.createElement('span'); - this._domNode.className = 'codelens-decoration'; - this.updateHeight(); + this._domNode.className = `codelens-decoration ${className}`; this.withCommands(lenses, false); } - updateHeight(): void { - const options = this._editor.getOptions(); - const fontInfo = options.get(EditorOption.fontInfo); - const lineHeight = options.get(EditorOption.lineHeight); - this._domNode.style.height = `${Math.round(lineHeight * 1.1)}px`; - this._domNode.style.lineHeight = `${lineHeight}px`; - this._domNode.style.fontSize = `${Math.round(fontInfo.fontSize * 0.9)}px`; - this._domNode.style.paddingRight = `${Math.round(fontInfo.fontSize * 0.45)}px`; - } - withCommands(lenses: Array, animate: boolean): void { this._commands.clear(); @@ -117,14 +107,14 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { // symbols but no commands this._domNode.innerHTML = 'no commands'; - } else { + } else if (innerHtml) { // symbols and commands - const wasEmpty = this._domNode.innerHTML === '' || this._domNode.innerHTML === ' '; - this._domNode.innerHTML = innerHtml || ' '; + this._domNode.innerHTML = innerHtml; this._editor.layoutContentWidget(this); - if (wasEmpty && animate) { + if (this._isEmpty && animate) { dom.addClass(this._domNode, 'fadein'); } + this._isEmpty = false; } } @@ -208,6 +198,7 @@ export class CodeLensWidget { constructor( data: CodeLensItem[], editor: editorBrowser.ICodeEditor, + className: string, helper: CodeLensHelper, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor, updateCallback: Function @@ -236,7 +227,7 @@ export class CodeLensWidget { }); if (range) { - this._contentWidget = new CodeLensContentWidget(editor, range, lenses); + this._contentWidget = new CodeLensContentWidget(editor, className, range, lenses); this._viewZone = new CodeLensViewZone(range.startLineNumber - 1, updateCallback); this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone); @@ -306,10 +297,6 @@ export class CodeLensWidget { } } - updateHeight(): void { - this._contentWidget.updateHeight(); - } - getCommand(link: HTMLLinkElement): Command | undefined { return this._contentWidget.getCommand(link); } From 03ef538d3d6603a2cf9e3d8bebaaa288b243da00 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Nov 2019 09:09:21 +0100 Subject: [PATCH 45/88] code lens must not be empty... #84726 --- src/vs/editor/contrib/codelens/codelensWidget.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index fe1e9f2f2e0..54318b98678 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -107,8 +107,11 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { // symbols but no commands this._domNode.innerHTML = 'no commands'; - } else if (innerHtml) { + } else { // symbols and commands + if (!innerHtml) { + innerHtml = ' '; + } this._domNode.innerHTML = innerHtml; this._editor.layoutContentWidget(this); if (this._isEmpty && animate) { From e97c0c2070eec5bd953750cef0cc8ee2166bb7d8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Nov 2019 09:20:33 +0100 Subject: [PATCH 46/88] fix #84787 --- src/vs/base/common/filters.ts | 2 +- src/vs/base/test/common/filters.test.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 5e3150d3308..8e8fd433fb2 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -543,7 +543,7 @@ export function fuzzyScore(pattern: string, patternLow: string, patternStart: nu const patternLen = pattern.length > _maxLen ? _maxLen : pattern.length; const wordLen = word.length > _maxLen ? _maxLen : word.length; - if (patternStart >= patternLen || wordStart >= wordLen || patternLen > wordLen) { + if (patternStart >= patternLen || wordStart >= wordLen || (patternLen - patternStart) > (wordLen - wordStart)) { return undefined; } diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index 6f96e22f47e..f41838588a5 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -498,4 +498,9 @@ suite('Filters', () => { fuzzyScore ); }); + + test('"Go to Symbol" with the exact method name doesn\'t work as expected #84787', function () { + const match = fuzzyScore(':get', ':get', 1, 'get', 'get', 0, true); + assert.ok(Boolean(match)); + }); }); From 0083c78aa4c906d9ac0b8fe91e1a548313dc9678 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Nov 2019 09:28:21 +0100 Subject: [PATCH 47/88] Revert "add URI.from(url)-overload" This reverts commit e24fefe0a89205ce519efd8bd4ee53bde4bfe90c. --- src/vs/base/common/uri.ts | 9 +-------- src/vs/monaco.d.ts | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 3df1ee149e2..19f89eec626 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -323,14 +323,7 @@ export class URI implements UriComponents { return new _URI('file', authority, path, _empty, _empty); } - static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string } | URL): URI { - - if (components instanceof URL) { - const value = <_URI>_URI.parse(components.href); - value._formatted = components.href; - return value; - } - + static from(components: { scheme: string; authority?: string; path?: string; query?: string; fragment?: string }): URI { return new _URI( components.scheme, components.authority, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index ff535f4bfbc..2fa74f0ac35 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -160,7 +160,7 @@ declare namespace monaco { path?: string; query?: string; fragment?: string; - } | URL): Uri; + }): Uri; /** * Creates a string representation for this Uri. It's guaranteed that calling * `Uri.parse` with the result of this function creates an Uri which is equal From ee3a3406c639bffdc48bc0eefd7d7c5414771143 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Nov 2019 09:38:41 +0100 Subject: [PATCH 48/88] lcd - put tabs on their own layer to enable proper rendering --- .../parts/editor/media/editorgroupview.css | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css index 582eac233e2..15fb7042231 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css +++ b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css @@ -48,6 +48,21 @@ overflow: hidden; } +.monaco-workbench.windows .part.editor > .content .editor-group-container > .title, +.monaco-workbench.linux .part.editor > .content .editor-group-container > .title, +.monaco-workbench.web .part.editor > .content .editor-group-container > .title { + /* + * Explicitly put the part onto its own layer to help Chrome to + * render the content with LCD-anti-aliasing. By partioning the + * workbench into multiple layers, we can ensure that a bad + * behaving part is not making another part fallback to greyscale + * rendering. + * + * macOS: does not render LCD-anti-aliased. + */ + transform: translate3d(0px, 0px, 0px); +} + .monaco-workbench .part.editor > .content .editor-group-container > .title:not(.tabs) { display: flex; /* when tabs are not shown, use flex layout */ flex-wrap: nowrap; From 655df8fe0d4e8d83ebe5a796d4f1203dfdba81e0 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 15 Nov 2019 10:05:32 +0100 Subject: [PATCH 49/88] Make sure tha allViews are ready in remote explorer Part of https://github.com/microsoft/vscode-remote-release/issues/1847 --- .../browser/parts/views/viewsViewlet.ts | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 874677282dd..f934a70b2dd 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -350,22 +350,26 @@ export abstract class FilterViewContainerViewlet extends ViewContainerViewlet { })); this._register(this.viewsModel.onDidChangeActiveViews((viewDescriptors) => { - viewDescriptors.forEach(descriptor => { - let filterOnValue = this.getFilterOn(descriptor); - if (!filterOnValue) { - return; - } - if (!this.allViews.has(filterOnValue)) { - this.allViews.set(filterOnValue, new Map()); - } - this.allViews.get(filterOnValue)!.set(descriptor.id, descriptor); - if (filterOnValue !== this.filterValue) { - this.viewsModel.setVisible(descriptor.id, false); - } - }); + this.updateAllViews(viewDescriptors); })); } + private updateAllViews(viewDescriptors: IViewDescriptor[]) { + viewDescriptors.forEach(descriptor => { + let filterOnValue = this.getFilterOn(descriptor); + if (!filterOnValue) { + return; + } + if (!this.allViews.has(filterOnValue)) { + this.allViews.set(filterOnValue, new Map()); + } + this.allViews.get(filterOnValue)!.set(descriptor.id, descriptor); + if (filterOnValue !== this.filterValue) { + this.viewsModel.setVisible(descriptor.id, false); + } + }); + } + protected addConstantViewDescriptors(constantViewDescriptors: IViewDescriptor[]) { constantViewDescriptors.forEach(viewDescriptor => this.constantViewDescriptors.set(viewDescriptor.id, viewDescriptor)); } @@ -415,6 +419,10 @@ export abstract class FilterViewContainerViewlet extends ViewContainerViewlet { } onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] { + // Check that allViews is ready + if (this.allViews.size === 0) { + this.updateAllViews(this.viewsModel.viewDescriptors); + } const panels: ViewletPanel[] = super.onDidAddViews(added); for (let i = 0; i < added.length; i++) { if (this.constantViewDescriptors.has(added[i].viewDescriptor.id)) { From f651ccac7e2806d8f8c57c47429293c2435c2200 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Nov 2019 10:13:48 +0100 Subject: [PATCH 50/88] extract openers --- .../editor/browser/services/openerService.ts | 143 +++++++++--------- 1 file changed, 71 insertions(+), 72 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index b92275c5cf0..ead9bd31bfb 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -16,6 +16,71 @@ import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, import { EditorOpenContext } from 'vs/platform/editor/common/editor'; +class CommandOpener implements IOpener { + + constructor(@ICommandService private readonly _commandService: ICommandService) { } + + async open(target: URI | string) { + if (!matchesScheme(target, Schemas.command)) { + return false; + } + // run command or bail out if command isn't known + if (typeof target === 'string') { + target = URI.parse(target); + } + if (!CommandsRegistry.getCommand(target.path)) { + throw new Error(`command '${target.path}' NOT known`); + } + // execute as command + let args: any = []; + try { + args = parse(target.query); + if (!Array.isArray(args)) { + args = [args]; + } + } catch (e) { + // ignore error + } + await this._commandService.executeCommand(target.path, ...args); + return true; + } +} + +class EditorOpener implements IOpener { + + constructor(@ICodeEditorService private readonly _editorService: ICodeEditorService) { } + + async open(target: URI | string, options: OpenOptions) { + if (typeof target === 'string') { + target = URI.parse(target); + } + let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined; + const match = /^L?(\d+)(?:,(\d+))?/.exec(target.fragment); + if (match) { + // support file:///some/file.js#73,84 + // support file:///some/file.js#L73 + selection = { + startLineNumber: parseInt(match[1]), + startColumn: match[2] ? parseInt(match[2]) : 1 + }; + // remove fragment + target = target.with({ fragment: '' }); + } + + if (target.scheme === Schemas.file) { + target = normalizePath(target); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) + } + + await this._editorService.openCodeEditor( + { resource: target, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } }, + this._editorService.getFocusedCodeEditor(), + options?.openToSide + ); + + return true; + } +} + export class OpenerService implements IOpenerService { _serviceBrand: undefined; @@ -25,9 +90,6 @@ export class OpenerService implements IOpenerService { private readonly _resolvers = new LinkedList(); private _externalOpener: IExternalOpener; - private _openerAsExternal: IOpener; - private _openerAsCommand: IOpener; - private _openerAsEditor: IOpener; constructor( @ICodeEditorService editorService: ICodeEditorService, @@ -42,7 +104,7 @@ export class OpenerService implements IOpenerService { }; // Default opener: maito, http(s), command, and catch-all-editors - this._openerAsExternal = { + this._openers.push({ open: async (target: URI | string, options?: OpenOptions) => { if (options?.openExternal || matchesScheme(target, Schemas.mailto) || matchesScheme(target, Schemas.http) || matchesScheme(target, Schemas.https)) { // open externally @@ -51,70 +113,13 @@ export class OpenerService implements IOpenerService { } return false; } - }; - - this._openerAsCommand = { - open: async (target) => { - if (!matchesScheme(target, Schemas.command)) { - return false; - } - // run command or bail out if command isn't known - if (typeof target === 'string') { - target = URI.parse(target); - } - if (!CommandsRegistry.getCommand(target.path)) { - throw new Error(`command '${target.path}' NOT known`); - } - // execute as command - let args: any = []; - try { - args = parse(target.query); - if (!Array.isArray(args)) { - args = [args]; - } - } catch (e) { - // ignore error - } - await commandService.executeCommand(target.path, ...args); - return true; - } - }; - - this._openerAsEditor = { - open: async (target, options: OpenOptions) => { - if (typeof target === 'string') { - target = URI.parse(target); - } - let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined; - const match = /^L?(\d+)(?:,(\d+))?/.exec(target.fragment); - if (match) { - // support file:///some/file.js#73,84 - // support file:///some/file.js#L73 - selection = { - startLineNumber: parseInt(match[1]), - startColumn: match[2] ? parseInt(match[2]) : 1 - }; - // remove fragment - target = target.with({ fragment: '' }); - } - - if (target.scheme === Schemas.file) { - target = normalizePath(target); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) - } - - await editorService.openCodeEditor( - { resource: target, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } }, - editorService.getFocusedCodeEditor(), - options?.openToSide - ); - - return true; - } - }; + }); + this._openers.push(new CommandOpener(commandService)); + this._openers.push(new EditorOpener(editorService)); } registerOpener(opener: IOpener): IDisposable { - const remove = this._openers.push(opener); + const remove = this._openers.unshift(opener); return { dispose: remove }; } @@ -149,13 +154,7 @@ export class OpenerService implements IOpenerService { } } - // use default openers - for (const opener of [this._openerAsExternal, this._openerAsCommand, this._openerAsEditor]) { - if (await opener.open(target, options)) { - break; - } - } - return true; + return false; } async resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise { From 7a4a4deffcebaa826d57dd828527a1e153deda34 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 15 Nov 2019 10:14:35 +0100 Subject: [PATCH 51/88] New window uses old theme. Fixes #84539 --- .../splash/electron-browser/partsSplash.contribution.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts index 7e511a8449d..15a23398c98 100644 --- a/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts +++ b/src/vs/workbench/contrib/splash/electron-browser/partsSplash.contribution.ts @@ -60,10 +60,12 @@ class PartsSplash { if (e.affectsConfiguration('window.titleBarStyle')) { this._didChangeTitleBarStyle = true; this._savePartsSplash(); - } else if (e.affectsConfiguration('workbench.colorTheme') || e.affectsConfiguration('workbench.colorCustomizations')) { - this._savePartsSplash(); } }, this, this._disposables); + + _themeService.onThemeChange(_ => { + this._savePartsSplash(); + }, this, this._disposables); } dispose(): void { From 544b0abf5bc125d63a8c141d3268aa36d8cd4f92 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Nov 2019 10:28:03 +0100 Subject: [PATCH 52/88] allow $openUri to accept a URI and string, adopt consumer but keep the API as is --- .../workbench/api/browser/mainThreadWindow.ts | 24 ++++++++++++++++--- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostRequireInterceptor.ts | 12 ++++++---- src/vs/workbench/api/common/extHostWindow.ts | 12 ---------- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWindow.ts b/src/vs/workbench/api/browser/mainThreadWindow.ts index bbce19a3388..e03411f1823 100644 --- a/src/vs/workbench/api/browser/mainThreadWindow.ts +++ b/src/vs/workbench/api/browser/mainThreadWindow.ts @@ -10,6 +10,8 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ExtHostContext, ExtHostWindowShape, IExtHostContext, IOpenUriOptions, MainContext, MainThreadWindowShape } from '../common/extHost.protocol'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { Schemas } from 'vs/base/common/network'; +import { isFalsyOrWhitespace } from 'vs/base/common/strings'; @extHostNamedCustomer(MainContext.MainThreadWindow) export class MainThreadWindow implements MainThreadWindowShape { @@ -42,9 +44,25 @@ export class MainThreadWindow implements MainThreadWindowShape { return Promise.resolve(this.hostService.hasFocus); } - async $openUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise { - const uri = URI.from(uriComponents); - return this.openerService.open(uri, { openExternal: true, allowTunneling: options.allowTunneling }); + async $openUri(stringOrComp: UriComponents | string, options: IOpenUriOptions): Promise { + + const uri = typeof stringOrComp === 'string' + ? URI.parse(stringOrComp) + : URI.revive(stringOrComp); + + // validate + if (isFalsyOrWhitespace(uri.scheme)) { + return Promise.reject('Invalid scheme - cannot be empty'); + } else if (uri.scheme === Schemas.command) { + return Promise.reject(`Invalid scheme '${uri.scheme}'`); + } + + // open AS-IS, keep string alive + if (typeof stringOrComp === 'string') { + return this.openerService.open(stringOrComp, { openExternal: true, allowTunneling: options.allowTunneling }); + } else { + return this.openerService.open(uri, { openExternal: true, allowTunneling: options.allowTunneling }); + } } async $asExternalUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9bc433a1727..3ce80496914 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -759,7 +759,7 @@ export interface IOpenUriOptions { export interface MainThreadWindowShape extends IDisposable { $getWindowVisibility(): Promise; - $openUri(uri: UriComponents, options: IOpenUriOptions): Promise; + $openUri(uri: UriComponents | string, options: IOpenUriOptions): Promise; $asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise; } diff --git a/src/vs/workbench/api/common/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts index b6f94048fbd..6f8fc58d0b8 100644 --- a/src/vs/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -19,6 +19,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { platform } from 'vs/base/common/process'; import { ILogService } from 'vs/platform/log/common/log'; +import { matchesScheme } from 'vs/platform/opener/common/opener'; +import { Schemas } from 'vs/base/common/network'; interface LoadFunction { @@ -239,15 +241,15 @@ class OpenNodeModuleFactory implements INodeModuleFactory { const mainThreadWindow = rpcService.getProxy(MainContext.MainThreadWindow); this._impl = (target, options) => { - const uri: URI = URI.parse(target); // If we have options use the original method. if (options) { return this.callOriginal(target, options); } - if (uri.scheme === 'http' || uri.scheme === 'https') { - return mainThreadWindow.$openUri(uri, { allowTunneling: true }); - } else if (uri.scheme === 'mailto' || uri.scheme === this._appUriScheme) { - return mainThreadWindow.$openUri(uri, {}); + if (matchesScheme(target, Schemas.http) || matchesScheme(target, Schemas.https)) { + return mainThreadWindow.$openUri(target, { allowTunneling: true }); + + } else if (matchesScheme(target, Schemas.mailto) || matchesScheme(target, this._appUriScheme)) { + return mainThreadWindow.$openUri(target, {}); } return this.callOriginal(target, options); }; diff --git a/src/vs/workbench/api/common/extHostWindow.ts b/src/vs/workbench/api/common/extHostWindow.ts index 8ce82ac8b2d..d86a9203f3c 100644 --- a/src/vs/workbench/api/common/extHostWindow.ts +++ b/src/vs/workbench/api/common/extHostWindow.ts @@ -39,18 +39,6 @@ export class ExtHostWindow implements ExtHostWindowShape { } openUri(stringOrUri: string | URI, options: IOpenUriOptions): Promise { - if (typeof stringOrUri === 'string') { - try { - stringOrUri = URI.parse(stringOrUri); - } catch (e) { - return Promise.reject(`Invalid uri - '${stringOrUri}'`); - } - } - if (isFalsyOrWhitespace(stringOrUri.scheme)) { - return Promise.reject('Invalid scheme - cannot be empty'); - } else if (stringOrUri.scheme === Schemas.command) { - return Promise.reject(`Invalid scheme '${stringOrUri.scheme}'`); - } return this._proxy.$openUri(stringOrUri, options); } From e51ef85bcbcd1d4f9f9af274235de79890c09504 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Nov 2019 10:29:29 +0100 Subject: [PATCH 53/88] Revert "allow $openUri to accept a URI and string, adopt consumer but keep the API as is" This reverts commit 544b0abf5bc125d63a8c141d3268aa36d8cd4f92. --- .../workbench/api/browser/mainThreadWindow.ts | 24 +++---------------- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostRequireInterceptor.ts | 12 ++++------ src/vs/workbench/api/common/extHostWindow.ts | 12 ++++++++++ 4 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWindow.ts b/src/vs/workbench/api/browser/mainThreadWindow.ts index e03411f1823..bbce19a3388 100644 --- a/src/vs/workbench/api/browser/mainThreadWindow.ts +++ b/src/vs/workbench/api/browser/mainThreadWindow.ts @@ -10,8 +10,6 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ExtHostContext, ExtHostWindowShape, IExtHostContext, IOpenUriOptions, MainContext, MainThreadWindowShape } from '../common/extHost.protocol'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { Schemas } from 'vs/base/common/network'; -import { isFalsyOrWhitespace } from 'vs/base/common/strings'; @extHostNamedCustomer(MainContext.MainThreadWindow) export class MainThreadWindow implements MainThreadWindowShape { @@ -44,25 +42,9 @@ export class MainThreadWindow implements MainThreadWindowShape { return Promise.resolve(this.hostService.hasFocus); } - async $openUri(stringOrComp: UriComponents | string, options: IOpenUriOptions): Promise { - - const uri = typeof stringOrComp === 'string' - ? URI.parse(stringOrComp) - : URI.revive(stringOrComp); - - // validate - if (isFalsyOrWhitespace(uri.scheme)) { - return Promise.reject('Invalid scheme - cannot be empty'); - } else if (uri.scheme === Schemas.command) { - return Promise.reject(`Invalid scheme '${uri.scheme}'`); - } - - // open AS-IS, keep string alive - if (typeof stringOrComp === 'string') { - return this.openerService.open(stringOrComp, { openExternal: true, allowTunneling: options.allowTunneling }); - } else { - return this.openerService.open(uri, { openExternal: true, allowTunneling: options.allowTunneling }); - } + async $openUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise { + const uri = URI.from(uriComponents); + return this.openerService.open(uri, { openExternal: true, allowTunneling: options.allowTunneling }); } async $asExternalUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 3ce80496914..9bc433a1727 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -759,7 +759,7 @@ export interface IOpenUriOptions { export interface MainThreadWindowShape extends IDisposable { $getWindowVisibility(): Promise; - $openUri(uri: UriComponents | string, options: IOpenUriOptions): Promise; + $openUri(uri: UriComponents, options: IOpenUriOptions): Promise; $asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise; } diff --git a/src/vs/workbench/api/common/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts index 6f8fc58d0b8..b6f94048fbd 100644 --- a/src/vs/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -19,8 +19,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { platform } from 'vs/base/common/process'; import { ILogService } from 'vs/platform/log/common/log'; -import { matchesScheme } from 'vs/platform/opener/common/opener'; -import { Schemas } from 'vs/base/common/network'; interface LoadFunction { @@ -241,15 +239,15 @@ class OpenNodeModuleFactory implements INodeModuleFactory { const mainThreadWindow = rpcService.getProxy(MainContext.MainThreadWindow); this._impl = (target, options) => { + const uri: URI = URI.parse(target); // If we have options use the original method. if (options) { return this.callOriginal(target, options); } - if (matchesScheme(target, Schemas.http) || matchesScheme(target, Schemas.https)) { - return mainThreadWindow.$openUri(target, { allowTunneling: true }); - - } else if (matchesScheme(target, Schemas.mailto) || matchesScheme(target, this._appUriScheme)) { - return mainThreadWindow.$openUri(target, {}); + if (uri.scheme === 'http' || uri.scheme === 'https') { + return mainThreadWindow.$openUri(uri, { allowTunneling: true }); + } else if (uri.scheme === 'mailto' || uri.scheme === this._appUriScheme) { + return mainThreadWindow.$openUri(uri, {}); } return this.callOriginal(target, options); }; diff --git a/src/vs/workbench/api/common/extHostWindow.ts b/src/vs/workbench/api/common/extHostWindow.ts index d86a9203f3c..8ce82ac8b2d 100644 --- a/src/vs/workbench/api/common/extHostWindow.ts +++ b/src/vs/workbench/api/common/extHostWindow.ts @@ -39,6 +39,18 @@ export class ExtHostWindow implements ExtHostWindowShape { } openUri(stringOrUri: string | URI, options: IOpenUriOptions): Promise { + if (typeof stringOrUri === 'string') { + try { + stringOrUri = URI.parse(stringOrUri); + } catch (e) { + return Promise.reject(`Invalid uri - '${stringOrUri}'`); + } + } + if (isFalsyOrWhitespace(stringOrUri.scheme)) { + return Promise.reject('Invalid scheme - cannot be empty'); + } else if (stringOrUri.scheme === Schemas.command) { + return Promise.reject(`Invalid scheme '${stringOrUri.scheme}'`); + } return this._proxy.$openUri(stringOrUri, options); } From db07506b859efa01cfc6fef5133cd402acee20b2 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 15 Nov 2019 10:34:29 +0100 Subject: [PATCH 54/88] Move tests to use LinesLayout --- .../editor/common/viewLayout/linesLayout.ts | 64 +- .../viewLayout/whitespaceComputer.test.ts | 657 +++++++++--------- 2 files changed, 392 insertions(+), 329 deletions(-) diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index d6673ac8b8f..f95b15741bc 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -12,7 +12,6 @@ import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewMode * * These objects are basically either text (lines) or spaces between those lines (whitespaces). * This provides commodity operations for working with lines that contain whitespace that pushes lines lower (vertically). - * This is written with no knowledge of an editor in mind. */ export class LinesLayout { @@ -475,4 +474,67 @@ export class LinesLayout { public getWhitespaces(): IEditorWhitespace[] { return this._whitespaces.getWhitespaces(this._lineHeight); } + + /** + * The number of whitespaces. + */ + public getWhitespacesCount(): number { + return this._whitespaces.getCount(); + } + + /** + * Get the `id` for whitespace at index `index`. + * + * @param index The index of the whitespace. + * @return `id` of whitespace at `index`. + */ + public getIdForWhitespaceIndex(index: number): string { + return this._whitespaces.getIdForWhitespaceIndex(index); + } + + /** + * Get the `afterLineNumber` for whitespace at index `index`. + * + * @param index The index of the whitespace. + * @return `afterLineNumber` of whitespace at `index`. + */ + public getAfterLineNumberForWhitespaceIndex(index: number): number { + return this._whitespaces.getAfterLineNumberForWhitespaceIndex(index); + } + + /** + * Get the `height` for whitespace at index `index`. + * + * @param index The index of the whitespace. + * @return `height` of whitespace at `index`. + */ + public getHeightForWhitespaceIndex(index: number): number { + return this._whitespaces.getHeightForWhitespaceIndex(index); + } + + /** + * Find the index of the first whitespace which has `afterLineNumber` >= `lineNumber`. + * @return The index of the first whitespace with `afterLineNumber` >= `lineNumber` or -1 if no whitespace is found. + */ + public getFirstWhitespaceIndexAfterLineNumber(lineNumber: number): number { + return this._whitespaces.getFirstWhitespaceIndexAfterLineNumber(lineNumber); + } + + /** + * Return the sum of the heights of the whitespaces at [0..index]. + * This includes the whitespace at `index`. + * + * @param index The index of the whitespace. + * @return The sum of the heights of all whitespaces before the one at `index`, including the one at `index`. + */ + public getWhitespacesAccumulatedHeight(index: number): number { + return this._whitespaces.getAccumulatedHeight(index); + } + + /** + * Get the sum of all the whitespaces. + */ + public getWhitespacesTotalHeight(): number { + return this._whitespaces.getTotalHeight(); + } } diff --git a/src/vs/editor/test/common/viewLayout/whitespaceComputer.test.ts b/src/vs/editor/test/common/viewLayout/whitespaceComputer.test.ts index d9ade64f3a5..df7527d6b9b 100644 --- a/src/vs/editor/test/common/viewLayout/whitespaceComputer.test.ts +++ b/src/vs/editor/test/common/viewLayout/whitespaceComputer.test.ts @@ -4,245 +4,246 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { WhitespaceComputer } from 'vs/editor/common/viewLayout/whitespaceComputer'; +import { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout'; suite('Editor ViewLayout - WhitespaceComputer', () => { test('WhitespaceComputer', () => { - let whitespaceComputer = new WhitespaceComputer(); + const linesLayout = new LinesLayout(100, 20); // Insert a whitespace after line number 2, of height 10 - let a = whitespaceComputer.insertWhitespace(2, 0, 10, 0); + const a = linesLayout.insertWhitespace(2, 0, 10, 0); // whitespaces: a(2, 10) - assert.equal(whitespaceComputer.getCount(), 1); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10); - assert.equal(whitespaceComputer.getTotalHeight(), 10); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 10); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 10); + assert.equal(linesLayout.getWhitespacesCount(), 1); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 10); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 10); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 10); // Insert a whitespace again after line number 2, of height 20 - let b = whitespaceComputer.insertWhitespace(2, 0, 20, 0); + let b = linesLayout.insertWhitespace(2, 0, 20, 0); // whitespaces: a(2, 10), b(2, 20) - assert.equal(whitespaceComputer.getCount(), 2); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10); - assert.equal(whitespaceComputer.getAccumulatedHeight(1), 30); - assert.equal(whitespaceComputer.getTotalHeight(), 30); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 30); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 30); + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 30); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 30); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 30); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30); // Change last inserted whitespace height to 30 - whitespaceComputer.changeWhitespaceHeight(b, 30); + linesLayout.changeWhitespace(b, 2, 30); // whitespaces: a(2, 10), b(2, 30) - assert.equal(whitespaceComputer.getCount(), 2); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 30); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10); - assert.equal(whitespaceComputer.getAccumulatedHeight(1), 40); - assert.equal(whitespaceComputer.getTotalHeight(), 40); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 40); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 40); + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 30); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 40); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 40); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 40); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 40); // Remove last inserted whitespace - whitespaceComputer.removeWhitespace(b); + linesLayout.removeWhitespace(b); // whitespaces: a(2, 10) - assert.equal(whitespaceComputer.getCount(), 1); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 10); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 10); - assert.equal(whitespaceComputer.getTotalHeight(), 10); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 10); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 10); + assert.equal(linesLayout.getWhitespacesCount(), 1); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 10); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 10); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 10); // Add a whitespace before the first line of height 50 - b = whitespaceComputer.insertWhitespace(0, 0, 50, 0); + b = linesLayout.insertWhitespace(0, 0, 50, 0); // whitespaces: b(0, 50), a(2, 10) - assert.equal(whitespaceComputer.getCount(), 2); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 10); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50); - assert.equal(whitespaceComputer.getAccumulatedHeight(1), 60); - assert.equal(whitespaceComputer.getTotalHeight(), 60); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 60); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 60); + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 10); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 60); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 60); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 60); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 60); // Add a whitespace after line 4 of height 20 - whitespaceComputer.insertWhitespace(4, 0, 20, 0); + linesLayout.insertWhitespace(4, 0, 20, 0); // whitespaces: b(0, 50), a(2, 10), c(4, 20) - assert.equal(whitespaceComputer.getCount(), 3); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 10); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 4); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 20); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50); - assert.equal(whitespaceComputer.getAccumulatedHeight(1), 60); - assert.equal(whitespaceComputer.getAccumulatedHeight(2), 80); - assert.equal(whitespaceComputer.getTotalHeight(), 80); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 60); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 60); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 80); + assert.equal(linesLayout.getWhitespacesCount(), 3); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 10); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 4); + assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 60); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 80); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 80); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 60); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 60); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 80); // Add a whitespace after line 3 of height 30 - whitespaceComputer.insertWhitespace(3, 0, 30, 0); + linesLayout.insertWhitespace(3, 0, 30, 0); // whitespaces: b(0, 50), a(2, 10), d(3, 30), c(4, 20) - assert.equal(whitespaceComputer.getCount(), 4); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 10); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 3); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 30); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(3), 4); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(3), 20); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50); - assert.equal(whitespaceComputer.getAccumulatedHeight(1), 60); - assert.equal(whitespaceComputer.getAccumulatedHeight(2), 90); - assert.equal(whitespaceComputer.getAccumulatedHeight(3), 110); - assert.equal(whitespaceComputer.getTotalHeight(), 110); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 60); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 90); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 110); + assert.equal(linesLayout.getWhitespacesCount(), 4); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 10); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(3), 4); + assert.equal(linesLayout.getHeightForWhitespaceIndex(3), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 60); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 90); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(3), 110); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 110); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 60); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 90); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 110); // Change whitespace after line 2 to height of 100 - whitespaceComputer.changeWhitespaceHeight(a, 100); + linesLayout.changeWhitespace(a, 2, 100); // whitespaces: b(0, 50), a(2, 100), d(3, 30), c(4, 20) - assert.equal(whitespaceComputer.getCount(), 4); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 2); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 100); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 3); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 30); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(3), 4); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(3), 20); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50); - assert.equal(whitespaceComputer.getAccumulatedHeight(1), 150); - assert.equal(whitespaceComputer.getAccumulatedHeight(2), 180); - assert.equal(whitespaceComputer.getAccumulatedHeight(3), 200); - assert.equal(whitespaceComputer.getTotalHeight(), 200); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 150); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 180); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 200); + assert.equal(linesLayout.getWhitespacesCount(), 4); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 100); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(3), 4); + assert.equal(linesLayout.getHeightForWhitespaceIndex(3), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 150); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 180); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(3), 200); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 200); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 150); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 180); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 200); // Remove whitespace after line 2 - whitespaceComputer.removeWhitespace(a); + linesLayout.removeWhitespace(a); // whitespaces: b(0, 50), d(3, 30), c(4, 20) - assert.equal(whitespaceComputer.getCount(), 3); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 50); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 30); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 4); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(2), 20); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 50); - assert.equal(whitespaceComputer.getAccumulatedHeight(1), 80); - assert.equal(whitespaceComputer.getAccumulatedHeight(2), 100); - assert.equal(whitespaceComputer.getTotalHeight(), 100); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 80); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 100); + assert.equal(linesLayout.getWhitespacesCount(), 3); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 4); + assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 80); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 100); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 100); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 80); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 100); // Remove whitespace before line 1 - whitespaceComputer.removeWhitespace(b); + linesLayout.removeWhitespace(b); // whitespaces: d(3, 30), c(4, 20) - assert.equal(whitespaceComputer.getCount(), 2); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 4); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30); - assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50); - assert.equal(whitespaceComputer.getTotalHeight(), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 30); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50); + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 4); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50); // Delete line 1 - whitespaceComputer.onLinesDeleted(1, 1); + linesLayout.onLinesDeleted(1, 1); // whitespaces: d(2, 30), c(3, 20) - assert.equal(whitespaceComputer.getCount(), 2); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30); - assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50); - assert.equal(whitespaceComputer.getTotalHeight(), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 30); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50); + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 30); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50); // Insert a line before line 1 - whitespaceComputer.onLinesInserted(1, 1); + linesLayout.onLinesInserted(1, 1); // whitespaces: d(3, 30), c(4, 20) - assert.equal(whitespaceComputer.getCount(), 2); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 4); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30); - assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50); - assert.equal(whitespaceComputer.getTotalHeight(), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 30); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50); + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 4); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50); // Delete line 4 - whitespaceComputer.onLinesDeleted(4, 4); + linesLayout.onLinesDeleted(4, 4); // whitespaces: d(3, 30), c(3, 20) - assert.equal(whitespaceComputer.getCount(), 2); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(0), 30); - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(whitespaceComputer.getHeightForWhitespaceIndex(1), 20); - assert.equal(whitespaceComputer.getAccumulatedHeight(0), 30); - assert.equal(whitespaceComputer.getAccumulatedHeight(1), 50); - assert.equal(whitespaceComputer.getTotalHeight(), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(3), 0); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(4), 50); - assert.equal(whitespaceComputer.getAccumulatedHeightBeforeLineNumber(5), 50); + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50); }); test('WhitespaceComputer findInsertionIndex', () => { - let makeArray = (size: number, fillValue: number) => { + const makeArray = (size: number, fillValue: number) => { let r: number[] = []; for (let i = 0; i < size; i++) { r[i] = fillValue; @@ -375,184 +376,184 @@ suite('Editor ViewLayout - WhitespaceComputer', () => { }); test('WhitespaceComputer changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber', () => { - let whitespaceComputer = new WhitespaceComputer(); + const linesLayout = new LinesLayout(100, 20); - let a = whitespaceComputer.insertWhitespace(0, 0, 1, 0); - let b = whitespaceComputer.insertWhitespace(7, 0, 1, 0); - let c = whitespaceComputer.insertWhitespace(3, 0, 1, 0); + const a = linesLayout.insertWhitespace(0, 0, 1, 0); + const b = linesLayout.insertWhitespace(7, 0, 1, 0); + const c = linesLayout.insertWhitespace(3, 0, 1, 0); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 0); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 1); // c - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 1); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- // Do not really move a - whitespaceComputer.changeWhitespaceAfterLineNumber(a, 1); + linesLayout.changeWhitespace(a, 1, 1); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 1 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 1); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 1 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 1); + assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- // Do not really move a - whitespaceComputer.changeWhitespaceAfterLineNumber(a, 2); + linesLayout.changeWhitespace(a, 2, 1); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 2 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 2); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 2 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- // Change a to conflict with c => a gets placed after c - whitespaceComputer.changeWhitespaceAfterLineNumber(a, 3); + linesLayout.changeWhitespace(a, 3, 1); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), c); // 3 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), a); // 3 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), c); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(1), a); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- // Make a no-op - whitespaceComputer.changeWhitespaceAfterLineNumber(c, 3); + linesLayout.changeWhitespace(c, 3, 1); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), c); // 3 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), a); // 3 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), c); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(1), a); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- // Conflict c with b => c gets placed after b - whitespaceComputer.changeWhitespaceAfterLineNumber(c, 7); + linesLayout.changeWhitespace(c, 7, 1); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 3 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(0), 3); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), b); // 7 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(1), 7); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 7 - assert.equal(whitespaceComputer.getAfterLineNumberForWhitespaceIndex(2), 7); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(1), b); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 7); + assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(3), 0); // a - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(4), 1); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(5), 1); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(6), 1); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(7), 1); // b - assert.equal(whitespaceComputer.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 0); // a + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 1); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 1); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 1); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 1); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- }); test('WhitespaceComputer Bug', () => { - let whitespaceComputer = new WhitespaceComputer(); + const linesLayout = new LinesLayout(100, 20); - let a = whitespaceComputer.insertWhitespace(0, 0, 1, 0); - let b = whitespaceComputer.insertWhitespace(7, 0, 1, 0); + const a = linesLayout.insertWhitespace(0, 0, 1, 0); + const b = linesLayout.insertWhitespace(7, 0, 1, 0); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), b); // 7 + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), b); // 7 - let c = whitespaceComputer.insertWhitespace(3, 0, 1, 0); + const c = linesLayout.insertWhitespace(3, 0, 1, 0); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), c); // 3 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 + assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 - let d = whitespaceComputer.insertWhitespace(2, 0, 1, 0); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7 + const d = linesLayout.insertWhitespace(2, 0, 1, 0); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 + assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 + assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 - let e = whitespaceComputer.insertWhitespace(8, 0, 1, 0); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), e); // 8 + const e = linesLayout.insertWhitespace(8, 0, 1, 0); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 + assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 + assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 + assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8 - let f = whitespaceComputer.insertWhitespace(11, 0, 1, 0); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), e); // 8 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(5), f); // 11 + const f = linesLayout.insertWhitespace(11, 0, 1, 0); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 + assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 + assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 + assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8 + assert.equal(linesLayout.getIdForWhitespaceIndex(5), f); // 11 - let g = whitespaceComputer.insertWhitespace(10, 0, 1, 0); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), d); // 2 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), c); // 3 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), b); // 7 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), e); // 8 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(5), g); // 10 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(6), f); // 11 + const g = linesLayout.insertWhitespace(10, 0, 1, 0); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 + assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 + assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 + assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8 + assert.equal(linesLayout.getIdForWhitespaceIndex(5), g); // 10 + assert.equal(linesLayout.getIdForWhitespaceIndex(6), f); // 11 - let h = whitespaceComputer.insertWhitespace(0, 0, 1, 0); - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(1), h); // 0 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(2), d); // 2 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(3), c); // 3 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(4), b); // 7 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(5), e); // 8 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(6), g); // 10 - assert.equal(whitespaceComputer.getIdForWhitespaceIndex(7), f); // 11 + const h = linesLayout.insertWhitespace(0, 0, 1, 0); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), h); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(2), d); // 2 + assert.equal(linesLayout.getIdForWhitespaceIndex(3), c); // 3 + assert.equal(linesLayout.getIdForWhitespaceIndex(4), b); // 7 + assert.equal(linesLayout.getIdForWhitespaceIndex(5), e); // 8 + assert.equal(linesLayout.getIdForWhitespaceIndex(6), g); // 10 + assert.equal(linesLayout.getIdForWhitespaceIndex(7), f); // 11 }); }); From 6ff2985fba6675897df9e9951b1979c7ef4f78f2 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 15 Nov 2019 10:50:05 +0100 Subject: [PATCH 55/88] Merge WhitespaceComputer into LinesLayout --- src/vs/editor/browser/editorBrowser.ts | 2 +- .../editor/browser/widget/codeEditorWidget.ts | 2 +- .../editor/browser/widget/diffEditorWidget.ts | 2 +- .../editor/common/viewLayout/linesLayout.ts | 481 ++++++++++++--- src/vs/editor/common/viewLayout/viewLayout.ts | 3 +- .../common/viewLayout/whitespaceComputer.ts | 493 --------------- src/vs/editor/common/viewModel/viewModel.ts | 2 +- .../common/viewLayout/linesLayout.test.ts | 548 ++++++++++++++++- .../viewLayout/whitespaceComputer.test.ts | 559 ------------------ 9 files changed, 959 insertions(+), 1133 deletions(-) delete mode 100644 src/vs/editor/common/viewLayout/whitespaceComputer.ts delete mode 100644 src/vs/editor/test/common/viewLayout/whitespaceComputer.test.ts diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 50eed2efb08..bcd0995f843 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -16,7 +16,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; 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'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IDiffComputationResult } from 'vs/editor/common/services/editorWorkerService'; diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 83e47a48f1d..357eeab2a9f 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -40,7 +40,7 @@ import * as modes from 'vs/editor/common/modes'; import { editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity } from 'vs/editor/common/view/editorColorRegistry'; import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; -import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 0bd9f2788ff..e03249c867a 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -32,7 +32,7 @@ import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/s import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; -import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { InlineDecoration, InlineDecorationType, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index f95b15741bc..e18482b2ef4 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -4,8 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import { IEditorWhitespace, WhitespaceComputer } from 'vs/editor/common/viewLayout/whitespaceComputer'; import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; +import * as strings from 'vs/base/common/strings'; + +export interface IEditorWhitespace { + readonly id: string; + readonly afterLineNumber: number; + readonly heightInLines: number; +} /** * Layouting of objects that take vertical space (by having a height) and push down other objects. @@ -15,6 +21,59 @@ import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewMode */ export class LinesLayout { + private static INSTANCE_COUNT = 0; + + private readonly _instanceId: string; + + /** + * heights[i] is the height in pixels for whitespace at index i + */ + private readonly _heights: number[]; + + /** + * minWidths[i] is the min width in pixels for whitespace at index i + */ + private readonly _minWidths: number[]; + + /** + * afterLineNumbers[i] is the line number whitespace at index i is after + */ + private readonly _afterLineNumbers: number[]; + + /** + * ordinals[i] is the orinal of the whitespace at index i + */ + private readonly _ordinals: number[]; + + /** + * prefixSum[i] = SUM(heights[j]), 1 <= j <= i + */ + private readonly _prefixSum: number[]; + + /** + * prefixSum[i], 1 <= i <= prefixSumValidIndex can be trusted + */ + private _prefixSumValidIndex: number; + + /** + * ids[i] is the whitespace id of whitespace at index i + */ + private readonly _ids: string[]; + + /** + * index at which a whitespace is positioned (inside heights, afterLineNumbers, prefixSum members) + */ + private readonly _whitespaceId2Index: { + [id: string]: number; + }; + + /** + * last whitespace id issued + */ + private _lastWhitespaceId: number; + + private _minWidth: number; + /** * Keep track of the total number of lines. * This is useful for doing binary searches or for doing hit-testing. @@ -26,15 +85,47 @@ export class LinesLayout { */ private _lineHeight: number; - /** - * Contains whitespace information in pixels - */ - private readonly _whitespaces: WhitespaceComputer; - constructor(lineCount: number, lineHeight: number) { + this._instanceId = strings.singleLetterHash(++LinesLayout.INSTANCE_COUNT); + this._heights = []; + this._minWidths = []; + this._ids = []; + this._afterLineNumbers = []; + this._ordinals = []; + this._prefixSum = []; + this._prefixSumValidIndex = -1; + this._whitespaceId2Index = {}; + this._lastWhitespaceId = 0; + this._minWidth = -1; /* marker for not being computed */ this._lineCount = lineCount; this._lineHeight = lineHeight; - this._whitespaces = new WhitespaceComputer(); + } + + /** + * Find the insertion index for a new value inside a sorted array of values. + * If the value is already present in the sorted array, the insertion index will be after the already existing value. + */ + public static findInsertionIndex(sortedArray: number[], value: number, ordinals: number[], valueOrdinal: number): number { + let low = 0; + let high = sortedArray.length; + + while (low < high) { + let mid = ((low + high) >>> 1); + + if (value === sortedArray[mid]) { + if (valueOrdinal < ordinals[mid]) { + high = mid; + } else { + low = mid + 1; + } + } else if (value < sortedArray[mid]) { + high = mid; + } else { + low = mid + 1; + } + } + + return low; } /** @@ -63,14 +154,114 @@ export class LinesLayout { * @return An id that can be used later to mutate or delete the whitespace */ public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { - return this._whitespaces.insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); + afterLineNumber = afterLineNumber | 0; + ordinal = ordinal | 0; + heightInPx = heightInPx | 0; + minWidth = minWidth | 0; + + let id = this._instanceId + (++this._lastWhitespaceId); + let insertionIndex = LinesLayout.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: string, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): void { + insertIndex = insertIndex | 0; + afterLineNumber = afterLineNumber | 0; + ordinal = ordinal | 0; + heightInPx = heightInPx | 0; + minWidth = minWidth | 0; + + this._heights.splice(insertIndex, 0, heightInPx); + this._minWidths.splice(insertIndex, 0, minWidth); + this._ids.splice(insertIndex, 0, id); + this._afterLineNumbers.splice(insertIndex, 0, afterLineNumber); + this._ordinals.splice(insertIndex, 0, ordinal); + this._prefixSum.splice(insertIndex, 0, 0); + + let keys = Object.keys(this._whitespaceId2Index); + for (let i = 0, len = keys.length; i < len; i++) { + let sid = keys[i]; + let oldIndex = this._whitespaceId2Index[sid]; + if (oldIndex >= insertIndex) { + this._whitespaceId2Index[sid] = oldIndex + 1; + } + } + + this._whitespaceId2Index[id] = insertIndex; + this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1); } /** * Change properties associated with a certain whitespace. */ public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { - return this._whitespaces.changeWhitespace(id, newAfterLineNumber, newHeight); + newAfterLineNumber = newAfterLineNumber | 0; + newHeight = newHeight | 0; + + let hasChanges = false; + hasChanges = this.changeWhitespaceHeight(id, newHeight) || hasChanges; + hasChanges = this.changeWhitespaceAfterLineNumber(id, newAfterLineNumber) || hasChanges; + return hasChanges; + } + + /** + * Change the height of an existing whitespace + * + * @param id The whitespace to change + * @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: string, newHeightInPx: number): boolean { + newHeightInPx = newHeightInPx | 0; + + 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); + return true; + } + } + return false; + } + + /** + * Change the line number after which an existing whitespace flows. + * + * @param id The whitespace to change + * @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: string, newAfterLineNumber: number): boolean { + newAfterLineNumber = newAfterLineNumber | 0; + + if (this._whitespaceId2Index.hasOwnProperty(id)) { + let index = this._whitespaceId2Index[id]; + if (this._afterLineNumbers[index] !== newAfterLineNumber) { + // `afterLineNumber` changed for this whitespace + + // Record old ordinal + let ordinal = this._ordinals[index]; + + // Record old height + let heightInPx = this._heights[index]; + + // Record old min width + let minWidth = this._minWidths[index]; + + // Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace + this.removeWhitespace(id); + + // And add it again + let insertionIndex = LinesLayout.findInsertionIndex(this._afterLineNumbers, newAfterLineNumber, this._ordinals, ordinal); + this._insertWhitespaceAtIndex(id, insertionIndex, newAfterLineNumber, ordinal, heightInPx, minWidth); + + return true; + } + } + return false; } /** @@ -80,7 +271,36 @@ export class LinesLayout { * @return Returns true if the whitespace is found and it is removed. */ public removeWhitespace(id: string): boolean { - return this._whitespaces.removeWhitespace(id); + 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; + } + + return false; + } + + private _removeWhitespaceAtIndex(removeIndex: number): void { + removeIndex = removeIndex | 0; + + this._heights.splice(removeIndex, 1); + this._minWidths.splice(removeIndex, 1); + this._ids.splice(removeIndex, 1); + this._afterLineNumbers.splice(removeIndex, 1); + this._ordinals.splice(removeIndex, 1); + this._prefixSum.splice(removeIndex, 1); + this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, removeIndex - 1); + + let keys = Object.keys(this._whitespaceId2Index); + for (let i = 0, len = keys.length; i < len; i++) { + let sid = keys[i]; + let oldIndex = this._whitespaceId2Index[sid]; + if (oldIndex >= removeIndex) { + this._whitespaceId2Index[sid] = oldIndex - 1; + } + } } /** @@ -90,8 +310,23 @@ export class LinesLayout { * @param toLineNumber The line number at which the deletion ended, inclusive */ public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void { + fromLineNumber = fromLineNumber | 0; + toLineNumber = toLineNumber | 0; + this._lineCount -= (toLineNumber - fromLineNumber + 1); - this._whitespaces.onLinesDeleted(fromLineNumber, toLineNumber); + for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) { + let afterLineNumber = this._afterLineNumbers[i]; + + if (fromLineNumber <= afterLineNumber && afterLineNumber <= toLineNumber) { + // The line this whitespace was after has been deleted + // => move whitespace to before first deleted line + this._afterLineNumbers[i] = fromLineNumber - 1; + } else if (afterLineNumber > toLineNumber) { + // The line this whitespace was after has been moved up + // => move whitespace up + this._afterLineNumbers[i] -= (toLineNumber - fromLineNumber + 1); + } + } } /** @@ -101,8 +336,50 @@ export class LinesLayout { * @param toLineNumber The line number at which the insertion ended, inclusive. */ public onLinesInserted(fromLineNumber: number, toLineNumber: number): void { + fromLineNumber = fromLineNumber | 0; + toLineNumber = toLineNumber | 0; + this._lineCount += (toLineNumber - fromLineNumber + 1); - this._whitespaces.onLinesInserted(fromLineNumber, toLineNumber); + for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) { + let afterLineNumber = this._afterLineNumbers[i]; + + if (fromLineNumber <= afterLineNumber) { + this._afterLineNumbers[i] += (toLineNumber - fromLineNumber + 1); + } + } + } + + /** + * Get the sum of all the whitespaces. + */ + public getWhitespacesTotalHeight(): number { + if (this._heights.length === 0) { + return 0; + } + return this.getWhitespacesAccumulatedHeight(this._heights.length - 1); + } + + /** + * Return the sum of the heights of the whitespaces at [0..index]. + * This includes the whitespace at `index`. + * + * @param index The index of the whitespace. + * @return The sum of the heights of all whitespaces before the one at `index`, including the one at `index`. + */ + public getWhitespacesAccumulatedHeight(index: number): number { + index = index | 0; + + let startIndex = Math.max(0, this._prefixSumValidIndex + 1); + if (startIndex === 0) { + this._prefixSum[0] = this._heights[0]; + startIndex++; + } + + for (let i = startIndex; i <= index; i++) { + this._prefixSum[i] = this._prefixSum[i - 1] + this._heights[i]; + } + this._prefixSumValidIndex = Math.max(this._prefixSumValidIndex, index); + return this._prefixSum[index]; } /** @@ -112,10 +389,77 @@ export class LinesLayout { */ public getLinesTotalHeight(): number { let linesHeight = this._lineHeight * this._lineCount; - let whitespacesHeight = this._whitespaces.getTotalHeight(); + let whitespacesHeight = this.getWhitespacesTotalHeight(); return linesHeight + whitespacesHeight; } + /** + * Returns the accumulated height of whitespaces before the given line number. + * + * @param lineNumber The line number + */ + public getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber: number): number { + lineNumber = lineNumber | 0; + + let lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); + + if (lastWhitespaceBeforeLineNumber === -1) { + return 0; + } + + return this.getWhitespacesAccumulatedHeight(lastWhitespaceBeforeLineNumber); + } + + private _findLastWhitespaceBeforeLineNumber(lineNumber: number): number { + lineNumber = lineNumber | 0; + + // Find the whitespace before line number + let afterLineNumbers = this._afterLineNumbers; + let low = 0; + let high = afterLineNumbers.length - 1; + + while (low <= high) { + let delta = (high - low) | 0; + let halfDelta = (delta / 2) | 0; + let mid = (low + halfDelta) | 0; + + if (afterLineNumbers[mid] < lineNumber) { + if (mid + 1 >= afterLineNumbers.length || afterLineNumbers[mid + 1] >= lineNumber) { + return mid; + } else { + low = (mid + 1) | 0; + } + } else { + high = (mid - 1) | 0; + } + } + + return -1; + } + + private _findFirstWhitespaceAfterLineNumber(lineNumber: number): number { + lineNumber = lineNumber | 0; + + let lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); + let firstWhitespaceAfterLineNumber = lastWhitespaceBeforeLineNumber + 1; + + if (firstWhitespaceAfterLineNumber < this._heights.length) { + return firstWhitespaceAfterLineNumber; + } + + return -1; + } + + /** + * Find the index of the first whitespace which has `afterLineNumber` >= `lineNumber`. + * @return The index of the first whitespace with `afterLineNumber` >= `lineNumber` or -1 if no whitespace is found. + */ + public getFirstWhitespaceIndexAfterLineNumber(lineNumber: number): number { + lineNumber = lineNumber | 0; + + return this._findFirstWhitespaceAfterLineNumber(lineNumber); + } + /** * Get the vertical offset (the sum of heights for all objects above) a certain line number. * @@ -132,29 +476,30 @@ export class LinesLayout { previousLinesHeight = 0; } - let previousWhitespacesHeight = this._whitespaces.getAccumulatedHeightBeforeLineNumber(lineNumber); + let previousWhitespacesHeight = this.getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber); return previousLinesHeight + previousWhitespacesHeight; } - /** - * Returns the accumulated height of whitespaces before the given line number. - * - * @param lineNumber The line number - */ - public getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber: number): number { - return this._whitespaces.getAccumulatedHeightBeforeLineNumber(lineNumber); - } - /** * Returns if there is any whitespace in the document. */ public hasWhitespace(): boolean { - return this._whitespaces.getCount() > 0; + return this.getWhitespacesCount() > 0; } + /** + * The maximum min width for all whitespaces. + */ public getWhitespaceMinWidth(): number { - return this._whitespaces.getMinWidth(); + if (this._minWidth === -1) { + let minWidth = 0; + for (let i = 0, len = this._minWidths.length; i < len; i++) { + minWidth = Math.max(minWidth, this._minWidths[i]); + } + this._minWidth = minWidth; + } + return this._minWidth; } /** @@ -229,8 +574,8 @@ export class LinesLayout { let endLineNumber = this._lineCount | 0; // Also keep track of what whitespace we've got - let whitespaceIndex = this._whitespaces.getFirstWhitespaceIndexAfterLineNumber(startLineNumber) | 0; - const whitespaceCount = this._whitespaces.getCount() | 0; + let whitespaceIndex = this.getFirstWhitespaceIndexAfterLineNumber(startLineNumber) | 0; + const whitespaceCount = this.getWhitespacesCount() | 0; let currentWhitespaceHeight: number; let currentWhitespaceAfterLineNumber: number; @@ -239,8 +584,8 @@ export class LinesLayout { currentWhitespaceAfterLineNumber = endLineNumber + 1; currentWhitespaceHeight = 0; } else { - currentWhitespaceAfterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0; - currentWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(whitespaceIndex) | 0; + currentWhitespaceAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0; + currentWhitespaceHeight = this.getHeightForWhitespaceIndex(whitespaceIndex) | 0; } let currentVerticalOffset = startLineNumberVerticalOffset; @@ -290,8 +635,8 @@ export class LinesLayout { if (whitespaceIndex >= whitespaceCount) { currentWhitespaceAfterLineNumber = endLineNumber + 1; } else { - currentWhitespaceAfterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0; - currentWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(whitespaceIndex) | 0; + currentWhitespaceAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0; + currentWhitespaceHeight = this.getHeightForWhitespaceIndex(whitespaceIndex) | 0; } } @@ -336,7 +681,7 @@ export class LinesLayout { public getVerticalOffsetForWhitespaceIndex(whitespaceIndex: number): number { whitespaceIndex = whitespaceIndex | 0; - let afterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(whitespaceIndex); + let afterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex); let previousLinesHeight: number; if (afterLineNumber >= 1) { @@ -347,7 +692,7 @@ export class LinesLayout { let previousWhitespacesHeight: number; if (whitespaceIndex > 0) { - previousWhitespacesHeight = this._whitespaces.getAccumulatedHeight(whitespaceIndex - 1); + previousWhitespacesHeight = this.getWhitespacesAccumulatedHeight(whitespaceIndex - 1); } else { previousWhitespacesHeight = 0; } @@ -359,7 +704,7 @@ export class LinesLayout { let midWhitespaceIndex: number, minWhitespaceIndex = 0, - maxWhitespaceIndex = this._whitespaces.getCount() - 1, + maxWhitespaceIndex = this.getWhitespacesCount() - 1, midWhitespaceVerticalOffset: number, midWhitespaceHeight: number; @@ -369,7 +714,7 @@ export class LinesLayout { // Special case: nothing to be found let maxWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(maxWhitespaceIndex); - let maxWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(maxWhitespaceIndex); + let maxWhitespaceHeight = this.getHeightForWhitespaceIndex(maxWhitespaceIndex); if (verticalOffset >= maxWhitespaceVerticalOffset + maxWhitespaceHeight) { return -1; } @@ -378,7 +723,7 @@ export class LinesLayout { midWhitespaceIndex = Math.floor((minWhitespaceIndex + maxWhitespaceIndex) / 2); midWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(midWhitespaceIndex); - midWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(midWhitespaceIndex); + midWhitespaceHeight = this.getHeightForWhitespaceIndex(midWhitespaceIndex); if (verticalOffset >= midWhitespaceVerticalOffset + midWhitespaceHeight) { // vertical offset is after whitespace @@ -409,7 +754,7 @@ export class LinesLayout { return null; } - if (candidateIndex >= this._whitespaces.getCount()) { + if (candidateIndex >= this.getWhitespacesCount()) { return null; } @@ -419,9 +764,9 @@ export class LinesLayout { return null; } - let candidateHeight = this._whitespaces.getHeightForWhitespaceIndex(candidateIndex); - let candidateId = this._whitespaces.getIdForWhitespaceIndex(candidateIndex); - let candidateAfterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(candidateIndex); + let candidateHeight = this.getHeightForWhitespaceIndex(candidateIndex); + let candidateId = this.getIdForWhitespaceIndex(candidateIndex); + let candidateAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(candidateIndex); return { id: candidateId, @@ -443,7 +788,7 @@ export class LinesLayout { verticalOffset2 = verticalOffset2 | 0; let startIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset1); - let endIndex = this._whitespaces.getCount() - 1; + let endIndex = this.getWhitespacesCount() - 1; if (startIndex < 0) { return []; @@ -452,14 +797,14 @@ export class LinesLayout { let result: IViewWhitespaceViewportData[] = []; for (let i = startIndex; i <= endIndex; i++) { let top = this.getVerticalOffsetForWhitespaceIndex(i); - let height = this._whitespaces.getHeightForWhitespaceIndex(i); + let height = this.getHeightForWhitespaceIndex(i); if (top >= verticalOffset2) { break; } result.push({ - id: this._whitespaces.getIdForWhitespaceIndex(i), - afterLineNumber: this._whitespaces.getAfterLineNumberForWhitespaceIndex(i), + id: this.getIdForWhitespaceIndex(i), + afterLineNumber: this.getAfterLineNumberForWhitespaceIndex(i), verticalOffset: top, height: height }); @@ -472,14 +817,22 @@ export class LinesLayout { * Get all whitespaces. */ public getWhitespaces(): IEditorWhitespace[] { - return this._whitespaces.getWhitespaces(this._lineHeight); + let result: IEditorWhitespace[] = []; + for (let i = 0; i < this._heights.length; i++) { + result.push({ + id: this._ids[i], + afterLineNumber: this._afterLineNumbers[i], + heightInLines: this._heights[i] / this._lineHeight + }); + } + return result; } /** * The number of whitespaces. */ public getWhitespacesCount(): number { - return this._whitespaces.getCount(); + return this._heights.length; } /** @@ -489,7 +842,9 @@ export class LinesLayout { * @return `id` of whitespace at `index`. */ public getIdForWhitespaceIndex(index: number): string { - return this._whitespaces.getIdForWhitespaceIndex(index); + index = index | 0; + + return this._ids[index]; } /** @@ -499,7 +854,9 @@ export class LinesLayout { * @return `afterLineNumber` of whitespace at `index`. */ public getAfterLineNumberForWhitespaceIndex(index: number): number { - return this._whitespaces.getAfterLineNumberForWhitespaceIndex(index); + index = index | 0; + + return this._afterLineNumbers[index]; } /** @@ -509,32 +866,8 @@ export class LinesLayout { * @return `height` of whitespace at `index`. */ public getHeightForWhitespaceIndex(index: number): number { - return this._whitespaces.getHeightForWhitespaceIndex(index); - } + index = index | 0; - /** - * Find the index of the first whitespace which has `afterLineNumber` >= `lineNumber`. - * @return The index of the first whitespace with `afterLineNumber` >= `lineNumber` or -1 if no whitespace is found. - */ - public getFirstWhitespaceIndexAfterLineNumber(lineNumber: number): number { - return this._whitespaces.getFirstWhitespaceIndexAfterLineNumber(lineNumber); - } - - /** - * Return the sum of the heights of the whitespaces at [0..index]. - * This includes the whitespace at `index`. - * - * @param index The index of the whitespace. - * @return The sum of the heights of all whitespaces before the one at `index`, including the one at `index`. - */ - public getWhitespacesAccumulatedHeight(index: number): number { - return this._whitespaces.getAccumulatedHeight(index); - } - - /** - * Get the sum of all the whitespaces. - */ - public getWhitespacesTotalHeight(): number { - return this._whitespaces.getTotalHeight(); + return this._heights[index]; } } diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index 74fca28d029..cab08b23f44 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -8,9 +8,8 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout'; +import { LinesLayout, IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel'; const SMOOTH_SCROLLING_TIME = 125; diff --git a/src/vs/editor/common/viewLayout/whitespaceComputer.ts b/src/vs/editor/common/viewLayout/whitespaceComputer.ts deleted file mode 100644 index 8e5dd347e83..00000000000 --- a/src/vs/editor/common/viewLayout/whitespaceComputer.ts +++ /dev/null @@ -1,493 +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 strings from 'vs/base/common/strings'; - -export interface IEditorWhitespace { - readonly id: string; - readonly afterLineNumber: number; - readonly heightInLines: number; -} - -/** - * Represent whitespaces in between lines and provide fast CRUD management methods. - * The whitespaces are sorted ascending by `afterLineNumber`. - */ -export class WhitespaceComputer { - - private static INSTANCE_COUNT = 0; - - private readonly _instanceId: string; - - /** - * heights[i] is the height in pixels for whitespace at index i - */ - private readonly _heights: number[]; - - /** - * minWidths[i] is the min width in pixels for whitespace at index i - */ - private readonly _minWidths: number[]; - - /** - * afterLineNumbers[i] is the line number whitespace at index i is after - */ - private readonly _afterLineNumbers: number[]; - - /** - * ordinals[i] is the orinal of the whitespace at index i - */ - private readonly _ordinals: number[]; - - /** - * prefixSum[i] = SUM(heights[j]), 1 <= j <= i - */ - private readonly _prefixSum: number[]; - - /** - * prefixSum[i], 1 <= i <= prefixSumValidIndex can be trusted - */ - private _prefixSumValidIndex: number; - - /** - * ids[i] is the whitespace id of whitespace at index i - */ - private readonly _ids: string[]; - - /** - * index at which a whitespace is positioned (inside heights, afterLineNumbers, prefixSum members) - */ - private readonly _whitespaceId2Index: { - [id: string]: number; - }; - - /** - * last whitespace id issued - */ - private _lastWhitespaceId: number; - - private _minWidth: number; - - constructor() { - this._instanceId = strings.singleLetterHash(++WhitespaceComputer.INSTANCE_COUNT); - this._heights = []; - this._minWidths = []; - this._ids = []; - this._afterLineNumbers = []; - this._ordinals = []; - this._prefixSum = []; - this._prefixSumValidIndex = -1; - this._whitespaceId2Index = {}; - this._lastWhitespaceId = 0; - this._minWidth = -1; /* marker for not being computed */ - } - - /** - * Find the insertion index for a new value inside a sorted array of values. - * If the value is already present in the sorted array, the insertion index will be after the already existing value. - */ - public static findInsertionIndex(sortedArray: number[], value: number, ordinals: number[], valueOrdinal: number): number { - let low = 0; - let high = sortedArray.length; - - while (low < high) { - let mid = ((low + high) >>> 1); - - if (value === sortedArray[mid]) { - if (valueOrdinal < ordinals[mid]) { - high = mid; - } else { - low = mid + 1; - } - } else if (value < sortedArray[mid]) { - high = mid; - } else { - low = mid + 1; - } - } - - return low; - } - - /** - * Insert a new whitespace of a certain height after a line number. - * The whitespace has a "sticky" characteristic. - * Irrespective of edits above or below `afterLineNumber`, the whitespace will follow the initial line. - * - * @param afterLineNumber The conceptual position of this whitespace. The whitespace will follow this line as best as possible even when deleting/inserting lines above/below. - * @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): string { - afterLineNumber = afterLineNumber | 0; - ordinal = ordinal | 0; - heightInPx = heightInPx | 0; - minWidth = minWidth | 0; - - 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: string, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): void { - insertIndex = insertIndex | 0; - afterLineNumber = afterLineNumber | 0; - ordinal = ordinal | 0; - heightInPx = heightInPx | 0; - minWidth = minWidth | 0; - - this._heights.splice(insertIndex, 0, heightInPx); - this._minWidths.splice(insertIndex, 0, minWidth); - this._ids.splice(insertIndex, 0, id); - this._afterLineNumbers.splice(insertIndex, 0, afterLineNumber); - this._ordinals.splice(insertIndex, 0, ordinal); - this._prefixSum.splice(insertIndex, 0, 0); - - let keys = Object.keys(this._whitespaceId2Index); - for (let i = 0, len = keys.length; i < len; i++) { - let sid = keys[i]; - let oldIndex = this._whitespaceId2Index[sid]; - if (oldIndex >= insertIndex) { - this._whitespaceId2Index[sid] = oldIndex + 1; - } - } - - this._whitespaceId2Index[id] = insertIndex; - this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1); - } - - /** - * Change properties associated with a certain whitespace. - */ - public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { - newAfterLineNumber = newAfterLineNumber | 0; - newHeight = newHeight | 0; - - let hasChanges = false; - hasChanges = this.changeWhitespaceHeight(id, newHeight) || hasChanges; - hasChanges = this.changeWhitespaceAfterLineNumber(id, newAfterLineNumber) || hasChanges; - return hasChanges; - } - - /** - * Change the height of an existing whitespace - * - * @param id The whitespace to change - * @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: string, newHeightInPx: number): boolean { - newHeightInPx = newHeightInPx | 0; - - 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); - return true; - } - } - return false; - } - - /** - * Change the line number after which an existing whitespace flows. - * - * @param id The whitespace to change - * @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: string, newAfterLineNumber: number): boolean { - newAfterLineNumber = newAfterLineNumber | 0; - - if (this._whitespaceId2Index.hasOwnProperty(id)) { - let index = this._whitespaceId2Index[id]; - if (this._afterLineNumbers[index] !== newAfterLineNumber) { - // `afterLineNumber` changed for this whitespace - - // Record old ordinal - let ordinal = this._ordinals[index]; - - // Record old height - let heightInPx = this._heights[index]; - - // Record old min width - let minWidth = this._minWidths[index]; - - // Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace - this.removeWhitespace(id); - - // And add it again - let insertionIndex = WhitespaceComputer.findInsertionIndex(this._afterLineNumbers, newAfterLineNumber, this._ordinals, ordinal); - this._insertWhitespaceAtIndex(id, insertionIndex, newAfterLineNumber, ordinal, heightInPx, minWidth); - - return true; - } - } - return false; - } - - /** - * Remove an existing whitespace. - * - * @param id The whitespace to remove - * @return Returns true if the whitespace is found and it is removed. - */ - 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; - } - - return false; - } - - private _removeWhitespaceAtIndex(removeIndex: number): void { - removeIndex = removeIndex | 0; - - this._heights.splice(removeIndex, 1); - this._minWidths.splice(removeIndex, 1); - this._ids.splice(removeIndex, 1); - this._afterLineNumbers.splice(removeIndex, 1); - this._ordinals.splice(removeIndex, 1); - this._prefixSum.splice(removeIndex, 1); - this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, removeIndex - 1); - - let keys = Object.keys(this._whitespaceId2Index); - for (let i = 0, len = keys.length; i < len; i++) { - let sid = keys[i]; - let oldIndex = this._whitespaceId2Index[sid]; - if (oldIndex >= removeIndex) { - this._whitespaceId2Index[sid] = oldIndex - 1; - } - } - } - - /** - * Notify the computer that lines have been deleted (a continuous zone of lines). - * This gives it a chance to update `afterLineNumber` for whitespaces, giving the "sticky" characteristic. - * - * @param fromLineNumber The line number at which the deletion started, inclusive - * @param toLineNumber The line number at which the deletion ended, inclusive - */ - public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void { - fromLineNumber = fromLineNumber | 0; - toLineNumber = toLineNumber | 0; - - for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) { - let afterLineNumber = this._afterLineNumbers[i]; - - if (fromLineNumber <= afterLineNumber && afterLineNumber <= toLineNumber) { - // The line this whitespace was after has been deleted - // => move whitespace to before first deleted line - this._afterLineNumbers[i] = fromLineNumber - 1; - } else if (afterLineNumber > toLineNumber) { - // The line this whitespace was after has been moved up - // => move whitespace up - this._afterLineNumbers[i] -= (toLineNumber - fromLineNumber + 1); - } - } - } - - /** - * Notify the computer that lines have been inserted (a continuous zone of lines). - * This gives it a chance to update `afterLineNumber` for whitespaces, giving the "sticky" characteristic. - * - * @param fromLineNumber The line number at which the insertion started, inclusive - * @param toLineNumber The line number at which the insertion ended, inclusive. - */ - public onLinesInserted(fromLineNumber: number, toLineNumber: number): void { - fromLineNumber = fromLineNumber | 0; - toLineNumber = toLineNumber | 0; - - for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) { - let afterLineNumber = this._afterLineNumbers[i]; - - if (fromLineNumber <= afterLineNumber) { - this._afterLineNumbers[i] += (toLineNumber - fromLineNumber + 1); - } - } - } - - /** - * Get the sum of all the whitespaces. - */ - public getTotalHeight(): number { - if (this._heights.length === 0) { - return 0; - } - return this.getAccumulatedHeight(this._heights.length - 1); - } - - /** - * Return the sum of the heights of the whitespaces at [0..index]. - * This includes the whitespace at `index`. - * - * @param index The index of the whitespace. - * @return The sum of the heights of all whitespaces before the one at `index`, including the one at `index`. - */ - public getAccumulatedHeight(index: number): number { - index = index | 0; - - let startIndex = Math.max(0, this._prefixSumValidIndex + 1); - if (startIndex === 0) { - this._prefixSum[0] = this._heights[0]; - startIndex++; - } - - for (let i = startIndex; i <= index; i++) { - this._prefixSum[i] = this._prefixSum[i - 1] + this._heights[i]; - } - this._prefixSumValidIndex = Math.max(this._prefixSumValidIndex, index); - return this._prefixSum[index]; - } - - /** - * Find all whitespaces with `afterLineNumber` < `lineNumber` and return the sum of their heights. - * - * @param lineNumber The line number whitespaces should be before. - * @return The sum of the heights of the whitespaces before `lineNumber`. - */ - public getAccumulatedHeightBeforeLineNumber(lineNumber: number): number { - lineNumber = lineNumber | 0; - - let lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); - - if (lastWhitespaceBeforeLineNumber === -1) { - return 0; - } - - return this.getAccumulatedHeight(lastWhitespaceBeforeLineNumber); - } - - private _findLastWhitespaceBeforeLineNumber(lineNumber: number): number { - lineNumber = lineNumber | 0; - - // Find the whitespace before line number - let afterLineNumbers = this._afterLineNumbers; - let low = 0; - let high = afterLineNumbers.length - 1; - - while (low <= high) { - let delta = (high - low) | 0; - let halfDelta = (delta / 2) | 0; - let mid = (low + halfDelta) | 0; - - if (afterLineNumbers[mid] < lineNumber) { - if (mid + 1 >= afterLineNumbers.length || afterLineNumbers[mid + 1] >= lineNumber) { - return mid; - } else { - low = (mid + 1) | 0; - } - } else { - high = (mid - 1) | 0; - } - } - - return -1; - } - - private _findFirstWhitespaceAfterLineNumber(lineNumber: number): number { - lineNumber = lineNumber | 0; - - let lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); - let firstWhitespaceAfterLineNumber = lastWhitespaceBeforeLineNumber + 1; - - if (firstWhitespaceAfterLineNumber < this._heights.length) { - return firstWhitespaceAfterLineNumber; - } - - return -1; - } - - /** - * Find the index of the first whitespace which has `afterLineNumber` >= `lineNumber`. - * @return The index of the first whitespace with `afterLineNumber` >= `lineNumber` or -1 if no whitespace is found. - */ - public getFirstWhitespaceIndexAfterLineNumber(lineNumber: number): number { - lineNumber = lineNumber | 0; - - return this._findFirstWhitespaceAfterLineNumber(lineNumber); - } - - /** - * The number of whitespaces. - */ - public getCount(): number { - return this._heights.length; - } - - /** - * The maximum min width for all whitespaces. - */ - public getMinWidth(): number { - if (this._minWidth === -1) { - let minWidth = 0; - for (let i = 0, len = this._minWidths.length; i < len; i++) { - minWidth = Math.max(minWidth, this._minWidths[i]); - } - this._minWidth = minWidth; - } - return this._minWidth; - } - - /** - * Get the `afterLineNumber` for whitespace at index `index`. - * - * @param index The index of the whitespace. - * @return `afterLineNumber` of whitespace at `index`. - */ - public getAfterLineNumberForWhitespaceIndex(index: number): number { - index = index | 0; - - return this._afterLineNumbers[index]; - } - - /** - * Get the `id` for whitespace at index `index`. - * - * @param index The index of the whitespace. - * @return `id` of whitespace at `index`. - */ - public getIdForWhitespaceIndex(index: number): string { - index = index | 0; - - return this._ids[index]; - } - - /** - * Get the `height` for whitespace at index `index`. - * - * @param index The index of the whitespace. - * @return `height` of whitespace at `index`. - */ - public getHeightForWhitespaceIndex(index: number): number { - index = index | 0; - - return this._heights[index]; - } - - /** - * Get all whitespaces. - */ - public getWhitespaces(deviceLineHeight: number): IEditorWhitespace[] { - deviceLineHeight = deviceLineHeight | 0; - - let result: IEditorWhitespace[] = []; - for (let i = 0; i < this._heights.length; i++) { - result.push({ - id: this._ids[i], - afterLineNumber: this._afterLineNumbers[i], - heightInLines: this._heights[i] / deviceLineHeight - }); - } - return result; - } -} diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 0f16d0a84a2..3dd75a94d4b 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -13,7 +13,7 @@ import { INewScrollPosition } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, TextModelResolvedOptions } from 'vs/editor/common/model'; import { IViewEventListener } from 'vs/editor/common/view/viewEvents'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ITheme } from 'vs/platform/theme/common/themeService'; export interface IViewWhitespaceViewportData { diff --git a/src/vs/editor/test/common/viewLayout/linesLayout.test.ts b/src/vs/editor/test/common/viewLayout/linesLayout.test.ts index dceeaab37b0..a88c5c16c6d 100644 --- a/src/vs/editor/test/common/viewLayout/linesLayout.test.ts +++ b/src/vs/editor/test/common/viewLayout/linesLayout.test.ts @@ -479,7 +479,6 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.deepEqual(viewportData.relativeVerticalOffset, [160, 170, 180, 190]); }); - test('LinesLayout getLinesViewportData 2 & getWhitespaceViewportData', () => { let linesLayout = new LinesLayout(10, 10); let a = linesLayout.insertWhitespace(6, 0, 100, 0); @@ -592,4 +591,551 @@ suite('Editor ViewLayout - LinesLayout', () => { whitespace = linesLayout.getWhitespaceAtVerticalOffset(220); assert.equal(whitespace, null); }); + + test('LinesLayout', () => { + + const linesLayout = new LinesLayout(100, 20); + + // Insert a whitespace after line number 2, of height 10 + const a = linesLayout.insertWhitespace(2, 0, 10, 0); + // whitespaces: a(2, 10) + assert.equal(linesLayout.getWhitespacesCount(), 1); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 10); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 10); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 10); + + // Insert a whitespace again after line number 2, of height 20 + let b = linesLayout.insertWhitespace(2, 0, 20, 0); + // whitespaces: a(2, 10), b(2, 20) + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 30); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 30); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 30); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30); + + // Change last inserted whitespace height to 30 + linesLayout.changeWhitespace(b, 2, 30); + // whitespaces: a(2, 10), b(2, 30) + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 30); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 40); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 40); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 40); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 40); + + // Remove last inserted whitespace + linesLayout.removeWhitespace(b); + // whitespaces: a(2, 10) + assert.equal(linesLayout.getWhitespacesCount(), 1); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 10); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 10); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 10); + + // Add a whitespace before the first line of height 50 + b = linesLayout.insertWhitespace(0, 0, 50, 0); + // whitespaces: b(0, 50), a(2, 10) + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 10); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 60); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 60); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 60); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 60); + + // Add a whitespace after line 4 of height 20 + linesLayout.insertWhitespace(4, 0, 20, 0); + // whitespaces: b(0, 50), a(2, 10), c(4, 20) + assert.equal(linesLayout.getWhitespacesCount(), 3); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 10); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 4); + assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 60); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 80); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 80); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 60); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 60); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 80); + + // Add a whitespace after line 3 of height 30 + linesLayout.insertWhitespace(3, 0, 30, 0); + // whitespaces: b(0, 50), a(2, 10), d(3, 30), c(4, 20) + assert.equal(linesLayout.getWhitespacesCount(), 4); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 10); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(3), 4); + assert.equal(linesLayout.getHeightForWhitespaceIndex(3), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 60); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 90); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(3), 110); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 110); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 60); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 90); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 110); + + // Change whitespace after line 2 to height of 100 + linesLayout.changeWhitespace(a, 2, 100); + // whitespaces: b(0, 50), a(2, 100), d(3, 30), c(4, 20) + assert.equal(linesLayout.getWhitespacesCount(), 4); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 100); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(3), 4); + assert.equal(linesLayout.getHeightForWhitespaceIndex(3), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 150); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 180); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(3), 200); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 200); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 150); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 180); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 200); + + // Remove whitespace after line 2 + linesLayout.removeWhitespace(a); + // whitespaces: b(0, 50), d(3, 30), c(4, 20) + assert.equal(linesLayout.getWhitespacesCount(), 3); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 4); + assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 80); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 100); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 100); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 80); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 100); + + // Remove whitespace before line 1 + linesLayout.removeWhitespace(b); + // whitespaces: d(3, 30), c(4, 20) + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 4); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50); + + // Delete line 1 + linesLayout.onLinesDeleted(1, 1); + // whitespaces: d(2, 30), c(3, 20) + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 30); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50); + + // Insert a line before line 1 + linesLayout.onLinesInserted(1, 1); + // whitespaces: d(3, 30), c(4, 20) + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 4); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50); + + // Delete line 4 + linesLayout.onLinesDeleted(4, 4); + // whitespaces: d(3, 30), c(3, 20) + assert.equal(linesLayout.getWhitespacesCount(), 2); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30); + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30); + assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50); + assert.equal(linesLayout.getWhitespacesTotalHeight(), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 0); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 50); + assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50); + }); + + test('LinesLayout findInsertionIndex', () => { + + const makeArray = (size: number, fillValue: number) => { + let r: number[] = []; + for (let i = 0; i < size; i++) { + r[i] = fillValue; + } + return r; + }; + + let arr: number[]; + let ordinals: number[]; + + arr = []; + ordinals = makeArray(arr.length, 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 0); + + arr = [1]; + ordinals = makeArray(arr.length, 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); + + arr = [1, 3]; + ordinals = makeArray(arr.length, 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); + + arr = [1, 3, 5]; + ordinals = makeArray(arr.length, 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); + + arr = [1, 3, 5]; + ordinals = makeArray(arr.length, 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); + + arr = [1, 3, 5, 7]; + ordinals = makeArray(arr.length, 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 7, ordinals, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 8, ordinals, 0), 4); + + arr = [1, 3, 5, 7, 9]; + ordinals = makeArray(arr.length, 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 7, ordinals, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 8, ordinals, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 9, ordinals, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 10, ordinals, 0), 5); + + arr = [1, 3, 5, 7, 9, 11]; + ordinals = makeArray(arr.length, 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 7, ordinals, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 8, ordinals, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 9, ordinals, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 10, ordinals, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 11, ordinals, 0), 6); + assert.equal(LinesLayout.findInsertionIndex(arr, 12, ordinals, 0), 6); + + arr = [1, 3, 5, 7, 9, 11, 13]; + ordinals = makeArray(arr.length, 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 7, ordinals, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 8, ordinals, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 9, ordinals, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 10, ordinals, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 11, ordinals, 0), 6); + assert.equal(LinesLayout.findInsertionIndex(arr, 12, ordinals, 0), 6); + assert.equal(LinesLayout.findInsertionIndex(arr, 13, ordinals, 0), 7); + assert.equal(LinesLayout.findInsertionIndex(arr, 14, ordinals, 0), 7); + + arr = [1, 3, 5, 7, 9, 11, 13, 15]; + ordinals = makeArray(arr.length, 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 7, ordinals, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 8, ordinals, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 9, ordinals, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 10, ordinals, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 11, ordinals, 0), 6); + assert.equal(LinesLayout.findInsertionIndex(arr, 12, ordinals, 0), 6); + assert.equal(LinesLayout.findInsertionIndex(arr, 13, ordinals, 0), 7); + assert.equal(LinesLayout.findInsertionIndex(arr, 14, ordinals, 0), 7); + assert.equal(LinesLayout.findInsertionIndex(arr, 15, ordinals, 0), 8); + assert.equal(LinesLayout.findInsertionIndex(arr, 16, ordinals, 0), 8); + }); + + test('LinesLayout changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber', () => { + const linesLayout = new LinesLayout(100, 20); + + const a = linesLayout.insertWhitespace(0, 0, 1, 0); + const b = linesLayout.insertWhitespace(7, 0, 1, 0); + const c = linesLayout.insertWhitespace(3, 0, 1, 0); + + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); + assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); + + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 1); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + + // Do not really move a + linesLayout.changeWhitespace(a, 1, 1); + + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 1 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 1); + assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); + + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + + + // Do not really move a + linesLayout.changeWhitespace(a, 2, 1); + + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 2 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); + assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); + + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + + + // Change a to conflict with c => a gets placed after c + linesLayout.changeWhitespace(a, 3, 1); + + assert.equal(linesLayout.getIdForWhitespaceIndex(0), c); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(1), a); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); + + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + + + // Make a no-op + linesLayout.changeWhitespace(c, 3, 1); + + assert.equal(linesLayout.getIdForWhitespaceIndex(0), c); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(1), a); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); + + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + + + + // Conflict c with b => c gets placed after b + linesLayout.changeWhitespace(c, 7, 1); + + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 3 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); + assert.equal(linesLayout.getIdForWhitespaceIndex(1), b); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 7); + assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 7 + assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); + + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 0); // a + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 1); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 1); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 1); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 1); // b + assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- + }); + + test('LinesLayout Bug', () => { + const linesLayout = new LinesLayout(100, 20); + + const a = linesLayout.insertWhitespace(0, 0, 1, 0); + const b = linesLayout.insertWhitespace(7, 0, 1, 0); + + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), b); // 7 + + const c = linesLayout.insertWhitespace(3, 0, 1, 0); + + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 + assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 + + const d = linesLayout.insertWhitespace(2, 0, 1, 0); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 + assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 + assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 + + const e = linesLayout.insertWhitespace(8, 0, 1, 0); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 + assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 + assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 + assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8 + + const f = linesLayout.insertWhitespace(11, 0, 1, 0); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 + assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 + assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 + assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8 + assert.equal(linesLayout.getIdForWhitespaceIndex(5), f); // 11 + + const g = linesLayout.insertWhitespace(10, 0, 1, 0); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 + assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 + assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 + assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8 + assert.equal(linesLayout.getIdForWhitespaceIndex(5), g); // 10 + assert.equal(linesLayout.getIdForWhitespaceIndex(6), f); // 11 + + const h = linesLayout.insertWhitespace(0, 0, 1, 0); + assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(1), h); // 0 + assert.equal(linesLayout.getIdForWhitespaceIndex(2), d); // 2 + assert.equal(linesLayout.getIdForWhitespaceIndex(3), c); // 3 + assert.equal(linesLayout.getIdForWhitespaceIndex(4), b); // 7 + assert.equal(linesLayout.getIdForWhitespaceIndex(5), e); // 8 + assert.equal(linesLayout.getIdForWhitespaceIndex(6), g); // 10 + assert.equal(linesLayout.getIdForWhitespaceIndex(7), f); // 11 + }); }); diff --git a/src/vs/editor/test/common/viewLayout/whitespaceComputer.test.ts b/src/vs/editor/test/common/viewLayout/whitespaceComputer.test.ts deleted file mode 100644 index df7527d6b9b..00000000000 --- a/src/vs/editor/test/common/viewLayout/whitespaceComputer.test.ts +++ /dev/null @@ -1,559 +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 assert from 'assert'; -import { WhitespaceComputer } from 'vs/editor/common/viewLayout/whitespaceComputer'; -import { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout'; - -suite('Editor ViewLayout - WhitespaceComputer', () => { - - test('WhitespaceComputer', () => { - - const linesLayout = new LinesLayout(100, 20); - - // Insert a whitespace after line number 2, of height 10 - const a = linesLayout.insertWhitespace(2, 0, 10, 0); - // whitespaces: a(2, 10) - assert.equal(linesLayout.getWhitespacesCount(), 1); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 10); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 10); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 10); - - // Insert a whitespace again after line number 2, of height 20 - let b = linesLayout.insertWhitespace(2, 0, 20, 0); - // whitespaces: a(2, 10), b(2, 20) - assert.equal(linesLayout.getWhitespacesCount(), 2); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); - assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 30); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 30); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 30); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30); - - // Change last inserted whitespace height to 30 - linesLayout.changeWhitespace(b, 2, 30); - // whitespaces: a(2, 10), b(2, 30) - assert.equal(linesLayout.getWhitespacesCount(), 2); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); - assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 30); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 40); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 40); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 40); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 40); - - // Remove last inserted whitespace - linesLayout.removeWhitespace(b); - // whitespaces: a(2, 10) - assert.equal(linesLayout.getWhitespacesCount(), 1); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 10); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 10); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 10); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 10); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 10); - - // Add a whitespace before the first line of height 50 - b = linesLayout.insertWhitespace(0, 0, 50, 0); - // whitespaces: b(0, 50), a(2, 10) - assert.equal(linesLayout.getWhitespacesCount(), 2); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); - assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 10); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 60); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 60); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 60); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 60); - - // Add a whitespace after line 4 of height 20 - linesLayout.insertWhitespace(4, 0, 20, 0); - // whitespaces: b(0, 50), a(2, 10), c(4, 20) - assert.equal(linesLayout.getWhitespacesCount(), 3); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); - assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 10); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 4); - assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 20); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 60); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 80); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 80); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 60); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 60); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 80); - - // Add a whitespace after line 3 of height 30 - linesLayout.insertWhitespace(3, 0, 30, 0); - // whitespaces: b(0, 50), a(2, 10), d(3, 30), c(4, 20) - assert.equal(linesLayout.getWhitespacesCount(), 4); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); - assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 10); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 3); - assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 30); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(3), 4); - assert.equal(linesLayout.getHeightForWhitespaceIndex(3), 20); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 60); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 90); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(3), 110); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 110); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 60); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 90); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 110); - - // Change whitespace after line 2 to height of 100 - linesLayout.changeWhitespace(a, 2, 100); - // whitespaces: b(0, 50), a(2, 100), d(3, 30), c(4, 20) - assert.equal(linesLayout.getWhitespacesCount(), 4); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 2); - assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 100); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 3); - assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 30); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(3), 4); - assert.equal(linesLayout.getHeightForWhitespaceIndex(3), 20); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 150); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 180); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(3), 200); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 200); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 150); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 180); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 200); - - // Remove whitespace after line 2 - linesLayout.removeWhitespace(a); - // whitespaces: b(0, 50), d(3, 30), c(4, 20) - assert.equal(linesLayout.getWhitespacesCount(), 3); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 50); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 30); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 4); - assert.equal(linesLayout.getHeightForWhitespaceIndex(2), 20); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 50); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 80); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(2), 100); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 100); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 80); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 100); - - // Remove whitespace before line 1 - linesLayout.removeWhitespace(b); - // whitespaces: d(3, 30), c(4, 20) - assert.equal(linesLayout.getWhitespacesCount(), 2); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 4); - assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50); - - // Delete line 1 - linesLayout.onLinesDeleted(1, 1); - // whitespaces: d(2, 30), c(3, 20) - assert.equal(linesLayout.getWhitespacesCount(), 2); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 30); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50); - - // Insert a line before line 1 - linesLayout.onLinesInserted(1, 1); - // whitespaces: d(3, 30), c(4, 20) - assert.equal(linesLayout.getWhitespacesCount(), 2); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 4); - assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50); - - // Delete line 4 - linesLayout.onLinesDeleted(4, 4); - // whitespaces: d(3, 30), c(3, 20) - assert.equal(linesLayout.getWhitespacesCount(), 2); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); - assert.equal(linesLayout.getHeightForWhitespaceIndex(0), 30); - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(linesLayout.getHeightForWhitespaceIndex(1), 20); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(0), 30); - assert.equal(linesLayout.getWhitespacesAccumulatedHeight(1), 50); - assert.equal(linesLayout.getWhitespacesTotalHeight(), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(1), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(2), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(3), 0); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 50); - assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 50); - }); - - test('WhitespaceComputer findInsertionIndex', () => { - - const makeArray = (size: number, fillValue: number) => { - let r: number[] = []; - for (let i = 0; i < size; i++) { - r[i] = fillValue; - } - return r; - }; - - let arr: number[]; - let ordinals: number[]; - - arr = []; - ordinals = makeArray(arr.length, 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 0); - - arr = [1]; - ordinals = makeArray(arr.length, 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); - - arr = [1, 3]; - ordinals = makeArray(arr.length, 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); - - arr = [1, 3, 5]; - ordinals = makeArray(arr.length, 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); - - arr = [1, 3, 5]; - ordinals = makeArray(arr.length, 3); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); - - arr = [1, 3, 5, 7]; - ordinals = makeArray(arr.length, 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4); - - arr = [1, 3, 5, 7, 9]; - ordinals = makeArray(arr.length, 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5); - - arr = [1, 3, 5, 7, 9, 11]; - ordinals = makeArray(arr.length, 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 11, ordinals, 0), 6); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 12, ordinals, 0), 6); - - arr = [1, 3, 5, 7, 9, 11, 13]; - ordinals = makeArray(arr.length, 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 11, ordinals, 0), 6); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 12, ordinals, 0), 6); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 13, ordinals, 0), 7); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 14, ordinals, 0), 7); - - arr = [1, 3, 5, 7, 9, 11, 13, 15]; - ordinals = makeArray(arr.length, 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 5, ordinals, 0), 3); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 6, ordinals, 0), 3); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 7, ordinals, 0), 4); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 8, ordinals, 0), 4); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 9, ordinals, 0), 5); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 10, ordinals, 0), 5); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 11, ordinals, 0), 6); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 12, ordinals, 0), 6); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 13, ordinals, 0), 7); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 14, ordinals, 0), 7); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 15, ordinals, 0), 8); - assert.equal(WhitespaceComputer.findInsertionIndex(arr, 16, ordinals, 0), 8); - }); - - test('WhitespaceComputer changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber', () => { - const linesLayout = new LinesLayout(100, 20); - - const a = linesLayout.insertWhitespace(0, 0, 1, 0); - const b = linesLayout.insertWhitespace(7, 0, 1, 0); - const c = linesLayout.insertWhitespace(3, 0, 1, 0); - - assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); - assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); - - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 1); // c - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- - - // Do not really move a - linesLayout.changeWhitespace(a, 1, 1); - - assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 1 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 1); - assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); - - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 1); // c - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- - - - // Do not really move a - linesLayout.changeWhitespace(a, 2, 1); - - assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 2 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); - assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); - - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 1); // c - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- - - - // Change a to conflict with c => a gets placed after c - linesLayout.changeWhitespace(a, 3, 1); - - assert.equal(linesLayout.getIdForWhitespaceIndex(0), c); // 3 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); - assert.equal(linesLayout.getIdForWhitespaceIndex(1), a); // 3 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); - - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- - - - // Make a no-op - linesLayout.changeWhitespace(c, 3, 1); - - assert.equal(linesLayout.getIdForWhitespaceIndex(0), c); // 3 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); - assert.equal(linesLayout.getIdForWhitespaceIndex(1), a); // 3 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 3); - assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); - - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // c - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // c - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 0); // c - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 2); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- - - - - // Conflict c with b => c gets placed after b - linesLayout.changeWhitespace(c, 7, 1); - - assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 3 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); - assert.equal(linesLayout.getIdForWhitespaceIndex(1), b); // 7 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(1), 7); - assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 7 - assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(2), 7); - - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(1), 0); // a - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(2), 0); // a - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(3), 0); // a - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(4), 1); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(5), 1); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(6), 1); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(7), 1); // b - assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- - }); - - - test('WhitespaceComputer Bug', () => { - const linesLayout = new LinesLayout(100, 20); - - const a = linesLayout.insertWhitespace(0, 0, 1, 0); - const b = linesLayout.insertWhitespace(7, 0, 1, 0); - - assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(linesLayout.getIdForWhitespaceIndex(1), b); // 7 - - const c = linesLayout.insertWhitespace(3, 0, 1, 0); - - assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 - assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 - - const d = linesLayout.insertWhitespace(2, 0, 1, 0); - assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 - assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 - assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 - - const e = linesLayout.insertWhitespace(8, 0, 1, 0); - assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 - assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 - assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 - assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8 - - const f = linesLayout.insertWhitespace(11, 0, 1, 0); - assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 - assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 - assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 - assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8 - assert.equal(linesLayout.getIdForWhitespaceIndex(5), f); // 11 - - const g = linesLayout.insertWhitespace(10, 0, 1, 0); - assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 - assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 - assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 - assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8 - assert.equal(linesLayout.getIdForWhitespaceIndex(5), g); // 10 - assert.equal(linesLayout.getIdForWhitespaceIndex(6), f); // 11 - - const h = linesLayout.insertWhitespace(0, 0, 1, 0); - assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 - assert.equal(linesLayout.getIdForWhitespaceIndex(1), h); // 0 - assert.equal(linesLayout.getIdForWhitespaceIndex(2), d); // 2 - assert.equal(linesLayout.getIdForWhitespaceIndex(3), c); // 3 - assert.equal(linesLayout.getIdForWhitespaceIndex(4), b); // 7 - assert.equal(linesLayout.getIdForWhitespaceIndex(5), e); // 8 - assert.equal(linesLayout.getIdForWhitespaceIndex(6), g); // 10 - assert.equal(linesLayout.getIdForWhitespaceIndex(7), f); // 11 - }); -}); - From e80c62bdcb0a1d7ce0d3d826241df0ef20162417 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Nov 2019 10:53:04 +0100 Subject: [PATCH 56/88] allow $openUri to accept URI and string --- src/vs/workbench/api/browser/mainThreadWindow.ts | 12 ++++++++++-- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostRequireInterceptor.ts | 4 ++-- src/vs/workbench/api/common/extHostWindow.ts | 4 +++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadWindow.ts b/src/vs/workbench/api/browser/mainThreadWindow.ts index bbce19a3388..2a431111e93 100644 --- a/src/vs/workbench/api/browser/mainThreadWindow.ts +++ b/src/vs/workbench/api/browser/mainThreadWindow.ts @@ -42,9 +42,17 @@ export class MainThreadWindow implements MainThreadWindowShape { return Promise.resolve(this.hostService.hasFocus); } - async $openUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise { + async $openUri(uriComponents: UriComponents, uriString: string | undefined, options: IOpenUriOptions): Promise { const uri = URI.from(uriComponents); - return this.openerService.open(uri, { openExternal: true, allowTunneling: options.allowTunneling }); + let target: URI | string; + if (uriString && URI.parse(uriString).toString() === uri.toString()) { + // called with string and no transformation happened -> keep string + target = uriString; + } else { + // called with URI or transformed -> use uri + target = uri; + } + return this.openerService.open(target, { openExternal: true, allowTunneling: options.allowTunneling }); } async $asExternalUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9bc433a1727..5df456ab6c7 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -759,7 +759,7 @@ export interface IOpenUriOptions { export interface MainThreadWindowShape extends IDisposable { $getWindowVisibility(): Promise; - $openUri(uri: UriComponents, options: IOpenUriOptions): Promise; + $openUri(uri: UriComponents, uriString: string | undefined, options: IOpenUriOptions): Promise; $asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise; } diff --git a/src/vs/workbench/api/common/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts index b6f94048fbd..c06e86c8305 100644 --- a/src/vs/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -245,9 +245,9 @@ class OpenNodeModuleFactory implements INodeModuleFactory { return this.callOriginal(target, options); } if (uri.scheme === 'http' || uri.scheme === 'https') { - return mainThreadWindow.$openUri(uri, { allowTunneling: true }); + return mainThreadWindow.$openUri(uri, target, { allowTunneling: true }); } else if (uri.scheme === 'mailto' || uri.scheme === this._appUriScheme) { - return mainThreadWindow.$openUri(uri, {}); + return mainThreadWindow.$openUri(uri, target, {}); } return this.callOriginal(target, options); }; diff --git a/src/vs/workbench/api/common/extHostWindow.ts b/src/vs/workbench/api/common/extHostWindow.ts index 8ce82ac8b2d..f8c5d5fa3d7 100644 --- a/src/vs/workbench/api/common/extHostWindow.ts +++ b/src/vs/workbench/api/common/extHostWindow.ts @@ -39,7 +39,9 @@ export class ExtHostWindow implements ExtHostWindowShape { } openUri(stringOrUri: string | URI, options: IOpenUriOptions): Promise { + let uriAsString: string | undefined; if (typeof stringOrUri === 'string') { + uriAsString = stringOrUri; try { stringOrUri = URI.parse(stringOrUri); } catch (e) { @@ -51,7 +53,7 @@ export class ExtHostWindow implements ExtHostWindowShape { } else if (stringOrUri.scheme === Schemas.command) { return Promise.reject(`Invalid scheme '${stringOrUri.scheme}'`); } - return this._proxy.$openUri(stringOrUri, options); + return this._proxy.$openUri(stringOrUri, uriAsString, options); } async asExternalUri(uri: URI, options: IOpenUriOptions): Promise { From 6b41d230d0a90596ba2a85f5d636fab890cff32f Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 15 Nov 2019 10:57:11 +0100 Subject: [PATCH 57/88] :lipstick: --- src/vs/editor/browser/editorBrowser.ts | 4 +- .../editor/browser/widget/codeEditorWidget.ts | 4 +- .../editor/browser/widget/diffEditorWidget.ts | 30 ++-- .../editor/common/viewLayout/linesLayout.ts | 131 +++++++++--------- src/vs/editor/common/viewLayout/viewLayout.ts | 4 +- src/vs/editor/common/viewModel/viewModel.ts | 4 +- 6 files changed, 87 insertions(+), 90 deletions(-) diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index bcd0995f843..26b562f400f 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -16,7 +16,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; 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/linesLayout'; +import { EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IDiffComputationResult } from 'vs/editor/common/services/editorWorkerService'; @@ -672,7 +672,7 @@ export interface ICodeEditor extends editorCommon.IEditor { * Get the view zones. * @internal */ - getWhitespaces(): IEditorWhitespace[]; + getWhitespaces(): EditorWhitespace[]; /** * Get the vertical position (top offset) for the line w.r.t. to the first line. diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 357eeab2a9f..348487009ef 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -40,7 +40,7 @@ import * as modes from 'vs/editor/common/modes'; import { editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity } from 'vs/editor/common/view/editorColorRegistry'; import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; -import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; +import { EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -451,7 +451,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._modelData.viewModel.getVisibleRanges(); } - public getWhitespaces(): IEditorWhitespace[] { + public getWhitespaces(): EditorWhitespace[] { if (!this._modelData) { return []; } diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index e03249c867a..d97ef2e6f19 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -32,7 +32,7 @@ import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/s import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; -import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; +import { EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { InlineDecoration, InlineDecorationType, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -68,7 +68,7 @@ interface IEditorsZones { } interface IDiffEditorWidgetStyle { - getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones; + getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: EditorWhitespace[], modifiedWhitespaces: EditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones; setEnableSplitViewResizing(enableSplitViewResizing: boolean): void; applyColors(theme: ITheme): boolean; layout(): number; @@ -91,7 +91,7 @@ class VisualEditorState { this._decorations = []; } - public getForeignViewZones(allViewZones: IEditorWhitespace[]): IEditorWhitespace[] { + public getForeignViewZones(allViewZones: EditorWhitespace[]): EditorWhitespace[] { return allViewZones.filter((z) => !this._zonesMap[String(z.id)]); } @@ -1303,7 +1303,7 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi return hasChanges; } - public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones { + public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: EditorWhitespace[], modifiedWhitespaces: EditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones { // Get view zones modifiedWhitespaces = modifiedWhitespaces.sort((a, b) => { return a.afterLineNumber - b.afterLineNumber; @@ -1331,7 +1331,7 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi }; } - protected abstract _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones; + protected abstract _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: EditorWhitespace[], modifiedForeignVZ: EditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones; protected abstract _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations; protected abstract _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations; @@ -1352,10 +1352,10 @@ interface IMyViewZone { class ForeignViewZonesIterator { private _index: number; - private readonly _source: IEditorWhitespace[]; - public current: IEditorWhitespace | null; + private readonly _source: EditorWhitespace[]; + public current: EditorWhitespace | null; - constructor(source: IEditorWhitespace[]) { + constructor(source: EditorWhitespace[]) { this._source = source; this._index = -1; this.current = null; @@ -1375,10 +1375,10 @@ class ForeignViewZonesIterator { abstract class ViewZonesComputer { private readonly lineChanges: editorCommon.ILineChange[]; - private readonly originalForeignVZ: IEditorWhitespace[]; - private readonly modifiedForeignVZ: IEditorWhitespace[]; + private readonly originalForeignVZ: EditorWhitespace[]; + private readonly modifiedForeignVZ: EditorWhitespace[]; - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) { + constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: EditorWhitespace[], modifiedForeignVZ: EditorWhitespace[]) { this.lineChanges = lineChanges; this.originalForeignVZ = originalForeignVZ; this.modifiedForeignVZ = modifiedForeignVZ; @@ -1731,7 +1731,7 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffE return this._dataSource.getHeight(); } - protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones { + protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: EditorWhitespace[], modifiedForeignVZ: EditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones { let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ); return c.getViewZones(); } @@ -1859,7 +1859,7 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffE class SideBySideViewZonesComputer extends ViewZonesComputer { - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) { + constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: EditorWhitespace[], modifiedForeignVZ: EditorWhitespace[]) { super(lineChanges, originalForeignVZ, modifiedForeignVZ); } @@ -1911,7 +1911,7 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito // Nothing to do.. } - protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones { + protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: EditorWhitespace[], modifiedForeignVZ: EditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones { let computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor, renderIndicators); return computer.getViewZones(); } @@ -2019,7 +2019,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { private readonly modifiedEditorTabSize: number; private readonly renderIndicators: boolean; - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) { + constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: EditorWhitespace[], modifiedForeignVZ: EditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) { super(lineChanges, originalForeignVZ, modifiedForeignVZ); this.originalModel = originalEditor.getModel()!; this.modifiedEditorOptions = modifiedEditor.getOptions(); diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index e18482b2ef4..d3f4d359431 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -7,10 +7,12 @@ import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewL import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; import * as strings from 'vs/base/common/strings'; -export interface IEditorWhitespace { - readonly id: string; - readonly afterLineNumber: number; - readonly heightInLines: number; +export class EditorWhitespace { + constructor( + public readonly id: string, + public readonly afterLineNumber: number, + public readonly heightInLines: number, + ) { } } /** @@ -110,7 +112,7 @@ export class LinesLayout { let high = sortedArray.length; while (low < high) { - let mid = ((low + high) >>> 1); + const mid = ((low + high) >>> 1); if (value === sortedArray[mid]) { if (valueOrdinal < ordinals[mid]) { @@ -159,8 +161,8 @@ export class LinesLayout { heightInPx = heightInPx | 0; minWidth = minWidth | 0; - let id = this._instanceId + (++this._lastWhitespaceId); - let insertionIndex = LinesLayout.findInsertionIndex(this._afterLineNumbers, afterLineNumber, this._ordinals, ordinal); + const id = this._instanceId + (++this._lastWhitespaceId); + const insertionIndex = LinesLayout.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; @@ -180,10 +182,9 @@ export class LinesLayout { this._ordinals.splice(insertIndex, 0, ordinal); this._prefixSum.splice(insertIndex, 0, 0); - let keys = Object.keys(this._whitespaceId2Index); - for (let i = 0, len = keys.length; i < len; i++) { - let sid = keys[i]; - let oldIndex = this._whitespaceId2Index[sid]; + const keys = Object.keys(this._whitespaceId2Index); + for (const sid of keys) { + const oldIndex = this._whitespaceId2Index[sid]; if (oldIndex >= insertIndex) { this._whitespaceId2Index[sid] = oldIndex + 1; } @@ -217,7 +218,7 @@ export class LinesLayout { newHeightInPx = newHeightInPx | 0; if (this._whitespaceId2Index.hasOwnProperty(id)) { - let index = this._whitespaceId2Index[id]; + const index = this._whitespaceId2Index[id]; if (this._heights[index] !== newHeightInPx) { this._heights[index] = newHeightInPx; this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1); @@ -238,24 +239,24 @@ export class LinesLayout { newAfterLineNumber = newAfterLineNumber | 0; if (this._whitespaceId2Index.hasOwnProperty(id)) { - let index = this._whitespaceId2Index[id]; + const index = this._whitespaceId2Index[id]; if (this._afterLineNumbers[index] !== newAfterLineNumber) { // `afterLineNumber` changed for this whitespace // Record old ordinal - let ordinal = this._ordinals[index]; + const ordinal = this._ordinals[index]; // Record old height - let heightInPx = this._heights[index]; + const heightInPx = this._heights[index]; // Record old min width - let minWidth = this._minWidths[index]; + const minWidth = this._minWidths[index]; // Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace this.removeWhitespace(id); // And add it again - let insertionIndex = LinesLayout.findInsertionIndex(this._afterLineNumbers, newAfterLineNumber, this._ordinals, ordinal); + const insertionIndex = LinesLayout.findInsertionIndex(this._afterLineNumbers, newAfterLineNumber, this._ordinals, ordinal); this._insertWhitespaceAtIndex(id, insertionIndex, newAfterLineNumber, ordinal, heightInPx, minWidth); return true; @@ -272,7 +273,7 @@ export class LinesLayout { */ public removeWhitespace(id: string): boolean { if (this._whitespaceId2Index.hasOwnProperty(id)) { - let index = this._whitespaceId2Index[id]; + const index = this._whitespaceId2Index[id]; delete this._whitespaceId2Index[id]; this._removeWhitespaceAtIndex(index); this._minWidth = -1; /* marker for not being computed */ @@ -293,10 +294,9 @@ export class LinesLayout { this._prefixSum.splice(removeIndex, 1); this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, removeIndex - 1); - let keys = Object.keys(this._whitespaceId2Index); - for (let i = 0, len = keys.length; i < len; i++) { - let sid = keys[i]; - let oldIndex = this._whitespaceId2Index[sid]; + const keys = Object.keys(this._whitespaceId2Index); + for (const sid of keys) { + const oldIndex = this._whitespaceId2Index[sid]; if (oldIndex >= removeIndex) { this._whitespaceId2Index[sid] = oldIndex - 1; } @@ -315,7 +315,7 @@ export class LinesLayout { this._lineCount -= (toLineNumber - fromLineNumber + 1); for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) { - let afterLineNumber = this._afterLineNumbers[i]; + const afterLineNumber = this._afterLineNumbers[i]; if (fromLineNumber <= afterLineNumber && afterLineNumber <= toLineNumber) { // The line this whitespace was after has been deleted @@ -341,7 +341,7 @@ export class LinesLayout { this._lineCount += (toLineNumber - fromLineNumber + 1); for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) { - let afterLineNumber = this._afterLineNumbers[i]; + const afterLineNumber = this._afterLineNumbers[i]; if (fromLineNumber <= afterLineNumber) { this._afterLineNumbers[i] += (toLineNumber - fromLineNumber + 1); @@ -388,8 +388,8 @@ export class LinesLayout { * @return The sum of heights for all objects. */ public getLinesTotalHeight(): number { - let linesHeight = this._lineHeight * this._lineCount; - let whitespacesHeight = this.getWhitespacesTotalHeight(); + const linesHeight = this._lineHeight * this._lineCount; + const whitespacesHeight = this.getWhitespacesTotalHeight(); return linesHeight + whitespacesHeight; } @@ -401,7 +401,7 @@ export class LinesLayout { public getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber: number): number { lineNumber = lineNumber | 0; - let lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); + const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); if (lastWhitespaceBeforeLineNumber === -1) { return 0; @@ -414,14 +414,14 @@ export class LinesLayout { lineNumber = lineNumber | 0; // Find the whitespace before line number - let afterLineNumbers = this._afterLineNumbers; + const afterLineNumbers = this._afterLineNumbers; let low = 0; let high = afterLineNumbers.length - 1; while (low <= high) { - let delta = (high - low) | 0; - let halfDelta = (delta / 2) | 0; - let mid = (low + halfDelta) | 0; + const delta = (high - low) | 0; + const halfDelta = (delta / 2) | 0; + const mid = (low + halfDelta) | 0; if (afterLineNumbers[mid] < lineNumber) { if (mid + 1 >= afterLineNumbers.length || afterLineNumbers[mid + 1] >= lineNumber) { @@ -440,8 +440,8 @@ export class LinesLayout { private _findFirstWhitespaceAfterLineNumber(lineNumber: number): number { lineNumber = lineNumber | 0; - let lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); - let firstWhitespaceAfterLineNumber = lastWhitespaceBeforeLineNumber + 1; + const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); + const firstWhitespaceAfterLineNumber = lastWhitespaceBeforeLineNumber + 1; if (firstWhitespaceAfterLineNumber < this._heights.length) { return firstWhitespaceAfterLineNumber; @@ -476,7 +476,7 @@ export class LinesLayout { previousLinesHeight = 0; } - let previousWhitespacesHeight = this.getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber); + const previousWhitespacesHeight = this.getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber); return previousLinesHeight + previousWhitespacesHeight; } @@ -506,7 +506,7 @@ export class LinesLayout { * Check if `verticalOffset` is below all lines. */ public isAfterLines(verticalOffset: number): boolean { - let totalHeight = this.getLinesTotalHeight(); + const totalHeight = this.getLinesTotalHeight(); return verticalOffset > totalHeight; } @@ -531,9 +531,9 @@ export class LinesLayout { let maxLineNumber = linesCount; while (minLineNumber < maxLineNumber) { - let midLineNumber = ((minLineNumber + maxLineNumber) / 2) | 0; + const midLineNumber = ((minLineNumber + maxLineNumber) / 2) | 0; - let midLineNumberVerticalOffset = this.getVerticalOffsetForLineNumber(midLineNumber) | 0; + const midLineNumberVerticalOffset = this.getVerticalOffsetForLineNumber(midLineNumber) | 0; if (verticalOffset >= midLineNumberVerticalOffset + lineHeight) { // vertical offset is after mid line number @@ -602,7 +602,7 @@ export class LinesLayout { currentLineRelativeOffset -= bigNumbersDelta; } - let linesOffsets: number[] = []; + const linesOffsets: number[] = []; const verticalCenter = verticalOffset1 + (verticalOffset2 - verticalOffset1) / 2; let centeredLineNumber = -1; @@ -611,8 +611,8 @@ export class LinesLayout { for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { if (centeredLineNumber === -1) { - let currentLineTop = currentVerticalOffset; - let currentLineBottom = currentVerticalOffset + lineHeight; + const currentLineTop = currentVerticalOffset; + const currentLineBottom = currentVerticalOffset + lineHeight; if ((currentLineTop <= verticalCenter && verticalCenter < currentLineBottom) || currentLineTop > verticalCenter) { centeredLineNumber = lineNumber; } @@ -681,7 +681,7 @@ export class LinesLayout { public getVerticalOffsetForWhitespaceIndex(whitespaceIndex: number): number { whitespaceIndex = whitespaceIndex | 0; - let afterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex); + const afterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex); let previousLinesHeight: number; if (afterLineNumber >= 1) { @@ -702,28 +702,25 @@ export class LinesLayout { public getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset: number): number { verticalOffset = verticalOffset | 0; - let midWhitespaceIndex: number, - minWhitespaceIndex = 0, - maxWhitespaceIndex = this.getWhitespacesCount() - 1, - midWhitespaceVerticalOffset: number, - midWhitespaceHeight: number; + let minWhitespaceIndex = 0; + let maxWhitespaceIndex = this.getWhitespacesCount() - 1; if (maxWhitespaceIndex < 0) { return -1; } // Special case: nothing to be found - let maxWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(maxWhitespaceIndex); - let maxWhitespaceHeight = this.getHeightForWhitespaceIndex(maxWhitespaceIndex); + const maxWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(maxWhitespaceIndex); + const maxWhitespaceHeight = this.getHeightForWhitespaceIndex(maxWhitespaceIndex); if (verticalOffset >= maxWhitespaceVerticalOffset + maxWhitespaceHeight) { return -1; } while (minWhitespaceIndex < maxWhitespaceIndex) { - midWhitespaceIndex = Math.floor((minWhitespaceIndex + maxWhitespaceIndex) / 2); + const midWhitespaceIndex = Math.floor((minWhitespaceIndex + maxWhitespaceIndex) / 2); - midWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(midWhitespaceIndex); - midWhitespaceHeight = this.getHeightForWhitespaceIndex(midWhitespaceIndex); + const midWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(midWhitespaceIndex); + const midWhitespaceHeight = this.getHeightForWhitespaceIndex(midWhitespaceIndex); if (verticalOffset >= midWhitespaceVerticalOffset + midWhitespaceHeight) { // vertical offset is after whitespace @@ -748,7 +745,7 @@ export class LinesLayout { public getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null { verticalOffset = verticalOffset | 0; - let candidateIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset); + const candidateIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset); if (candidateIndex < 0) { return null; @@ -758,15 +755,15 @@ export class LinesLayout { return null; } - let candidateTop = this.getVerticalOffsetForWhitespaceIndex(candidateIndex); + const candidateTop = this.getVerticalOffsetForWhitespaceIndex(candidateIndex); if (candidateTop > verticalOffset) { return null; } - let candidateHeight = this.getHeightForWhitespaceIndex(candidateIndex); - let candidateId = this.getIdForWhitespaceIndex(candidateIndex); - let candidateAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(candidateIndex); + const candidateHeight = this.getHeightForWhitespaceIndex(candidateIndex); + const candidateId = this.getIdForWhitespaceIndex(candidateIndex); + const candidateAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(candidateIndex); return { id: candidateId, @@ -787,8 +784,8 @@ export class LinesLayout { verticalOffset1 = verticalOffset1 | 0; verticalOffset2 = verticalOffset2 | 0; - let startIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset1); - let endIndex = this.getWhitespacesCount() - 1; + const startIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset1); + const endIndex = this.getWhitespacesCount() - 1; if (startIndex < 0) { return []; @@ -796,8 +793,8 @@ export class LinesLayout { let result: IViewWhitespaceViewportData[] = []; for (let i = startIndex; i <= endIndex; i++) { - let top = this.getVerticalOffsetForWhitespaceIndex(i); - let height = this.getHeightForWhitespaceIndex(i); + const top = this.getVerticalOffsetForWhitespaceIndex(i); + const height = this.getHeightForWhitespaceIndex(i); if (top >= verticalOffset2) { break; } @@ -816,14 +813,14 @@ export class LinesLayout { /** * Get all whitespaces. */ - public getWhitespaces(): IEditorWhitespace[] { - let result: IEditorWhitespace[] = []; + public getWhitespaces(): EditorWhitespace[] { + let result: EditorWhitespace[] = []; for (let i = 0; i < this._heights.length; i++) { - result.push({ - id: this._ids[i], - afterLineNumber: this._afterLineNumbers[i], - heightInLines: this._heights[i] / this._lineHeight - }); + result.push(new EditorWhitespace( + this._ids[i], + this._afterLineNumbers[i], + this._heights[i] / this._lineHeight + )); } return result; } diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index cab08b23f44..6fd8a951a57 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -8,7 +8,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { LinesLayout, IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; +import { LinesLayout, EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel'; @@ -235,7 +235,7 @@ export class ViewLayout extends Disposable implements IViewLayout { const visibleBox = this.getCurrentViewport(); return this._linesLayout.getWhitespaceViewportData(visibleBox.top, visibleBox.top + visibleBox.height); } - public getWhitespaces(): IEditorWhitespace[] { + public getWhitespaces(): EditorWhitespace[] { return this._linesLayout.getWhitespaces(); } diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 3dd75a94d4b..777195ac729 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -13,7 +13,7 @@ import { INewScrollPosition } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, TextModelResolvedOptions } from 'vs/editor/common/model'; import { IViewEventListener } from 'vs/editor/common/view/viewEvents'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; +import { EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ITheme } from 'vs/platform/theme/common/themeService'; export interface IViewWhitespaceViewportData { @@ -61,7 +61,7 @@ export interface IViewLayout { getLinesViewportData(): IPartialViewLinesViewportData; getLinesViewportDataAtScrollTop(scrollTop: number): IPartialViewLinesViewportData; - getWhitespaces(): IEditorWhitespace[]; + getWhitespaces(): EditorWhitespace[]; isAfterLines(verticalOffset: number): boolean; getLineNumberAtVerticalOffset(verticalOffset: number): number; From efe0e246b7ebe9eedfaf3ac01ee349712daba528 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 15 Nov 2019 10:58:50 +0100 Subject: [PATCH 58/88] enable strictFunctionTypes in VS Code codebase #81574 --- src/vs/base/common/path.ts | 4 ++-- src/vs/editor/contrib/folding/folding.ts | 2 +- src/vs/editor/contrib/folding/foldingModel.ts | 8 ++++++-- .../contrib/extensions/browser/extensionTipsService.ts | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/vs/base/common/path.ts b/src/vs/base/common/path.ts index 28b7d5e61cc..5f1739053bb 100644 --- a/src/vs/base/common/path.ts +++ b/src/vs/base/common/path.ts @@ -69,11 +69,11 @@ function validateString(value: string, name: string) { } } -function isPathSeparator(code: number) { +function isPathSeparator(code: number | undefined) { return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; } -function isPosixPathSeparator(code: number) { +function isPosixPathSeparator(code: number | undefined) { return code === CHAR_FORWARD_SLASH; } diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index d000560f648..86f04e0db86 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -423,7 +423,7 @@ export class FoldingController extends Disposable implements IEditorContribution if (iconClicked || isCollapsed) { let toToggle = [region]; if (e.event.middleButton || e.event.shiftKey) { - toToggle.push(...foldingModel.getRegionsInside(region, r => r.isCollapsed === isCollapsed)); + toToggle.push(...foldingModel.getRegionsInside(region, (r: FoldingRegion) => r.isCollapsed === isCollapsed)); } foldingModel.toggleCollapseState(toToggle); this.reveal({ lineNumber, column: 1 }); diff --git a/src/vs/editor/contrib/folding/foldingModel.ts b/src/vs/editor/contrib/folding/foldingModel.ts index 44feb79cdf2..93a52a14728 100644 --- a/src/vs/editor/contrib/folding/foldingModel.ts +++ b/src/vs/editor/contrib/folding/foldingModel.ts @@ -204,7 +204,7 @@ export class FoldingModel { return null; } - getRegionsInside(region: FoldingRegion | null, filter?: (r: FoldingRegion, level?: number) => boolean): FoldingRegion[] { + getRegionsInside(region: FoldingRegion | null, filter?: RegionFilter | RegionFilterWithLevel): FoldingRegion[] { let result: FoldingRegion[] = []; let index = region ? region.regionIndex + 1 : 0; let endLineNumber = region ? region.endLineNumber : Number.MAX_VALUE; @@ -229,7 +229,7 @@ export class FoldingModel { for (let i = index, len = this._regions.length; i < len; i++) { let current = this._regions.toRegion(i); if (this._regions.getStartLineNumber(i) < endLineNumber) { - if (!filter || filter(current)) { + if (!filter || (filter as RegionFilter)(current)) { result.push(current); } } else { @@ -242,6 +242,10 @@ export class FoldingModel { } +type RegionFilter = (r: FoldingRegion) => boolean; +type RegionFilterWithLevel = (r: FoldingRegion, level: number) => boolean; + + /** * Collapse or expand the regions at the given locations * @param levels The number of levels. Use 1 to only impact the regions at the location, use Number.MAX_VALUE for all levels. diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts index f3cde426dd0..3d934b3d584 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts @@ -1124,7 +1124,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (context.res.statusCode !== 200) { return Promise.resolve(undefined); } - return asJson(context).then((result: { [key: string]: any }) => { + return asJson(context).then((result: { [key: string]: any } | null) => { if (!result) { return; } From 9b61e277c22ad861a5c13869927d11f290a41fb2 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 15 Nov 2019 11:03:30 +0100 Subject: [PATCH 59/88] Support for `--force-renderer-accessibility` Electron argument, needed for Linux accessibility. For #84833 --- src/vs/platform/environment/common/environment.ts | 1 + src/vs/platform/environment/node/argv.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 945da642232..96031eb4ec7 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -86,6 +86,7 @@ export interface ParsedArgs { 'disable-gpu'?: boolean; 'nolazy'?: boolean; 'force-device-scale-factor'?: string; + 'force-renderer-accessibility'?: boolean; } export const IEnvironmentService = createDecorator('environmentService'); diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index b97cc4b20b1..6832b93c5cb 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -118,6 +118,7 @@ export const OPTIONS: OptionDescriptions> = { 'inspect-brk': { type: 'string' }, 'nolazy': { type: 'boolean' }, // node inspect 'force-device-scale-factor': { type: 'string' }, + 'force-renderer-accessibility': { type: 'boolean' }, '_urls': { type: 'string[]' }, _: { type: 'string[]' } // main arguments From 0b33f025db1f5e189d42ed92b1608fcf48ea9293 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Nov 2019 11:05:35 +0100 Subject: [PATCH 60/88] editors - more explicit editor view model for diff editors --- .../browser/parts/editor/editorGroupView.ts | 20 ++- src/vs/workbench/common/editor.ts | 23 +-- src/vs/workbench/common/editor/editorGroup.ts | 138 +++++++++++------- .../test/common/editor/editorGroups.test.ts | 47 ++++-- 4 files changed, 142 insertions(+), 86 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 01eab7e6856..ba94efe8d29 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -514,15 +514,21 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const editorsToClose = [editor]; // Include both sides of side by side editors when being closed and not opened multiple times - if (editor instanceof SideBySideEditorInput && !this.accessor.groups.some(groupView => groupView.group.contains(editor))) { + if (editor instanceof SideBySideEditorInput && !this.accessor.groups.some(groupView => groupView.group.containsEditorByInstance(editor))) { editorsToClose.push(editor.master, editor.details); } - // Close the editor when it is no longer open in any group including diff editors + // Dispose the editor when it is no longer open in any group including diff editors editorsToClose.forEach(editorToClose => { const resource = editorToClose ? editorToClose.getResource() : undefined; // prefer resource to not close right-hand side editors of a diff editor - if (!this.accessor.groups.some(groupView => groupView.group.contains(resource || editorToClose))) { - editorToClose.close(); + if (!this.accessor.groups.some(groupView => { + if (resource) { + return groupView.group.containsEditorByResource(resource, SideBySideEditor.MASTER); + } + + return groupView.group.containsEditorByInstance(editorToClose); + })) { + editorToClose.dispose(); } }); @@ -578,7 +584,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { editors.forEach(editor => { if (this._group.isActive(editor)) { activeEditor = editor; - } else if (this._group.contains(editor)) { + } else if (this._group.containsEditorByInstance(editor)) { inactiveEditors.push(editor); } }); @@ -770,7 +776,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } isOpened(editor: EditorInput): boolean { - return this._group.contains(editor); + return this._group.containsEditorByInstance(editor); } focus(): void { @@ -1264,7 +1270,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private async doHandleDirty(editor: EditorInput): Promise { if ( !editor.isDirty() || // editor must be dirty - this.accessor.groups.some(groupView => groupView !== this && groupView.group.contains(editor, true /* support side by side */)) || // editor is opened in other group + this.accessor.groups.some(groupView => groupView !== this && groupView.group.containsEditorByInstance(editor, SideBySideEditor.MASTER /* support side by side */)) || // editor is opened in other group editor instanceof SideBySideEditorInput && this.isOpened(editor.master) // side by side editor master is still opened ) { return false; diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 1764e7ed832..e6a70ab4702 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -333,14 +333,14 @@ export interface IEditorInput extends IDisposable { */ export abstract class EditorInput extends Disposable implements IEditorInput { - protected readonly _onDidChangeDirty: Emitter = this._register(new Emitter()); - readonly onDidChangeDirty: Event = this._onDidChangeDirty.event; + protected readonly _onDidChangeDirty = this._register(new Emitter()); + readonly onDidChangeDirty = this._onDidChangeDirty.event; - protected readonly _onDidChangeLabel: Emitter = this._register(new Emitter()); - readonly onDidChangeLabel: Event = this._onDidChangeLabel.event; + protected readonly _onDidChangeLabel = this._register(new Emitter()); + readonly onDidChangeLabel = this._onDidChangeLabel.event; - private readonly _onDispose: Emitter = this._register(new Emitter()); - readonly onDispose: Event = this._onDispose.event; + private readonly _onDispose = this._register(new Emitter()); + readonly onDispose = this._onDispose.event; private disposed: boolean = false; @@ -429,13 +429,6 @@ export abstract class EditorInput extends Disposable implements IEditorInput { return Promise.resolve(true); } - /** - * Called when this input is no longer opened in any editor. Subclasses can free resources as needed. - */ - close(): void { - this.dispose(); - } - /** * Subclasses can set this to false if it does not make sense to split the editor input. */ @@ -640,8 +633,8 @@ export interface ITextEditorModel extends IEditorModel { */ export class EditorModel extends Disposable implements IEditorModel { - private readonly _onDispose: Emitter = this._register(new Emitter()); - readonly onDispose: Event = this._onDispose.event; + private readonly _onDispose = this._register(new Emitter()); + readonly onDispose = this._onDispose.event; /** * Causes this model to load returning a promise when loading is completed. diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index 72f1223b558..13df58c3ac5 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -60,31 +60,31 @@ export class EditorGroup extends Disposable { //#region events private readonly _onDidEditorActivate = this._register(new Emitter()); - readonly onDidEditorActivate: Event = this._onDidEditorActivate.event; + readonly onDidEditorActivate = this._onDidEditorActivate.event; private readonly _onDidEditorOpen = this._register(new Emitter()); - readonly onDidEditorOpen: Event = this._onDidEditorOpen.event; + readonly onDidEditorOpen = this._onDidEditorOpen.event; private readonly _onDidEditorClose = this._register(new Emitter()); - readonly onDidEditorClose: Event = this._onDidEditorClose.event; + readonly onDidEditorClose = this._onDidEditorClose.event; private readonly _onDidEditorDispose = this._register(new Emitter()); - readonly onDidEditorDispose: Event = this._onDidEditorDispose.event; + readonly onDidEditorDispose = this._onDidEditorDispose.event; private readonly _onDidEditorBecomeDirty = this._register(new Emitter()); - readonly onDidEditorBecomeDirty: Event = this._onDidEditorBecomeDirty.event; + readonly onDidEditorBecomeDirty = this._onDidEditorBecomeDirty.event; private readonly _onDidEditorLabelChange = this._register(new Emitter()); - readonly onDidEditorLabelChange: Event = this._onDidEditorLabelChange.event; + readonly onDidEditorLabelChange = this._onDidEditorLabelChange.event; private readonly _onDidEditorMove = this._register(new Emitter()); - readonly onDidEditorMove: Event = this._onDidEditorMove.event; + readonly onDidEditorMove = this._onDidEditorMove.event; private readonly _onDidEditorPin = this._register(new Emitter()); - readonly onDidEditorPin: Event = this._onDidEditorPin.event; + readonly onDidEditorPin = this._onDidEditorPin.event; private readonly _onDidEditorUnpin = this._register(new Emitter()); - readonly onDidEditorUnpin: Event = this._onDidEditorUnpin.event; + readonly onDidEditorUnpin = this._onDidEditorUnpin.event; //#endregion @@ -93,7 +93,10 @@ export class EditorGroup extends Disposable { private editors: EditorInput[] = []; private mru: EditorInput[] = []; - private mapResourceToEditorCount: ResourceMap = new ResourceMap(); + + private mapResourceToEditorCount = new ResourceMap(); + private mapResourceToMasterEditorCount = new ResourceMap(); + private mapResourceToDetailsEditorCount = new ResourceMap(); private preview: EditorInput | null = null; // editor in preview state private active: EditorInput | null = null; // editor in active state @@ -143,7 +146,7 @@ export class EditorGroup extends Disposable { } const resource: URI = arg1; - if (!this.contains(resource)) { + if (!this.containsEditorByResource(resource, SideBySideEditor.MASTER)) { return undefined; // fast check for resource opened or not } @@ -509,7 +512,7 @@ export class EditorGroup extends Disposable { // Add if (!del && editor) { this.mru.push(editor); // make it LRU editor - this.updateResourceMap(editor, false /* add */); // add new to resource map + this.updateResourceCounterMap(editor, false /* add */); // add new to resource map } // Remove / Replace @@ -519,42 +522,63 @@ export class EditorGroup extends Disposable { // Remove if (del && !editor) { this.mru.splice(indexInMRU, 1); // remove from MRU - this.updateResourceMap(editorToDeleteOrReplace, true /* delete */); // remove from resource map + this.updateResourceCounterMap(editorToDeleteOrReplace, true /* delete */); // remove from resource map } // Replace else if (del && editor) { this.mru.splice(indexInMRU, 1, editor); // replace MRU at location - this.updateResourceMap(editor, false /* add */); // add new to resource map - this.updateResourceMap(editorToDeleteOrReplace, true /* delete */); // remove replaced from resource map + this.updateResourceCounterMap(editor, false /* add */); // add new to resource map + this.updateResourceCounterMap(editorToDeleteOrReplace, true /* delete */); // remove replaced from resource map } } } - private updateResourceMap(editor: EditorInput, remove: boolean): void { - const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); + private updateResourceCounterMap(editor: EditorInput, remove: boolean): void { + + // Remember editor resource in map for fast lookup + const resource = toResource(editor); if (resource) { + this.doUpdateResourceCounterMap(resource, this.mapResourceToEditorCount, remove); + } - // It is possible to have the same resource opened twice (once as normal input and once as diff input) - // So we need to do ref counting on the resource to provide the correct picture - const counter = this.mapResourceToEditorCount.get(resource) || 0; - - // Add - let newCounter: number; - if (!remove) { - newCounter = counter + 1; + // Side by Side editor: store resource information + // for master and details side in separate maps + // to be able to lookup properly. + if (editor instanceof SideBySideEditorInput) { + const masterResource = toResource(editor.master); + if (masterResource) { + this.doUpdateResourceCounterMap(masterResource, this.mapResourceToMasterEditorCount, remove); } - // Delete - else { - newCounter = counter - 1; + const detailsResource = toResource(editor.details); + if (detailsResource) { + this.doUpdateResourceCounterMap(detailsResource, this.mapResourceToDetailsEditorCount, remove); } + } + } - if (newCounter > 0) { - this.mapResourceToEditorCount.set(resource, newCounter); - } else { - this.mapResourceToEditorCount.delete(resource); - } + private doUpdateResourceCounterMap(resource: URI, map: ResourceMap, remove: boolean): void { + + // It is possible to have the same resource opened twice (once as normal input and once as diff input) + // So we need to do ref counting on the resource to provide the correct picture + const counter = map.get(resource) || 0; + + // Add + let newCounter: number; + if (!remove) { + newCounter = counter + 1; + } + + // Delete + else { + newCounter = counter - 1; + } + + if (newCounter > 0) { + map.set(resource, newCounter); + } else { + map.delete(resource); } } @@ -572,28 +596,38 @@ export class EditorGroup extends Disposable { return -1; } - contains(editorOrResource: EditorInput | URI): boolean; - contains(editor: EditorInput, supportSideBySide?: boolean): boolean; - contains(editorOrResource: EditorInput | URI, supportSideBySide?: boolean): boolean { - if (editorOrResource instanceof EditorInput) { - const index = this.indexOf(editorOrResource); + containsEditorByResource(resource: URI, supportSideBySide?: SideBySideEditor): boolean { + + // Check if exact editor match is contained + let counter = this.mapResourceToEditorCount.get(resource); + + // Optionally search by master/detail resource if instructed + if (supportSideBySide === SideBySideEditor.MASTER) { + counter = counter || this.mapResourceToMasterEditorCount.get(resource); + } else if (supportSideBySide === SideBySideEditor.DETAILS) { + counter = counter || this.mapResourceToDetailsEditorCount.get(resource); + } + + return typeof counter === 'number' && counter > 0; + } + + containsEditorByInstance(editor: EditorInput, supportSideBySide?: SideBySideEditor): boolean { + + // Check if exact editor match is contained + const index = this.indexOf(editor); + if (index >= 0) { + return true; + } + + // Optionally search by master/detail input if instructed + if (supportSideBySide && editor instanceof SideBySideEditorInput) { + const index = this.indexOf(supportSideBySide === SideBySideEditor.MASTER ? editor.master : editor.details); if (index >= 0) { return true; } - - if (supportSideBySide && editorOrResource instanceof SideBySideEditorInput) { - const index = this.indexOf(editorOrResource.master); - if (index >= 0) { - return true; - } - } - - return false; } - const counter = this.mapResourceToEditorCount.get(editorOrResource); - - return typeof counter === 'number' && counter > 0; + return false; } private setMostRecentlyUsed(editor: EditorInput): void { @@ -620,6 +654,8 @@ export class EditorGroup extends Disposable { group.editors = this.editors.slice(0); group.mru = this.mru.slice(0); group.mapResourceToEditorCount = this.mapResourceToEditorCount.clone(); + group.mapResourceToMasterEditorCount = this.mapResourceToMasterEditorCount.clone(); + group.mapResourceToDetailsEditorCount = this.mapResourceToDetailsEditorCount.clone(); group.preview = this.preview; group.active = this.active; group.editorOpenPositioning = this.editorOpenPositioning; @@ -678,7 +714,7 @@ export class EditorGroup extends Disposable { const editor = factory.deserialize(this.instantiationService, e.value); if (editor) { this.registerEditorListeners(editor); - this.updateResourceMap(editor, false /* add */); + this.updateResourceCounterMap(editor, false /* add */); } return editor; diff --git a/src/vs/workbench/test/common/editor/editorGroups.test.ts b/src/vs/workbench/test/common/editor/editorGroups.test.ts index 46805b2c9da..9f8721b7a68 100644 --- a/src/vs/workbench/test/common/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/common/editor/editorGroups.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { EditorGroup, ISerializedEditorGroup, EditorCloseEvent } from 'vs/workbench/common/editor/editorGroup'; -import { Extensions as EditorExtensions, IEditorInputFactoryRegistry, EditorInput, IFileEditorInput, IEditorInputFactory, CloseDirection } from 'vs/workbench/common/editor'; +import { Extensions as EditorExtensions, IEditorInputFactoryRegistry, EditorInput, IFileEditorInput, IEditorInputFactory, CloseDirection, SideBySideEditor } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { TestLifecycleService, TestContextService, TestStorageService } from 'vs/workbench/test/workbenchTestServices'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -193,13 +193,17 @@ suite('Workbench editor groups', () => { const input1 = input(); const input2 = input(); - const diffInput = new DiffEditorInput('name', 'description', input1, input2); + const diffInput1 = new DiffEditorInput('name', 'description', input1, input2); + const diffInput2 = new DiffEditorInput('name', 'description', input2, input1); group.openEditor(input2, { pinned: true, active: true }); - assert.equal(group.contains(input2), true); - assert.equal(group.contains(diffInput), false); - assert.equal(group.contains(diffInput, true), true); + assert.equal(group.containsEditorByInstance(input2, SideBySideEditor.MASTER), true); + assert.equal(group.containsEditorByInstance(diffInput1), false); + assert.equal(group.containsEditorByInstance(diffInput1, SideBySideEditor.MASTER), true); + assert.equal(group.containsEditorByInstance(diffInput1, SideBySideEditor.DETAILS), false); + assert.equal(group.containsEditorByInstance(diffInput2, SideBySideEditor.MASTER), false); + assert.equal(group.containsEditorByInstance(diffInput2, SideBySideEditor.DETAILS), true); }); test('group serialization', function () { @@ -1171,36 +1175,53 @@ suite('Workbench editor groups', () => { const input1 = input(undefined, false, input1Resource); group1.openEditor(input1); - assert.ok(group1.contains(input1Resource)); + assert.ok(group1.containsEditorByResource(input1Resource)); assert.equal(group1.getEditor(input1Resource), input1); assert.ok(!group1.getEditor(input1ResourceUpper)); - assert.ok(!group1.contains(input1ResourceUpper)); + assert.ok(!group1.containsEditorByResource(input1ResourceUpper)); group2.openEditor(input1); group1.closeEditor(input1); - assert.ok(!group1.contains(input1Resource)); + assert.ok(!group1.containsEditorByResource(input1Resource)); assert.ok(!group1.getEditor(input1Resource)); assert.ok(!group1.getEditor(input1ResourceUpper)); - assert.ok(group2.contains(input1Resource)); + assert.ok(group2.containsEditorByResource(input1Resource)); assert.equal(group2.getEditor(input1Resource), input1); const input1ResourceClone = URI.file('/hello/world.txt'); const input1Clone = input(undefined, false, input1ResourceClone); group1.openEditor(input1Clone); - assert.ok(group1.contains(input1Resource)); + assert.ok(group1.containsEditorByResource(input1Resource)); group2.closeEditor(input1); - assert.ok(group1.contains(input1Resource)); + assert.ok(group1.containsEditorByResource(input1Resource)); assert.equal(group1.getEditor(input1Resource), input1Clone); - assert.ok(!group2.contains(input1Resource)); + assert.ok(!group2.containsEditorByResource(input1Resource)); group1.closeEditor(input1Clone); - assert.ok(!group1.contains(input1Resource)); + assert.ok(!group1.containsEditorByResource(input1Resource)); + + const masterResource = URI.file('/hello/world2.txt'); + const masterInput = input(undefined, false, masterResource); + + const detailsResource = URI.file('/hello/world3.txt'); + const detailsInput = input(undefined, false, detailsResource); + + const diffEditorInput = new DiffEditorInput('name', 'description', detailsInput, masterInput); + group1.openEditor(diffEditorInput); + + assert.equal(group1.containsEditorByResource(masterResource), false); + assert.equal(group1.containsEditorByResource(masterResource, SideBySideEditor.MASTER), true); + assert.equal(group1.containsEditorByResource(masterResource, SideBySideEditor.DETAILS), false); + + assert.equal(group1.containsEditorByResource(detailsResource), false); + assert.equal(group1.containsEditorByResource(detailsResource, SideBySideEditor.MASTER), false); + assert.equal(group1.containsEditorByResource(detailsResource, SideBySideEditor.DETAILS), true); }); test('Multiple Editors - Editor Dispose', function () { From 742bb9e51f5605d2e00041e57f95f6fe54cf915a Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 15 Nov 2019 11:08:23 +0100 Subject: [PATCH 61/88] Move ViewZoneChangeAccessor to ViewZones --- src/vs/editor/browser/view/viewImpl.ts | 58 ++++--------------- .../browser/viewParts/viewZones/viewZones.ts | 47 ++++++++++++++- 2 files changed, 55 insertions(+), 50 deletions(-) diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 42ef1f6f542..02b337315c8 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -11,7 +11,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler'; import { ITextAreaHandlerHelper, TextAreaHandler } from 'vs/editor/browser/controller/textAreaHandler'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { IContentWidget, IContentWidgetPosition, IOverlayWidget, IOverlayWidgetPosition, IMouseTarget, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; import { ICommandDelegate, ViewController } from 'vs/editor/browser/view/viewController'; import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; import { ContentViewOverlays, MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays'; @@ -51,17 +51,15 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; export interface IContentWidgetData { - widget: editorBrowser.IContentWidget; - position: editorBrowser.IContentWidgetPosition | null; + widget: IContentWidget; + position: IContentWidgetPosition | null; } export interface IOverlayWidgetData { - widget: editorBrowser.IOverlayWidget; - position: editorBrowser.IOverlayWidgetPosition | null; + widget: IOverlayWidget; + position: IOverlayWidgetPosition | null; } -const invalidFunc = () => { throw new Error(`Invalid change accessor`); }; - export class View extends ViewEventHandler { private readonly eventDispatcher: ViewEventDispatcher; @@ -348,7 +346,7 @@ export class View extends ViewEventHandler { super.dispose(); } - private _renderOnce(callback: () => any): any { + private _renderOnce(callback: () => T): T { const r = safeInvokeNoArg(callback); this._scheduleRender(); return r; @@ -458,7 +456,7 @@ export class View extends ViewEventHandler { return visibleRange.left; } - public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget | null { + public getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget | null { return this.pointerHandler.getTargetAtClientPoint(clientX, clientY); } @@ -466,42 +464,15 @@ export class View extends ViewEventHandler { return new OverviewRuler(this._context, cssClassName); } - public change(callback: (changeAccessor: editorBrowser.IViewZoneChangeAccessor) => any): boolean { - let zonesHaveChanged = false; - - this._renderOnce(() => { - const changeAccessor: editorBrowser.IViewZoneChangeAccessor = { - addZone: (zone: editorBrowser.IViewZone): string => { - zonesHaveChanged = true; - return this.viewZones.addZone(zone); - }, - removeZone: (id: string): void => { - if (!id) { - return; - } - zonesHaveChanged = this.viewZones.removeZone(id) || zonesHaveChanged; - }, - layoutZone: (id: string): void => { - if (!id) { - return; - } - zonesHaveChanged = this.viewZones.layoutZone(id) || zonesHaveChanged; - } - }; - - safeInvoke1Arg(callback, changeAccessor); - - // Invalidate changeAccessor - changeAccessor.addZone = invalidFunc; - changeAccessor.removeZone = invalidFunc; - changeAccessor.layoutZone = invalidFunc; - + public change(callback: (changeAccessor: IViewZoneChangeAccessor) => any): boolean { + return this._renderOnce(() => { + const zonesHaveChanged = this.viewZones.changeViewZones(callback); if (zonesHaveChanged) { this._context.viewLayout.onHeightMaybeChanged(); this._context.privateViewEventBus.emit(new viewEvents.ViewZonesChangedEvent()); } + return zonesHaveChanged; }); - return zonesHaveChanged; } public render(now: boolean, everything: boolean): void { @@ -582,10 +553,3 @@ function safeInvokeNoArg(func: Function): any { } } -function safeInvoke1Arg(func: Function, arg1: any): any { - try { - return func(arg1); - } catch (e) { - onUnexpectedError(e); - } -} diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index 6d7e1473089..c82db418cee 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -5,7 +5,7 @@ import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IViewZone } from 'vs/editor/browser/editorBrowser'; +import { IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; import { ViewPart } from 'vs/editor/browser/view/viewPart'; import { Position } from 'vs/editor/common/core/position'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; @@ -14,7 +14,6 @@ import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; - export interface IMyViewZone { whitespaceId: string; delegate: IViewZone; @@ -29,6 +28,8 @@ interface IComputedViewZoneProps { minWidthInPx: number; } +const invalidFunc = () => { throw new Error(`Invalid change accessor`); }; + export class ViewZones extends ViewPart { private _zones: { [id: string]: IMyViewZone; }; @@ -138,7 +139,6 @@ export class ViewZones extends ViewPart { return 10000; } - private _computeWhitespaceProps(zone: IViewZone): IComputedViewZoneProps { if (zone.afterLineNumber === 0) { return { @@ -188,6 +188,39 @@ export class ViewZones extends ViewPart { }; } + public changeViewZones(callback: (changeAccessor: IViewZoneChangeAccessor) => any): boolean { + + let zonesHaveChanged = false; + + const changeAccessor: IViewZoneChangeAccessor = { + addZone: (zone: IViewZone): string => { + zonesHaveChanged = true; + return this.addZone(zone); + }, + removeZone: (id: string): void => { + if (!id) { + return; + } + zonesHaveChanged = this.removeZone(id) || zonesHaveChanged; + }, + layoutZone: (id: string): void => { + if (!id) { + return; + } + zonesHaveChanged = this.layoutZone(id) || zonesHaveChanged; + } + }; + + safeInvoke1Arg(callback, changeAccessor); + + // Invalidate changeAccessor + changeAccessor.addZone = invalidFunc; + changeAccessor.removeZone = invalidFunc; + changeAccessor.layoutZone = invalidFunc; + + return zonesHaveChanged; + } + 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); @@ -365,3 +398,11 @@ export class ViewZones extends ViewPart { } } } + +function safeInvoke1Arg(func: Function, arg1: any): any { + try { + return func(arg1); + } catch (e) { + onUnexpectedError(e); + } +} From 61c3f82c6519ae9fcf3e1599984d6d63db703b35 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 15 Nov 2019 11:13:00 +0100 Subject: [PATCH 62/88] Fix #83227 --- .../node/extensionManagementService.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index a36425204f7..5bfc2bb66c1 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -217,6 +217,16 @@ export class ExtensionManagementService extends Disposable implements IExtension } else if (semver.gt(existing.manifest.version, manifest.version)) { return this.uninstall(existing, true); } + } else { + // Remove the extension with same version if it is already uninstalled. + // Installing a VSIX extension shall replace the existing extension always. + return this.unsetUninstalledAndGetLocal(identifierWithVersion) + .then(existing => { + if (existing) { + return this.removeExtension(existing, 'existing').then(null, e => Promise.reject(new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)))); + } + return undefined; + }); } return undefined; }) From c54ed5925dadc71f47295257a60bc4d7ef0a2cfa Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Nov 2019 11:13:33 +0100 Subject: [PATCH 63/88] editors - :polish: methods --- .../browser/parts/editor/editorActions.ts | 2 +- .../browser/parts/editor/editorCommands.ts | 10 +-- .../browser/parts/editor/editorGroupView.ts | 4 +- .../browser/parts/editor/tabsTitleControl.ts | 18 ++--- src/vs/workbench/common/editor/editorGroup.ts | 22 +----- .../editor/common/editorGroupsService.ts | 10 +-- .../test/browser/editorGroupsService.test.ts | 76 +++++++++---------- .../test/common/editor/editorGroups.test.ts | 29 +++++-- .../workbench/test/workbenchTestServices.ts | 2 +- 9 files changed, 85 insertions(+), 88 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 0c2e0156455..92fd5dd5f94 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -507,7 +507,7 @@ export class CloseOneEditorAction extends Action { // Close specific editor in group if (typeof editorIndex === 'number') { - const editorAtIndex = group.getEditor(editorIndex); + const editorAtIndex = group.getEditorByIndex(editorIndex); if (editorAtIndex) { return group.closeEditor(editorAtIndex); } diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 38bdcd700ce..682dbaedff1 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -318,7 +318,7 @@ function registerOpenEditorAtIndexCommands(): void { const editorService = accessor.get(IEditorService); const activeControl = editorService.activeControl; if (activeControl) { - const editor = activeControl.group.getEditor(editorIndex); + const editor = activeControl.group.getEditorByIndex(editorIndex); if (editor) { editorService.openEditor(editor); } @@ -448,7 +448,7 @@ export function splitEditor(editorGroupService: IEditorGroupsService, direction: // Split editor (if it can be split) let editorToCopy: IEditorInput | undefined; if (context && typeof context.editorIndex === 'number') { - editorToCopy = sourceGroup.getEditor(context.editorIndex); + editorToCopy = sourceGroup.getEditorByIndex(context.editorIndex); } else { editorToCopy = types.withNullAsUndefined(sourceGroup.activeEditor); } @@ -548,7 +548,7 @@ function registerCloseEditorCommands() { if (group) { const editors = coalesce(contexts .filter(context => context.groupId === groupId) - .map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor)); + .map(context => typeof context.editorIndex === 'number' ? group.getEditorByIndex(context.editorIndex) : group.activeEditor)); return group.closeEditors(editors); } @@ -603,7 +603,7 @@ function registerCloseEditorCommands() { if (group) { const editors = contexts .filter(context => context.groupId === groupId) - .map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor); + .map(context => typeof context.editorIndex === 'number' ? group.getEditorByIndex(context.editorIndex) : group.activeEditor); const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1); if (group.activeEditor) { @@ -715,7 +715,7 @@ function resolveCommandsContext(editorGroupService: IEditorGroupsService, contex // Resolve from context let group = context && typeof context.groupId === 'number' ? editorGroupService.getGroup(context.groupId) : undefined; - let editor = group && context && typeof context.editorIndex === 'number' ? types.withNullAsUndefined(group.getEditor(context.editorIndex)) : undefined; + let editor = group && context && typeof context.editorIndex === 'number' ? types.withNullAsUndefined(group.getEditorByIndex(context.editorIndex)) : undefined; let control = group ? group.activeControl : undefined; // Fallback to active group as needed diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index ba94efe8d29..3bcbf85e0f7 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -767,8 +767,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return this.editors; } - getEditor(index: number): EditorInput | undefined { - return this._group.getEditor(index); + getEditorByIndex(index: number): EditorInput | undefined { + return this._group.getEditorByIndex(index); } getIndexOfEditor(editor: EditorInput): number { diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index d22c4b9ec41..d42242fe112 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -501,7 +501,7 @@ export class TabsTitleControl extends TitleControl { } // Open tabs editor - const input = this.group.getEditor(index); + const input = this.group.getEditorByIndex(index); if (input) { this.group.openEditor(input); } @@ -512,7 +512,7 @@ export class TabsTitleControl extends TitleControl { const showContextMenu = (e: Event) => { EventHelper.stop(e); - const input = this.group.getEditor(index); + const input = this.group.getEditorByIndex(index); if (input) { this.onContextMenu(input, e, tab); } @@ -562,7 +562,7 @@ export class TabsTitleControl extends TitleControl { // Run action on Enter/Space if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { handled = true; - const input = this.group.getEditor(index); + const input = this.group.getEditorByIndex(index); if (input) { this.group.openEditor(input); } @@ -581,7 +581,7 @@ export class TabsTitleControl extends TitleControl { targetIndex = this.group.count - 1; } - const target = this.group.getEditor(targetIndex); + const target = this.group.getEditorByIndex(targetIndex); if (target) { handled = true; this.group.openEditor(target, { preserveFocus: true }); @@ -603,7 +603,7 @@ export class TabsTitleControl extends TitleControl { disposables.add(addDisposableListener(tab, EventType.DBLCLICK, (e: MouseEvent) => { EventHelper.stop(e); - const editor = this.group.getEditor(index); + const editor = this.group.getEditorByIndex(index); if (editor && this.group.isPinned(editor)) { this.accessor.arrangeGroups(GroupsArrangement.TOGGLE, this.group); } else { @@ -615,7 +615,7 @@ export class TabsTitleControl extends TitleControl { disposables.add(addDisposableListener(tab, EventType.CONTEXT_MENU, (e: Event) => { EventHelper.stop(e, true); - const input = this.group.getEditor(index); + const input = this.group.getEditorByIndex(index); if (input) { this.onContextMenu(input, e, tab); } @@ -623,7 +623,7 @@ export class TabsTitleControl extends TitleControl { // Drag support disposables.add(addDisposableListener(tab, EventType.DRAG_START, (e: DragEvent) => { - const editor = this.group.getEditor(index); + const editor = this.group.getEditorByIndex(index); if (!editor) { return; } @@ -669,7 +669,7 @@ export class TabsTitleControl extends TitleControl { const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype); if (Array.isArray(data)) { const localDraggedEditor = data[0].identifier; - if (localDraggedEditor.editor === this.group.getEditor(index) && localDraggedEditor.groupId === this.group.id) { + if (localDraggedEditor.editor === this.group.getEditorByIndex(index) && localDraggedEditor.groupId === this.group.id) { if (e.dataTransfer) { e.dataTransfer.dropEffect = 'none'; } @@ -739,7 +739,7 @@ export class TabsTitleControl extends TitleControl { private updateDropFeedback(element: HTMLElement, isDND: boolean, index?: number): void { const isTab = (typeof index === 'number'); - const editor = typeof index === 'number' ? this.group.getEditor(index) : undefined; + const editor = typeof index === 'number' ? this.group.getEditorByIndex(index) : undefined; const isActiveTab = isTab && !!editor && this.group.isActive(editor); // Background diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index 13df58c3ac5..c1c3c36f108 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -138,26 +138,8 @@ export class EditorGroup extends Disposable { return mru ? this.mru.slice(0) : this.editors.slice(0); } - getEditor(index: number): EditorInput | undefined; - getEditor(resource: URI): EditorInput | undefined; - getEditor(arg1: number | URI): EditorInput | undefined { - if (typeof arg1 === 'number') { - return this.editors[arg1]; - } - - const resource: URI = arg1; - if (!this.containsEditorByResource(resource, SideBySideEditor.MASTER)) { - return undefined; // fast check for resource opened or not - } - - for (const editor of this.editors) { - const editorResource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }); - if (editorResource?.toString() === resource.toString()) { - return editor; - } - } - - return undefined; + getEditorByIndex(index: number): EditorInput | undefined { + return this.editors[index]; } get activeEditor(): EditorInput | null { diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index f7708c300e8..3f814693d4a 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -427,11 +427,6 @@ export interface IEditorGroup { */ readonly editors: ReadonlyArray; - /** - * Returns the editor at a specific index of the group. - */ - getEditor(index: number): IEditorInput | undefined; - /** * Get all editors that are currently opened in the group optionally * sorted by being most recent active. Will sort by sequential appearance @@ -439,6 +434,11 @@ export interface IEditorGroup { */ getEditors(order?: EditorsOrder): ReadonlyArray; + /** + * Returns the editor at a specific index of the group. + */ + getEditorByIndex(index: number): IEditorInput | undefined; + /** * Returns the index of the editor in the group or -1 if not opened. */ diff --git a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts index ca86ecb8744..05efe8dad59 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -440,8 +440,8 @@ suite('EditorGroupsService', () => { assert.equal(editorWillOpenCounter, 2); assert.equal(editorDidOpenCounter, 2); assert.equal(activeEditorChangeCounter, 1); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); + assert.equal(group.getEditorByIndex(0), input); + assert.equal(group.getEditorByIndex(1), inputInactive); assert.equal(group.getIndexOfEditor(input), 0); assert.equal(group.getIndexOfEditor(inputInactive), 1); @@ -491,8 +491,8 @@ suite('EditorGroupsService', () => { await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); + assert.equal(group.getEditorByIndex(0), input); + assert.equal(group.getEditorByIndex(1), inputInactive); await group.closeEditors([input, inputInactive]); assert.equal(group.isEmpty, true); @@ -510,13 +510,13 @@ suite('EditorGroupsService', () => { await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); assert.equal(group.count, 3); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - assert.equal(group.getEditor(2), input3); + assert.equal(group.getEditorByIndex(0), input1); + assert.equal(group.getEditorByIndex(1), input2); + assert.equal(group.getEditorByIndex(2), input3); await group.closeEditors({ except: input2 }); assert.equal(group.count, 1); - assert.equal(group.getEditor(0), input2); + assert.equal(group.getEditorByIndex(0), input2); part.dispose(); }); @@ -531,9 +531,9 @@ suite('EditorGroupsService', () => { await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); assert.equal(group.count, 3); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - assert.equal(group.getEditor(2), input3); + assert.equal(group.getEditorByIndex(0), input1); + assert.equal(group.getEditorByIndex(1), input2); + assert.equal(group.getEditorByIndex(2), input3); await group.closeEditors({ savedOnly: true }); assert.equal(group.count, 0); @@ -551,14 +551,14 @@ suite('EditorGroupsService', () => { await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); assert.equal(group.count, 3); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - assert.equal(group.getEditor(2), input3); + assert.equal(group.getEditorByIndex(0), input1); + assert.equal(group.getEditorByIndex(1), input2); + assert.equal(group.getEditorByIndex(2), input3); await group.closeEditors({ direction: CloseDirection.RIGHT, except: input2 }); assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); + assert.equal(group.getEditorByIndex(0), input1); + assert.equal(group.getEditorByIndex(1), input2); part.dispose(); }); @@ -573,14 +573,14 @@ suite('EditorGroupsService', () => { await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); assert.equal(group.count, 3); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - assert.equal(group.getEditor(2), input3); + assert.equal(group.getEditorByIndex(0), input1); + assert.equal(group.getEditorByIndex(1), input2); + assert.equal(group.getEditorByIndex(2), input3); await group.closeEditors({ direction: CloseDirection.LEFT, except: input2 }); assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input2); - assert.equal(group.getEditor(1), input3); + assert.equal(group.getEditorByIndex(0), input2); + assert.equal(group.getEditorByIndex(1), input3); part.dispose(); }); @@ -594,8 +594,8 @@ suite('EditorGroupsService', () => { await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); + assert.equal(group.getEditorByIndex(0), input); + assert.equal(group.getEditorByIndex(1), inputInactive); await group.closeAllEditors(); assert.equal(group.isEmpty, true); @@ -620,12 +620,12 @@ suite('EditorGroupsService', () => { await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); + assert.equal(group.getEditorByIndex(0), input); + assert.equal(group.getEditorByIndex(1), inputInactive); group.moveEditor(inputInactive, group, { index: 0 }); assert.equal(editorMoveCounter, 1); - assert.equal(group.getEditor(0), inputInactive); - assert.equal(group.getEditor(1), input); + assert.equal(group.getEditorByIndex(0), inputInactive); + assert.equal(group.getEditorByIndex(1), input); editorGroupChangeListener.dispose(); part.dispose(); }); @@ -642,13 +642,13 @@ suite('EditorGroupsService', () => { await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); + assert.equal(group.getEditorByIndex(0), input); + assert.equal(group.getEditorByIndex(1), inputInactive); group.moveEditor(inputInactive, rightGroup, { index: 0 }); assert.equal(group.count, 1); - assert.equal(group.getEditor(0), input); + assert.equal(group.getEditorByIndex(0), input); assert.equal(rightGroup.count, 1); - assert.equal(rightGroup.getEditor(0), inputInactive); + assert.equal(rightGroup.getEditorByIndex(0), inputInactive); part.dispose(); }); @@ -664,14 +664,14 @@ suite('EditorGroupsService', () => { await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); + assert.equal(group.getEditorByIndex(0), input); + assert.equal(group.getEditorByIndex(1), inputInactive); group.copyEditor(inputInactive, rightGroup, { index: 0 }); assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); + assert.equal(group.getEditorByIndex(0), input); + assert.equal(group.getEditorByIndex(1), inputInactive); assert.equal(rightGroup.count, 1); - assert.equal(rightGroup.getEditor(0), inputInactive); + assert.equal(rightGroup.getEditorByIndex(0), inputInactive); part.dispose(); }); @@ -685,11 +685,11 @@ suite('EditorGroupsService', () => { await group.openEditor(input); assert.equal(group.count, 1); - assert.equal(group.getEditor(0), input); + assert.equal(group.getEditorByIndex(0), input); await group.replaceEditors([{ editor: input, replacement: inputInactive }]); assert.equal(group.count, 1); - assert.equal(group.getEditor(0), inputInactive); + assert.equal(group.getEditorByIndex(0), inputInactive); part.dispose(); }); diff --git a/src/vs/workbench/test/common/editor/editorGroups.test.ts b/src/vs/workbench/test/common/editor/editorGroups.test.ts index 9f8721b7a68..987feb85fba 100644 --- a/src/vs/workbench/test/common/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/common/editor/editorGroups.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { EditorGroup, ISerializedEditorGroup, EditorCloseEvent } from 'vs/workbench/common/editor/editorGroup'; -import { Extensions as EditorExtensions, IEditorInputFactoryRegistry, EditorInput, IFileEditorInput, IEditorInputFactory, CloseDirection, SideBySideEditor } from 'vs/workbench/common/editor'; +import { Extensions as EditorExtensions, IEditorInputFactoryRegistry, EditorInput, IFileEditorInput, IEditorInputFactory, CloseDirection, SideBySideEditor, toResource } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { TestLifecycleService, TestContextService, TestStorageService } from 'vs/workbench/test/workbenchTestServices'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -1170,25 +1170,40 @@ suite('Workbench editor groups', () => { const group1 = createGroup(); const group2 = createGroup(); + function getEditorByResource(group: EditorGroup, resource: URI): EditorInput | undefined { + if (!group.containsEditorByResource(resource)) { + return undefined; // fast check for resource opened or not + } + + for (const editor of group.getEditors()) { + const editorResource = toResource(editor); + if (editorResource?.toString() === resource.toString()) { + return editor; + } + } + + return undefined; + } + const input1Resource = URI.file('/hello/world.txt'); const input1ResourceUpper = URI.file('/hello/WORLD.txt'); const input1 = input(undefined, false, input1Resource); group1.openEditor(input1); assert.ok(group1.containsEditorByResource(input1Resource)); - assert.equal(group1.getEditor(input1Resource), input1); + assert.equal(getEditorByResource(group1, input1Resource), input1); - assert.ok(!group1.getEditor(input1ResourceUpper)); + assert.ok(!getEditorByResource(group1, input1ResourceUpper)); assert.ok(!group1.containsEditorByResource(input1ResourceUpper)); group2.openEditor(input1); group1.closeEditor(input1); assert.ok(!group1.containsEditorByResource(input1Resource)); - assert.ok(!group1.getEditor(input1Resource)); - assert.ok(!group1.getEditor(input1ResourceUpper)); + assert.ok(!getEditorByResource(group1, input1Resource)); + assert.ok(!getEditorByResource(group1, input1ResourceUpper)); assert.ok(group2.containsEditorByResource(input1Resource)); - assert.equal(group2.getEditor(input1Resource), input1); + assert.equal(getEditorByResource(group2, input1Resource), input1); const input1ResourceClone = URI.file('/hello/world.txt'); const input1Clone = input(undefined, false, input1ResourceClone); @@ -1199,7 +1214,7 @@ suite('Workbench editor groups', () => { group2.closeEditor(input1); assert.ok(group1.containsEditorByResource(input1Resource)); - assert.equal(group1.getEditor(input1Resource), input1Clone); + assert.equal(getEditorByResource(group1, input1Resource), input1Clone); assert.ok(!group2.containsEditorByResource(input1Resource)); group1.closeEditor(input1Clone); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 1e87e03eeaa..37cd450349d 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -809,7 +809,7 @@ export class TestEditorGroup implements IEditorGroupView { return []; } - getEditor(_index: number): IEditorInput { + getEditorByIndex(_index: number): IEditorInput { throw new Error('not implemented'); } From 7ab5bca205ef3c7c1c1fd3f0dd931464ae91aaff Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 15 Nov 2019 11:13:30 +0100 Subject: [PATCH 64/88] CallStack: keep showing the session if we were in a multi session view fixes #84601 --- src/vs/workbench/contrib/debug/browser/callStackView.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index df3cc534863..528bffc3b13 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -25,7 +25,6 @@ import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/m import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { TreeResourceNavigator2, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; -import { onUnexpectedError } from 'vs/base/common/errors'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { createMatches, FuzzyScore } from 'vs/base/common/filters'; import { Event } from 'vs/base/common/event'; @@ -165,7 +164,7 @@ export class CallStackView extends ViewletPanel { expandOnlyOnTwistieClick: true }); - this.tree.setInput(this.debugService.getModel()).then(undefined, onUnexpectedError); + this.tree.setInput(this.debugService.getModel()); const callstackNavigator = new TreeResourceNavigator2(this.tree); this._register(callstackNavigator); @@ -644,7 +643,7 @@ class CallStackDataSource implements IAsyncDataSource 1) { + if (sessions.length > 1 || this.debugService.getViewModel().isMultiSessionView()) { return Promise.resolve(sessions.filter(s => !s.parentSession)); } From 156b4c92dfc123e30d99b76218e1e13368b6f9a8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Nov 2019 11:34:35 +0100 Subject: [PATCH 65/88] editors - cleanup dirty handling on close --- .../browser/parts/editor/editorGroupView.ts | 56 +++++++++++++----- src/vs/workbench/common/editor/editorGroup.ts | 12 +--- .../test/common/editor/editorGroups.test.ts | 59 +++++++++++++++++-- 3 files changed, 94 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 3bcbf85e0f7..f42aac0dca0 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -1127,7 +1127,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } // Check for dirty and veto - const veto = await this.handleDirty([editor]); + const veto = await this.handleDirtyClosing([editor]); if (veto) { return; } @@ -1238,7 +1238,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this._group.closeEditor(editor); } - private async handleDirty(editors: EditorInput[]): Promise { + private async handleDirtyClosing(editors: EditorInput[]): Promise { if (!editors.length) { return false; // no veto } @@ -1247,13 +1247,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // To prevent multiple confirmation dialogs from showing up one after the other // we check if a pending confirmation is currently showing and if so, join that - let handleDirtyPromise = this.mapEditorToPendingConfirmation.get(editor); - if (!handleDirtyPromise) { - handleDirtyPromise = this.doHandleDirty(editor); - this.mapEditorToPendingConfirmation.set(editor, handleDirtyPromise); + let handleDirtyClosingPromise = this.mapEditorToPendingConfirmation.get(editor); + if (!handleDirtyClosingPromise) { + handleDirtyClosingPromise = this.doHandleDirtyClosing(editor); + this.mapEditorToPendingConfirmation.set(editor, handleDirtyClosingPromise); } - const veto = await handleDirtyPromise; + const veto = await handleDirtyClosingPromise; // Make sure to remove from our map of cached pending confirmations this.mapEditorToPendingConfirmation.delete(editor); @@ -1264,16 +1264,40 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } // Otherwise continue with the remainders - return this.handleDirty(editors); + return this.handleDirtyClosing(editors); } - private async doHandleDirty(editor: EditorInput): Promise { - if ( - !editor.isDirty() || // editor must be dirty - this.accessor.groups.some(groupView => groupView !== this && groupView.group.containsEditorByInstance(editor, SideBySideEditor.MASTER /* support side by side */)) || // editor is opened in other group - editor instanceof SideBySideEditorInput && this.isOpened(editor.master) // side by side editor master is still opened - ) { + private async doHandleDirtyClosing(editor: EditorInput): Promise { + if (!editor.isDirty()) { + return false; // editor must be dirty + } + + if (editor instanceof SideBySideEditorInput && this.isOpened(editor.master)) { + return false; // master-side of editor is still opened somewhere else + } + + // Note: we explicitly decide to ask for confirm if closing a normal editor even + // if it is opened in a side-by-side editor in the group. This decision is made + // because it may be less obvious that one side of a side by side editor is dirty + // and can still be changed. + + if (this.accessor.groups.some(groupView => { + if (groupView === this) { + return false; // skip this group to avoid false assumptions about the editor being opened still + } + + const otherGroup = groupView.group; + if (otherGroup.containsEditorByInstance(editor)) { + return true; // exact editor still opened + } + + if (editor instanceof SideBySideEditorInput && otherGroup.containsEditorByInstance(editor.master)) { + return true; // master side of side by side editor still opened + } + return false; + })) { + return false; // editor is still editable somewhere else } // Switch to editor that we want to handle and confirm to save/revert @@ -1330,7 +1354,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const editors = this.getEditorsToClose(args); // Check for dirty and veto - const veto = await this.handleDirty(editors.slice(0)); + const veto = await this.handleDirtyClosing(editors.slice(0)); if (veto) { return; } @@ -1409,7 +1433,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Check for dirty and veto const editors = this._group.getEditors(true); - const veto = await this.handleDirty(editors.slice(0)); + const veto = await this.handleDirtyClosing(editors.slice(0)); if (veto) { return; } diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index c1c3c36f108..bb36fca7e24 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -593,22 +593,12 @@ export class EditorGroup extends Disposable { return typeof counter === 'number' && counter > 0; } - containsEditorByInstance(editor: EditorInput, supportSideBySide?: SideBySideEditor): boolean { - - // Check if exact editor match is contained + containsEditorByInstance(editor: EditorInput): boolean { const index = this.indexOf(editor); if (index >= 0) { return true; } - // Optionally search by master/detail input if instructed - if (supportSideBySide && editor instanceof SideBySideEditorInput) { - const index = this.indexOf(supportSideBySide === SideBySideEditor.MASTER ? editor.master : editor.details); - if (index >= 0) { - return true; - } - } - return false; } diff --git a/src/vs/workbench/test/common/editor/editorGroups.test.ts b/src/vs/workbench/test/common/editor/editorGroups.test.ts index 987feb85fba..87564524de4 100644 --- a/src/vs/workbench/test/common/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/common/editor/editorGroups.test.ts @@ -187,7 +187,7 @@ suite('Workbench editor groups', () => { assert.equal(clone.isActive(input3), true); }); - test('contains() with diff editor support', function () { + test('containsEditorByInstance()', function () { const group = createGroup(); const input1 = input(); @@ -196,14 +196,61 @@ suite('Workbench editor groups', () => { const diffInput1 = new DiffEditorInput('name', 'description', input1, input2); const diffInput2 = new DiffEditorInput('name', 'description', input2, input1); + group.openEditor(input1, { pinned: true, active: true }); + + assert.equal(group.containsEditorByInstance(input1), true); + assert.equal(group.containsEditorByInstance(input2), false); + assert.equal(group.containsEditorByInstance(diffInput1), false); + assert.equal(group.containsEditorByInstance(diffInput2), false); + group.openEditor(input2, { pinned: true, active: true }); - assert.equal(group.containsEditorByInstance(input2, SideBySideEditor.MASTER), true); + assert.equal(group.containsEditorByInstance(input1), true); + assert.equal(group.containsEditorByInstance(input2), true); assert.equal(group.containsEditorByInstance(diffInput1), false); - assert.equal(group.containsEditorByInstance(diffInput1, SideBySideEditor.MASTER), true); - assert.equal(group.containsEditorByInstance(diffInput1, SideBySideEditor.DETAILS), false); - assert.equal(group.containsEditorByInstance(diffInput2, SideBySideEditor.MASTER), false); - assert.equal(group.containsEditorByInstance(diffInput2, SideBySideEditor.DETAILS), true); + assert.equal(group.containsEditorByInstance(diffInput2), false); + + group.openEditor(diffInput1, { pinned: true, active: true }); + + assert.equal(group.containsEditorByInstance(input1), true); + assert.equal(group.containsEditorByInstance(input2), true); + assert.equal(group.containsEditorByInstance(diffInput1), true); + assert.equal(group.containsEditorByInstance(diffInput2), false); + + group.openEditor(diffInput2, { pinned: true, active: true }); + + assert.equal(group.containsEditorByInstance(input1), true); + assert.equal(group.containsEditorByInstance(input2), true); + assert.equal(group.containsEditorByInstance(diffInput1), true); + assert.equal(group.containsEditorByInstance(diffInput2), true); + + group.closeEditor(input1); + + assert.equal(group.containsEditorByInstance(input1), false); + assert.equal(group.containsEditorByInstance(input2), true); + assert.equal(group.containsEditorByInstance(diffInput1), true); + assert.equal(group.containsEditorByInstance(diffInput2), true); + + group.closeEditor(input2); + + assert.equal(group.containsEditorByInstance(input1), false); + assert.equal(group.containsEditorByInstance(input2), false); + assert.equal(group.containsEditorByInstance(diffInput1), true); + assert.equal(group.containsEditorByInstance(diffInput2), true); + + group.closeEditor(diffInput1); + + assert.equal(group.containsEditorByInstance(input1), false); + assert.equal(group.containsEditorByInstance(input2), false); + assert.equal(group.containsEditorByInstance(diffInput1), false); + assert.equal(group.containsEditorByInstance(diffInput2), true); + + group.closeEditor(diffInput2); + + assert.equal(group.containsEditorByInstance(input1), false); + assert.equal(group.containsEditorByInstance(input2), false); + assert.equal(group.containsEditorByInstance(diffInput1), false); + assert.equal(group.containsEditorByInstance(diffInput2), false); }); test('group serialization', function () { From 0c2f93d927fde50a6e6f8a1ef3411369862be950 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 15 Nov 2019 11:37:28 +0100 Subject: [PATCH 66/88] Some strict property init #81574 --- src/vs/workbench/api/node/extHostDebugService.ts | 9 +++++---- .../debug/browser/breakpointEditorContribution.ts | 2 +- .../contrib/debug/browser/debugEditorContribution.ts | 4 ++-- .../contrib/debug/common/abstractDebugAdapter.ts | 6 ++---- src/vs/workbench/contrib/debug/common/debugModel.ts | 9 +++------ src/vs/workbench/contrib/debug/common/debugViewModel.ts | 9 +++------ 6 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index bffcbcddd2b..81e40b7f893 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -38,6 +38,7 @@ import { ISignService } from 'vs/platform/sign/common/sign'; import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService'; +import { withNullAsUndefined } from 'vs/base/common/types'; export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugServiceShape { @@ -114,7 +115,7 @@ export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugSe this._onDidStartDebugSession = new Emitter(); this._onDidTerminateDebugSession = new Emitter(); - this._onDidChangeActiveDebugSession = new Emitter(); + this._onDidChangeActiveDebugSession = new Emitter(); this._onDidReceiveDebugSessionCustomEvent = new Emitter(); this._debugServiceProxy = extHostRpcService.getProxy(MainContext.MainThreadDebugService); @@ -511,11 +512,11 @@ export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugSe } this._debugServiceProxy.$acceptDAError(debugAdapterHandle, err.name, err.message, err.stack); }); - debugAdapter.onExit((code: number) => { + debugAdapter.onExit((code: number | null) => { if (tracker && tracker.onExit) { - tracker.onExit(code, undefined); + tracker.onExit(withNullAsUndefined(code), undefined); } - this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, code, undefined); + this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, withNullAsUndefined(code), undefined); }); if (tracker && tracker.onWillStartSession) { diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index f5e1e7b7a1b..e0a2e8c8c1d 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -239,7 +239,7 @@ class BreakpointEditorContribution implements IBreakpointEditorContribution { } this.ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber); })); - this.toDispose.push(this.editor.onMouseLeave((e: IEditorMouseEvent) => { + this.toDispose.push(this.editor.onMouseLeave(() => { this.ensureBreakpointHintDecoration(-1); })); diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 28fad67b439..b68c4991ebb 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -12,7 +12,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { StandardTokenType } from 'vs/editor/common/modes'; import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper'; -import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IDecorationOptions } from 'vs/editor/common/editorCommon'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -80,7 +80,7 @@ class DebugEditorContribution implements IDebugEditorContribution { this.toDispose.push(this.editor.onMouseDown((e: IEditorMouseEvent) => this.onEditorMouseDown(e))); this.toDispose.push(this.editor.onMouseUp(() => this.mouseDown = false)); this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => this.onEditorMouseMove(e))); - this.toDispose.push(this.editor.onMouseLeave((e: IEditorMouseEvent) => { + this.toDispose.push(this.editor.onMouseLeave((e: IPartialEditorMouseEvent) => { this.provideNonDebugHoverScheduler.cancel(); const hoverDomNode = this.hoverWidget.getDomNode(); if (!hoverDomNode) { diff --git a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts index 56a9de64e26..9cbc4234d65 100644 --- a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts +++ b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts @@ -18,13 +18,11 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { private requestCallback: ((request: DebugProtocol.Request) => void) | undefined; private eventCallback: ((request: DebugProtocol.Event) => void) | undefined; private messageCallback: ((message: DebugProtocol.ProtocolMessage) => void) | undefined; - protected readonly _onError: Emitter; - protected readonly _onExit: Emitter; + protected readonly _onError = new Emitter(); + protected readonly _onExit = new Emitter(); constructor() { this.sequence = 1; - this._onError = new Emitter(); - this._onExit = new Emitter(); } abstract startSession(): Promise; diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 0e647c09b23..62586d2f4ce 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -810,9 +810,9 @@ export class DebugModel implements IDebugModel { private toDispose: lifecycle.IDisposable[]; private schedulers = new Map(); private breakpointsActivated = true; - private readonly _onDidChangeBreakpoints: Emitter; - private readonly _onDidChangeCallStack: Emitter; - private readonly _onDidChangeWatchExpressions: Emitter; + private readonly _onDidChangeBreakpoints = new Emitter(); + private readonly _onDidChangeCallStack = new Emitter(); + private readonly _onDidChangeWatchExpressions = new Emitter(); constructor( private breakpoints: Breakpoint[], @@ -824,9 +824,6 @@ export class DebugModel implements IDebugModel { ) { this.sessions = []; this.toDispose = []; - this._onDidChangeBreakpoints = new Emitter(); - this._onDidChangeCallStack = new Emitter(); - this._onDidChangeWatchExpressions = new Emitter(); } getId(): string { diff --git a/src/vs/workbench/contrib/debug/common/debugViewModel.ts b/src/vs/workbench/contrib/debug/common/debugViewModel.ts index f261025e49d..7d0889b5c26 100644 --- a/src/vs/workbench/contrib/debug/common/debugViewModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugViewModel.ts @@ -17,9 +17,9 @@ export class ViewModel implements IViewModel { private _focusedThread: IThread | undefined; private selectedExpression: IExpression | undefined; private selectedFunctionBreakpoint: IFunctionBreakpoint | undefined; - private readonly _onDidFocusSession: Emitter; - private readonly _onDidFocusStackFrame: Emitter<{ stackFrame: IStackFrame | undefined, explicit: boolean }>; - private readonly _onDidSelectExpression: Emitter; + private readonly _onDidFocusSession = new Emitter(); + private readonly _onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame | undefined, explicit: boolean }>(); + private readonly _onDidSelectExpression = new Emitter(); private multiSessionView: boolean; private expressionSelectedContextKey: IContextKey; private breakpointSelectedContextKey: IContextKey; @@ -30,9 +30,6 @@ export class ViewModel implements IViewModel { private jumpToCursorSupported: IContextKey; constructor(contextKeyService: IContextKeyService) { - this._onDidFocusSession = new Emitter(); - this._onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame, explicit: boolean }>(); - this._onDidSelectExpression = new Emitter(); this.multiSessionView = false; this.expressionSelectedContextKey = CONTEXT_EXPRESSION_SELECTED.bindTo(contextKeyService); this.breakpointSelectedContextKey = CONTEXT_BREAKPOINT_SELECTED.bindTo(contextKeyService); From d189af8dd703ae07ab5e21d353ec4470727278bc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Nov 2019 11:57:39 +0100 Subject: [PATCH 67/88] Diff editor closes when closing left hand side editor in other tab (fixes #51039) --- .../browser/parts/editor/editorGroupView.ts | 19 +-- src/vs/workbench/common/editor/editorGroup.ts | 92 ++--------- .../test/common/editor/editorGroups.test.ts | 150 +++++------------- 3 files changed, 60 insertions(+), 201 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index f42aac0dca0..87fdf2a8415 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -514,20 +514,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const editorsToClose = [editor]; // Include both sides of side by side editors when being closed and not opened multiple times - if (editor instanceof SideBySideEditorInput && !this.accessor.groups.some(groupView => groupView.group.containsEditorByInstance(editor))) { + if (editor instanceof SideBySideEditorInput && !this.accessor.groups.some(groupView => groupView.group.contains(editor))) { editorsToClose.push(editor.master, editor.details); } // Dispose the editor when it is no longer open in any group including diff editors editorsToClose.forEach(editorToClose => { - const resource = editorToClose ? editorToClose.getResource() : undefined; // prefer resource to not close right-hand side editors of a diff editor - if (!this.accessor.groups.some(groupView => { - if (resource) { - return groupView.group.containsEditorByResource(resource, SideBySideEditor.MASTER); - } - - return groupView.group.containsEditorByInstance(editorToClose); - })) { + if (!this.accessor.groups.some(groupView => groupView.group.contains(editorToClose, true /* include side by side editor master & details */))) { editorToClose.dispose(); } }); @@ -584,7 +577,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { editors.forEach(editor => { if (this._group.isActive(editor)) { activeEditor = editor; - } else if (this._group.containsEditorByInstance(editor)) { + } else if (this._group.contains(editor)) { inactiveEditors.push(editor); } }); @@ -776,7 +769,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } isOpened(editor: EditorInput): boolean { - return this._group.containsEditorByInstance(editor); + return this._group.contains(editor); } focus(): void { @@ -1287,11 +1280,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } const otherGroup = groupView.group; - if (otherGroup.containsEditorByInstance(editor)) { + if (otherGroup.contains(editor)) { return true; // exact editor still opened } - if (editor instanceof SideBySideEditorInput && otherGroup.containsEditorByInstance(editor.master)) { + if (editor instanceof SideBySideEditorInput && otherGroup.contains(editor.master)) { return true; // master side of side by side editor still opened } diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index bb36fca7e24..c8de4f91317 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -4,13 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { Extensions, IEditorInputFactoryRegistry, EditorInput, toResource, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorInput, SideBySideEditor } from 'vs/workbench/common/editor'; -import { URI } from 'vs/base/common/uri'; +import { Extensions, IEditorInputFactoryRegistry, EditorInput, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, CloseDirection, IEditorInput, SideBySideEditorInput } from 'vs/workbench/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ResourceMap } from 'vs/base/common/map'; import { coalesce } from 'vs/base/common/arrays'; const EditorOpenPositioning = { @@ -94,10 +92,6 @@ export class EditorGroup extends Disposable { private editors: EditorInput[] = []; private mru: EditorInput[] = []; - private mapResourceToEditorCount = new ResourceMap(); - private mapResourceToMasterEditorCount = new ResourceMap(); - private mapResourceToDetailsEditorCount = new ResourceMap(); - private preview: EditorInput | null = null; // editor in preview state private active: EditorInput | null = null; // editor in active state @@ -494,7 +488,6 @@ export class EditorGroup extends Disposable { // Add if (!del && editor) { this.mru.push(editor); // make it LRU editor - this.updateResourceCounterMap(editor, false /* add */); // add new to resource map } // Remove / Replace @@ -504,66 +497,15 @@ export class EditorGroup extends Disposable { // Remove if (del && !editor) { this.mru.splice(indexInMRU, 1); // remove from MRU - this.updateResourceCounterMap(editorToDeleteOrReplace, true /* delete */); // remove from resource map } // Replace else if (del && editor) { this.mru.splice(indexInMRU, 1, editor); // replace MRU at location - this.updateResourceCounterMap(editor, false /* add */); // add new to resource map - this.updateResourceCounterMap(editorToDeleteOrReplace, true /* delete */); // remove replaced from resource map } } } - private updateResourceCounterMap(editor: EditorInput, remove: boolean): void { - - // Remember editor resource in map for fast lookup - const resource = toResource(editor); - if (resource) { - this.doUpdateResourceCounterMap(resource, this.mapResourceToEditorCount, remove); - } - - // Side by Side editor: store resource information - // for master and details side in separate maps - // to be able to lookup properly. - if (editor instanceof SideBySideEditorInput) { - const masterResource = toResource(editor.master); - if (masterResource) { - this.doUpdateResourceCounterMap(masterResource, this.mapResourceToMasterEditorCount, remove); - } - - const detailsResource = toResource(editor.details); - if (detailsResource) { - this.doUpdateResourceCounterMap(detailsResource, this.mapResourceToDetailsEditorCount, remove); - } - } - } - - private doUpdateResourceCounterMap(resource: URI, map: ResourceMap, remove: boolean): void { - - // It is possible to have the same resource opened twice (once as normal input and once as diff input) - // So we need to do ref counting on the resource to provide the correct picture - const counter = map.get(resource) || 0; - - // Add - let newCounter: number; - if (!remove) { - newCounter = counter + 1; - } - - // Delete - else { - newCounter = counter - 1; - } - - if (newCounter > 0) { - map.set(resource, newCounter); - } else { - map.delete(resource); - } - } - indexOf(candidate: IEditorInput | null, editors = this.editors): number { if (!candidate) { return -1; @@ -578,25 +520,17 @@ export class EditorGroup extends Disposable { return -1; } - containsEditorByResource(resource: URI, supportSideBySide?: SideBySideEditor): boolean { + contains(candidate: EditorInput, searchInSideBySideEditors?: boolean): boolean { + for (const editor of this.editors) { + if (this.matches(editor, candidate)) { + return true; + } - // Check if exact editor match is contained - let counter = this.mapResourceToEditorCount.get(resource); - - // Optionally search by master/detail resource if instructed - if (supportSideBySide === SideBySideEditor.MASTER) { - counter = counter || this.mapResourceToMasterEditorCount.get(resource); - } else if (supportSideBySide === SideBySideEditor.DETAILS) { - counter = counter || this.mapResourceToDetailsEditorCount.get(resource); - } - - return typeof counter === 'number' && counter > 0; - } - - containsEditorByInstance(editor: EditorInput): boolean { - const index = this.indexOf(editor); - if (index >= 0) { - return true; + if (searchInSideBySideEditors && editor instanceof SideBySideEditorInput) { + if (this.matches(editor.master, candidate) || this.matches(editor.details, candidate)) { + return true; + } + } } return false; @@ -625,9 +559,6 @@ export class EditorGroup extends Disposable { const group = this.instantiationService.createInstance(EditorGroup, undefined); group.editors = this.editors.slice(0); group.mru = this.mru.slice(0); - group.mapResourceToEditorCount = this.mapResourceToEditorCount.clone(); - group.mapResourceToMasterEditorCount = this.mapResourceToMasterEditorCount.clone(); - group.mapResourceToDetailsEditorCount = this.mapResourceToDetailsEditorCount.clone(); group.preview = this.preview; group.active = this.active; group.editorOpenPositioning = this.editorOpenPositioning; @@ -686,7 +617,6 @@ export class EditorGroup extends Disposable { const editor = factory.deserialize(this.instantiationService, e.value); if (editor) { this.registerEditorListeners(editor); - this.updateResourceCounterMap(editor, false /* add */); } return editor; diff --git a/src/vs/workbench/test/common/editor/editorGroups.test.ts b/src/vs/workbench/test/common/editor/editorGroups.test.ts index 87564524de4..6cacfb90746 100644 --- a/src/vs/workbench/test/common/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/common/editor/editorGroups.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { EditorGroup, ISerializedEditorGroup, EditorCloseEvent } from 'vs/workbench/common/editor/editorGroup'; -import { Extensions as EditorExtensions, IEditorInputFactoryRegistry, EditorInput, IFileEditorInput, IEditorInputFactory, CloseDirection, SideBySideEditor, toResource } from 'vs/workbench/common/editor'; +import { Extensions as EditorExtensions, IEditorInputFactoryRegistry, EditorInput, IFileEditorInput, IEditorInputFactory, CloseDirection } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { TestLifecycleService, TestContextService, TestStorageService } from 'vs/workbench/test/workbenchTestServices'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -187,7 +187,7 @@ suite('Workbench editor groups', () => { assert.equal(clone.isActive(input3), true); }); - test('containsEditorByInstance()', function () { + test('contains()', function () { const group = createGroup(); const input1 = input(); @@ -198,59 +198,68 @@ suite('Workbench editor groups', () => { group.openEditor(input1, { pinned: true, active: true }); - assert.equal(group.containsEditorByInstance(input1), true); - assert.equal(group.containsEditorByInstance(input2), false); - assert.equal(group.containsEditorByInstance(diffInput1), false); - assert.equal(group.containsEditorByInstance(diffInput2), false); + assert.equal(group.contains(input1), true); + assert.equal(group.contains(input1, true), true); + assert.equal(group.contains(input2), false); + assert.equal(group.contains(input2, true), false); + assert.equal(group.contains(diffInput1), false); + assert.equal(group.contains(diffInput2), false); group.openEditor(input2, { pinned: true, active: true }); - assert.equal(group.containsEditorByInstance(input1), true); - assert.equal(group.containsEditorByInstance(input2), true); - assert.equal(group.containsEditorByInstance(diffInput1), false); - assert.equal(group.containsEditorByInstance(diffInput2), false); + assert.equal(group.contains(input1), true); + assert.equal(group.contains(input2), true); + assert.equal(group.contains(diffInput1), false); + assert.equal(group.contains(diffInput2), false); group.openEditor(diffInput1, { pinned: true, active: true }); - assert.equal(group.containsEditorByInstance(input1), true); - assert.equal(group.containsEditorByInstance(input2), true); - assert.equal(group.containsEditorByInstance(diffInput1), true); - assert.equal(group.containsEditorByInstance(diffInput2), false); + assert.equal(group.contains(input1), true); + assert.equal(group.contains(input2), true); + assert.equal(group.contains(diffInput1), true); + assert.equal(group.contains(diffInput2), false); group.openEditor(diffInput2, { pinned: true, active: true }); - assert.equal(group.containsEditorByInstance(input1), true); - assert.equal(group.containsEditorByInstance(input2), true); - assert.equal(group.containsEditorByInstance(diffInput1), true); - assert.equal(group.containsEditorByInstance(diffInput2), true); + assert.equal(group.contains(input1), true); + assert.equal(group.contains(input2), true); + assert.equal(group.contains(diffInput1), true); + assert.equal(group.contains(diffInput2), true); group.closeEditor(input1); - assert.equal(group.containsEditorByInstance(input1), false); - assert.equal(group.containsEditorByInstance(input2), true); - assert.equal(group.containsEditorByInstance(diffInput1), true); - assert.equal(group.containsEditorByInstance(diffInput2), true); + assert.equal(group.contains(input1), false); + assert.equal(group.contains(input1, true), true); + assert.equal(group.contains(input2), true); + assert.equal(group.contains(diffInput1), true); + assert.equal(group.contains(diffInput2), true); group.closeEditor(input2); - assert.equal(group.containsEditorByInstance(input1), false); - assert.equal(group.containsEditorByInstance(input2), false); - assert.equal(group.containsEditorByInstance(diffInput1), true); - assert.equal(group.containsEditorByInstance(diffInput2), true); + assert.equal(group.contains(input1), false); + assert.equal(group.contains(input1, true), true); + assert.equal(group.contains(input2), false); + assert.equal(group.contains(input2, true), true); + assert.equal(group.contains(diffInput1), true); + assert.equal(group.contains(diffInput2), true); group.closeEditor(diffInput1); - assert.equal(group.containsEditorByInstance(input1), false); - assert.equal(group.containsEditorByInstance(input2), false); - assert.equal(group.containsEditorByInstance(diffInput1), false); - assert.equal(group.containsEditorByInstance(diffInput2), true); + assert.equal(group.contains(input1), false); + assert.equal(group.contains(input1, true), true); + assert.equal(group.contains(input2), false); + assert.equal(group.contains(input2, true), true); + assert.equal(group.contains(diffInput1), false); + assert.equal(group.contains(diffInput2), true); group.closeEditor(diffInput2); - assert.equal(group.containsEditorByInstance(input1), false); - assert.equal(group.containsEditorByInstance(input2), false); - assert.equal(group.containsEditorByInstance(diffInput1), false); - assert.equal(group.containsEditorByInstance(diffInput2), false); + assert.equal(group.contains(input1), false); + assert.equal(group.contains(input1, true), false); + assert.equal(group.contains(input2), false); + assert.equal(group.contains(input2, true), false); + assert.equal(group.contains(diffInput1), false); + assert.equal(group.contains(diffInput2), false); }); test('group serialization', function () { @@ -1213,79 +1222,6 @@ suite('Workbench editor groups', () => { assert.equal(group1.getEditors()[1].matches(serializableInput2), true); }); - test('Multiple Editors - Resources', function () { - const group1 = createGroup(); - const group2 = createGroup(); - - function getEditorByResource(group: EditorGroup, resource: URI): EditorInput | undefined { - if (!group.containsEditorByResource(resource)) { - return undefined; // fast check for resource opened or not - } - - for (const editor of group.getEditors()) { - const editorResource = toResource(editor); - if (editorResource?.toString() === resource.toString()) { - return editor; - } - } - - return undefined; - } - - const input1Resource = URI.file('/hello/world.txt'); - const input1ResourceUpper = URI.file('/hello/WORLD.txt'); - const input1 = input(undefined, false, input1Resource); - group1.openEditor(input1); - - assert.ok(group1.containsEditorByResource(input1Resource)); - assert.equal(getEditorByResource(group1, input1Resource), input1); - - assert.ok(!getEditorByResource(group1, input1ResourceUpper)); - assert.ok(!group1.containsEditorByResource(input1ResourceUpper)); - - group2.openEditor(input1); - group1.closeEditor(input1); - - assert.ok(!group1.containsEditorByResource(input1Resource)); - assert.ok(!getEditorByResource(group1, input1Resource)); - assert.ok(!getEditorByResource(group1, input1ResourceUpper)); - assert.ok(group2.containsEditorByResource(input1Resource)); - assert.equal(getEditorByResource(group2, input1Resource), input1); - - const input1ResourceClone = URI.file('/hello/world.txt'); - const input1Clone = input(undefined, false, input1ResourceClone); - group1.openEditor(input1Clone); - - assert.ok(group1.containsEditorByResource(input1Resource)); - - group2.closeEditor(input1); - - assert.ok(group1.containsEditorByResource(input1Resource)); - assert.equal(getEditorByResource(group1, input1Resource), input1Clone); - assert.ok(!group2.containsEditorByResource(input1Resource)); - - group1.closeEditor(input1Clone); - - assert.ok(!group1.containsEditorByResource(input1Resource)); - - const masterResource = URI.file('/hello/world2.txt'); - const masterInput = input(undefined, false, masterResource); - - const detailsResource = URI.file('/hello/world3.txt'); - const detailsInput = input(undefined, false, detailsResource); - - const diffEditorInput = new DiffEditorInput('name', 'description', detailsInput, masterInput); - group1.openEditor(diffEditorInput); - - assert.equal(group1.containsEditorByResource(masterResource), false); - assert.equal(group1.containsEditorByResource(masterResource, SideBySideEditor.MASTER), true); - assert.equal(group1.containsEditorByResource(masterResource, SideBySideEditor.DETAILS), false); - - assert.equal(group1.containsEditorByResource(detailsResource), false); - assert.equal(group1.containsEditorByResource(detailsResource, SideBySideEditor.MASTER), false); - assert.equal(group1.containsEditorByResource(detailsResource, SideBySideEditor.DETAILS), true); - }); - test('Multiple Editors - Editor Dispose', function () { const group1 = createGroup(); const group2 = createGroup(); From f9a52f260a11afb455d79ad4684cfcd730bbe206 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 15 Nov 2019 11:21:06 +0100 Subject: [PATCH 68/88] remove the marketplace azure search cache as it is enabled by default --- src/vs/code/electron-main/window.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index b55f4095e3b..5eb01d0c5d7 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -438,15 +438,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Inject headers when requests are incoming const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; - this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => { - this.marketplaceHeadersPromise.then(headers => { - const requestHeaders = objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined }; - if (!this.configurationService.getValue('extensions.disableExperimentalAzureSearch')) { - requestHeaders['Cookie'] = `${requestHeaders['Cookie'] ? requestHeaders['Cookie'] + ';' : ''}EnableExternalSearchForVSCode=true`; - } - cb({ cancel: false, requestHeaders }); - }); - }); + this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => + this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined } }))); } private onWindowError(error: WindowError): void { From 731ebce145cd219564c8f9a4e30d72ec264596fe Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 15 Nov 2019 12:12:58 +0100 Subject: [PATCH 69/88] Fix #83135 --- src/vs/workbench/contrib/markers/browser/media/markers.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/markers/browser/media/markers.css b/src/vs/workbench/contrib/markers/browser/media/markers.css index 963bf4a95ef..73aab9391ec 100644 --- a/src/vs/workbench/contrib/markers/browser/media/markers.css +++ b/src/vs/workbench/contrib/markers/browser/media/markers.css @@ -154,10 +154,6 @@ justify-content: center; } -.markers-panel .monaco-tl-contents .actions .action-item { - margin-right: 2px; -} - .markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-source, .markers-panel .markers-panel-container .tree-container .monaco-tl-contents .related-info-resource, .markers-panel .markers-panel-container .tree-container .monaco-tl-contents .related-info-resource-separator, From c96c45dffeeb8355defd9a2fbb79722e31d3b113 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 15 Nov 2019 12:37:59 +0100 Subject: [PATCH 70/88] :lipstick --- .../contrib/markers/browser/markersPanel.ts | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/contrib/markers/browser/markersPanel.ts b/src/vs/workbench/contrib/markers/browser/markersPanel.ts index 5b650202a90..239f59aa6d8 100644 --- a/src/vs/workbench/contrib/markers/browser/markersPanel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersPanel.ts @@ -482,9 +482,8 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.setCurrentActiveEditor(); if (this.filterAction.activeFile) { this.refreshPanel(); - } else { - this.autoReveal(); } + this.autoReveal(); } private setCurrentActiveEditor(): void { @@ -547,32 +546,32 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { } private autoReveal(focus: boolean = false): void { + // No need to auto reveal if active file filter is on + if (this.filterAction.activeFile) { + return; + } let autoReveal = this.configurationService.getValue('problems.autoReveal'); if (typeof autoReveal === 'boolean' && autoReveal) { - this.revealMarkersForCurrentActiveEditor(focus); - } - } + let currentActiveResource = this.getResourceForCurrentActiveResource(); + if (currentActiveResource) { + if (!this.tree.isCollapsed(currentActiveResource) && this.hasSelectedMarkerFor(currentActiveResource)) { + this.tree.reveal(this.tree.getSelection()[0], this.lastSelectedRelativeTop); + if (focus) { + this.tree.setFocus(this.tree.getSelection()); + } + } else { + this.tree.expand(currentActiveResource); + this.tree.reveal(currentActiveResource, 0); - private revealMarkersForCurrentActiveEditor(focus: boolean = false): void { - let currentActiveResource = this.getResourceForCurrentActiveResource(); - if (currentActiveResource) { - if (!this.tree.isCollapsed(currentActiveResource) && this.hasSelectedMarkerFor(currentActiveResource)) { - this.tree.reveal(this.tree.getSelection()[0], this.lastSelectedRelativeTop); - if (focus) { - this.tree.setFocus(this.tree.getSelection()); - } - } else { - this.tree.expand(currentActiveResource); - this.tree.reveal(currentActiveResource, 0); - - if (focus) { - this.tree.setFocus([currentActiveResource]); - this.tree.setSelection([currentActiveResource]); + if (focus) { + this.tree.setFocus([currentActiveResource]); + this.tree.setSelection([currentActiveResource]); + } } + } else if (focus) { + this.tree.setSelection([]); + this.tree.focusFirst(); } - } else if (focus) { - this.tree.setSelection([]); - this.tree.focusFirst(); } } From 3be1521df277ba99e8ce40e964c33a8fd83e7e7a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 15 Nov 2019 15:19:04 +0100 Subject: [PATCH 71/88] Fix #82677 --- .../contrib/extensions/browser/extensionsActions.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 2b847bf7ab5..d3e7af804db 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -59,7 +59,7 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IProductService } from 'vs/platform/product/common/productService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; export function toExtensionDescription(local: ILocalExtension): IExtensionDescription { @@ -1051,6 +1051,7 @@ export class CheckForUpdatesAction extends Action { @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService, @IViewletService private readonly viewletService: IViewletService, + @IDialogService private readonly dialogService: IDialogService, @INotificationService private readonly notificationService: INotificationService ) { super(id, label, '', true); @@ -1059,7 +1060,7 @@ export class CheckForUpdatesAction extends Action { private checkUpdatesAndNotify(): void { const outdated = this.extensionsWorkbenchService.outdated; if (!outdated.length) { - this.notificationService.info(localize('noUpdatesAvailable', "All extensions are up to date.")); + this.dialogService.show(Severity.Info, localize('noUpdatesAvailable', "All extensions are up to date."), [localize('ok', "OK")]); return; } From 72c2f506de1220bf5cdd1a1a8b2868335728c090 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Nov 2019 15:36:49 +0100 Subject: [PATCH 72/88] working copy - introduce first save/revert semantics (#84672) --- .../api/browser/mainThreadSaveParticipant.ts | 3 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../common/extHostDocumentSaveParticipant.ts | 2 +- .../api/common/extHostTypeConverters.ts | 3 +- .../browser/parts/editor/textEditor.ts | 3 +- src/vs/workbench/common/editor.ts | 31 ++++----- .../common/editor/untitledTextEditorInput.ts | 7 +- .../common/editor/untitledTextEditorModel.ts | 14 +++- .../customEditor/browser/customEditorModel.ts | 14 +++- .../contrib/files/browser/fileCommands.ts | 3 +- .../files/common/editors/fileEditorInput.ts | 9 +-- .../browser/testCustomEditors.ts | 12 ++-- .../dialogs/browser/simpleFileDialog.ts | 4 +- .../textfile/browser/textFileService.ts | 24 +++---- .../textfile/common/textFileEditorModel.ts | 23 ++++--- .../services/textfile/common/textfiles.ts | 20 ++---- .../textfile/test/textFileEditorModel.test.ts | 4 +- .../test/textFileEditorModelManager.test.ts | 2 +- .../workingCopy/common/workingCopyService.ts | 67 +++++++++++++++++++ .../test/common/workingCopyService.test.ts | 5 +- .../extHostDocumentSaveParticipant.test.ts | 2 +- .../api/mainThreadSaveParticipant.test.ts | 3 +- 22 files changed, 169 insertions(+), 88 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts index 3e79332f086..9d0778cfd0d 100644 --- a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts @@ -30,7 +30,8 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { ISaveParticipant, SaveReason, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { ISaveParticipant, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { SaveReason } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../common/extHost.protocol'; export interface ICodeActionsOnSaveOptions { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 5df456ab6c7..60701e91b6f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -45,7 +45,7 @@ import { ITerminalDimensions, IShellLaunchConfig } from 'vs/workbench/contrib/te import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions'; import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import * as search from 'vs/workbench/services/search/common/search'; -import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { SaveReason } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; export interface IEnvironment { diff --git a/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts index f55a89b3f30..3b1b80c1de4 100644 --- a/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/common/extHostDocumentSaveParticipant.ts @@ -11,7 +11,7 @@ import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, IResou import { TextEdit } from 'vs/workbench/api/common/extHostTypes'; import { Range, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/common/extHostTypeConverters'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; -import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { SaveReason } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import * as vscode from 'vscode'; import { LinkedList } from 'vs/base/common/linkedList'; import { ILogService } from 'vs/platform/log/common/log'; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index a0671a6e51e..3361a6e5d66 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -13,7 +13,7 @@ import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/mode import * as vscode from 'vscode'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress'; -import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { SaveReason } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IPosition } from 'vs/editor/common/core/position'; import * as editorRange from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; @@ -31,7 +31,6 @@ import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log'; import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays'; import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; - export interface PositionLike { line: number; character: number; diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index cb3930fa43a..b9e4dcc4696 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -16,7 +16,8 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ITextFileService, SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { SaveReason } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { isDiffEditor, isCodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index e6a70ab4702..aff98eee1b0 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -20,6 +20,7 @@ import { ActionRunner, IAction } from 'vs/base/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; import { IPathData } from 'vs/platform/windows/common/windows'; import { coalesce, firstOrDefault } from 'vs/base/common/arrays'; +import { ISaveOptions, IRevertOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService'; export const ActiveEditorContext = new RawContextKey('activeEditor', null); export const ActiveEditorIsSaveableContext = new RawContextKey('activeEditorIsSaveable', false); @@ -261,25 +262,12 @@ export const enum Verbosity { LONG } -export interface IRevertOptions { - - /** - * Forces to load the contents of the editor again even if the editor is not dirty. - */ - force?: boolean; - - /** - * A soft revert will clear dirty state of an editor but will not attempt to load it. - */ - soft?: boolean; -} - export interface IEditorInput extends IDisposable { /** * Triggered when this input is disposed. */ - onDispose: Event; + readonly onDispose: Event; /** * Returns the associated resource of this input. @@ -316,6 +304,11 @@ export interface IEditorInput extends IDisposable { */ isDirty(): boolean; + /** + * Saves the editor if it is dirty. + */ + save(options?: ISaveOptions): Promise; + /** * Reverts this input. */ @@ -418,7 +411,7 @@ export abstract class EditorInput extends Disposable implements IEditorInput { /** * Saves the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation. */ - save(): Promise { + save(options?: ISaveOptions): Promise { return Promise.resolve(true); } @@ -553,12 +546,12 @@ export class SideBySideEditorInput extends EditorInput { return this.master.isDirty(); } - save(): Promise { - return this.master.save(); + save(options?: ISaveOptions): Promise { + return this.master.save(options); } - revert(): Promise { - return this.master.revert(); + revert(options?: IRevertOptions): Promise { + return this.master.revert(options); } getTelemetryDescriptor(): { [key: string]: unknown } { diff --git a/src/vs/workbench/common/editor/untitledTextEditorInput.ts b/src/vs/workbench/common/editor/untitledTextEditorInput.ts index 3e2998c3e29..14c409a73eb 100644 --- a/src/vs/workbench/common/editor/untitledTextEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledTextEditorInput.ts @@ -15,6 +15,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILabelService } from 'vs/platform/label/common/label'; import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; +import { ISaveOptions, IRevertOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService'; /** * An editor input to be used for untitled text buffers. @@ -146,11 +147,11 @@ export class UntitledTextEditorInput extends EditorInput implements IEncodingSup return false; } - save(): Promise { - return this.textFileService.save(this.resource); + save(options?: ISaveOptions): Promise { + return this.textFileService.save(this.resource, options); } - revert(): Promise { + revert(options?: IRevertOptions): Promise { if (this.cachedModel) { this.cachedModel.revert(); } diff --git a/src/vs/workbench/common/editor/untitledTextEditorModel.ts b/src/vs/workbench/common/editor/untitledTextEditorModel.ts index b0d22deb9b3..280ad2c1068 100644 --- a/src/vs/workbench/common/editor/untitledTextEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledTextEditorModel.ts @@ -16,7 +16,8 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res import { ITextBufferFactory } from 'vs/editor/common/model'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; -import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopyService, IWorkingCopy, ISaveOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; export class UntitledTextEditorModel extends BaseTextEditorModel implements IEncodingSupport, IWorkingCopy { @@ -48,7 +49,8 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IEnc @IModelService modelService: IModelService, @IBackupFileService private readonly backupFileService: IBackupFileService, @ITextResourceConfigurationService private readonly configurationService: ITextResourceConfigurationService, - @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService + @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, + @ITextFileService private readonly textFileService: ITextFileService ) { super(modelService, modeService); @@ -115,11 +117,17 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IEnc this._onDidChangeDirty.fire(); } - revert(): void { + save(options?: ISaveOptions): Promise { + return this.textFileService.save(this.resource, options); + } + + async revert(): Promise { this.setDirty(false); // Handle content change event buffered this.contentChangeEventScheduler.schedule(); + + return true; } backup(): Promise { diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorModel.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorModel.ts index 4afe96cdc35..255c8f09f16 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorModel.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorModel.ts @@ -6,7 +6,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { IWorkingCopy, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopy, IWorkingCopyService, WorkingCopyCapabilities, ISaveOptions, IRevertOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService'; type Edit = string; @@ -56,9 +56,19 @@ export class CustomEditorModel extends Disposable implements IWorkingCopy { this._onDidChangeDirty.fire(); } - public save() { + public async save(options?: ISaveOptions) { this._savePoint = this._edits.length; this.updateDirty(); + + return true; + } + + public async revert(options?: IRevertOptions) { + while (this._currentEditIndex > 0) { + this.undo(); + } + + return true; } public undo() { diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index e6192c5c094..7fb4fcbab7c 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -14,7 +14,8 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { ExplorerFocusCondition, TextFileContentProvider, VIEWLET_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { ExplorerViewlet } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { ITextFileService, ISaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { ISaveOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { IListService } from 'vs/platform/list/browser/listService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 104bebe8fb3..b6aa3d45ac2 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -7,11 +7,12 @@ import { localize } from 'vs/nls'; import { createMemoizer } from 'vs/base/common/decorators'; import { dirname } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { EncodingMode, EditorInput, IFileEditorInput, ITextEditorModel, Verbosity, IRevertOptions } from 'vs/workbench/common/editor'; +import { EncodingMode, EditorInput, IFileEditorInput, ITextEditorModel, Verbosity } from 'vs/workbench/common/editor'; +import { IRevertOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; -import { ITextFileService, ModelState, TextFileModelChangeEvent, LoadReason, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ModelState, TextFileModelChangeEvent, LoadReason, TextFileOperationError, TextFileOperationResult, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IReference } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -243,8 +244,8 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { return model.isDirty(); } - save(): Promise { - return this.textFileService.save(this.resource); + save(options?: ITextFileSaveOptions): Promise { + return this.textFileService.save(this.resource, options); } revert(options?: IRevertOptions): Promise { diff --git a/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts b/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts index 1a716bb0180..ab34523d391 100644 --- a/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts +++ b/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { IEditorInputFactory, EditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, EditorModel, IRevertOptions, EditorOptions } from 'vs/workbench/common/editor'; +import { IEditorInputFactory, EditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, EditorModel, EditorOptions } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { Dimension, addDisposableListener, EventType } from 'vs/base/browser/dom'; @@ -24,7 +24,7 @@ import { isEqual } from 'vs/base/common/resources'; import { generateUuid } from 'vs/base/common/uuid'; import { CancellationToken } from 'vs/base/common/cancellation'; import { editorBackground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; -import { IWorkingCopy, IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopy, IWorkingCopyService, IRevertOptions, ISaveOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { env } from 'vs/base/common/process'; const CUSTOM_SCHEME = 'testCustomEditor'; @@ -160,16 +160,16 @@ class TestCustomEditorInput extends EditorInput implements IWorkingCopy { return this.dirty; } - save(): Promise { + async save(options?: ISaveOptions): Promise { this.setDirty(false); - return Promise.resolve(true); + return true; } - revert(options?: IRevertOptions): Promise { + async revert(options?: IRevertOptions): Promise { this.setDirty(false); - return Promise.resolve(true); + return true; } async resolve(): Promise { diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 4d1a4cface3..3afa7519ab9 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -32,7 +32,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { createCancelablePromise, CancelablePromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ICommandHandler } from 'vs/platform/commands/common/commands'; -import { ITextFileService, ISaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { toResource } from 'vs/workbench/common/editor'; import { normalizeDriveLetter } from 'vs/base/common/labels'; @@ -56,7 +56,7 @@ export namespace SaveLocalFileCommand { const textFileService = accessor.get(ITextFileService); const editorService = accessor.get(IEditorService); let resource: URI | undefined = toResource(editorService.activeEditor); - const options: ISaveOptions = { force: true, availableFileSystems: [Schemas.file] }; + const options: ITextFileSaveOptions = { force: true, availableFileSystems: [Schemas.file] }; if (resource) { return textFileService.saveAs(resource, undefined, options); } diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index fe4ddff014d..4d75a95a8e4 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -8,8 +8,8 @@ import { URI } from 'vs/base/common/uri'; import { Emitter, AsyncEmitter } from 'vs/base/common/event'; import * as platform from 'vs/base/common/platform'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, FileOperationWillRunEvent, FileOperationDidRunEvent } from 'vs/workbench/services/textfile/common/textfiles'; -import { IRevertOptions } from 'vs/workbench/common/editor'; +import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, FileOperationWillRunEvent, FileOperationDidRunEvent, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { SaveReason, IRevertOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ILifecycleService, ShutdownReason, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileService, FileOperationError, FileOperationResult, HotExitConfiguration, IFileStatWithMetadata, ICreateFileOptions, FileOperation } from 'vs/platform/files/common/files'; @@ -477,7 +477,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex //#region save/revert - async save(resource: URI, options?: ISaveOptions): Promise { + async save(resource: URI, options?: ITextFileSaveOptions): Promise { // Run a forced save if we detect the file is not dirty so that save participants can still run if (options?.force && this.fileService.canHandleResource(resource) && !this.isDirty(resource)) { @@ -496,9 +496,9 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return result.results.length === 1 && !!result.results[0].success; } - saveAll(includeUntitled?: boolean, options?: ISaveOptions): Promise; - saveAll(resources: URI[], options?: ISaveOptions): Promise; - saveAll(arg1?: boolean | URI[], options?: ISaveOptions): Promise { + saveAll(includeUntitled?: boolean, options?: ITextFileSaveOptions): Promise; + saveAll(resources: URI[], options?: ITextFileSaveOptions): Promise; + saveAll(arg1?: boolean | URI[], options?: ITextFileSaveOptions): Promise { // get all dirty let toSave: URI[] = []; @@ -522,7 +522,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return this.doSaveAll(filesToSave, untitledToSave, options); } - private async doSaveAll(fileResources: URI[], untitledResources: URI[], options?: ISaveOptions): Promise { + private async doSaveAll(fileResources: URI[], untitledResources: URI[], options?: ITextFileSaveOptions): Promise { // Handle files first that can just be saved const result = await this.doSaveAllFiles(fileResources, options); @@ -627,7 +627,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return options; } - private async doSaveAllFiles(resources?: URI[], options: ISaveOptions = Object.create(null)): Promise { + private async doSaveAllFiles(resources?: URI[], options: ITextFileSaveOptions = Object.create(null)): Promise { const dirtyFileModels = this.getDirtyFileModels(Array.isArray(resources) ? resources : undefined /* Save All */) .filter(model => { if ((model.hasState(ModelState.CONFLICT) || model.hasState(ModelState.ERROR)) && (options.reason === SaveReason.AUTO || options.reason === SaveReason.FOCUS_CHANGE || options.reason === SaveReason.WINDOW_CHANGE)) { @@ -675,7 +675,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return this.getFileModels(resources).filter(model => model.isDirty()); } - async saveAs(resource: URI, targetResource?: URI, options?: ISaveOptions): Promise { + async saveAs(resource: URI, targetResource?: URI, options?: ITextFileSaveOptions): Promise { // Get to target resource if (!targetResource) { @@ -702,7 +702,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return this.doSaveAs(resource, targetResource, options); } - private async doSaveAs(resource: URI, target: URI, options?: ISaveOptions): Promise { + private async doSaveAs(resource: URI, target: URI, options?: ITextFileSaveOptions): Promise { // Retrieve text model from provided resource if any let model: ITextFileEditorModel | UntitledTextEditorModel | undefined; @@ -736,7 +736,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return target; } - private async doSaveTextFileAs(sourceModel: ITextFileEditorModel | UntitledTextEditorModel, resource: URI, target: URI, options?: ISaveOptions): Promise { + private async doSaveTextFileAs(sourceModel: ITextFileEditorModel | UntitledTextEditorModel, resource: URI, target: URI, options?: ITextFileSaveOptions): Promise { // Prefer an existing model if it is already loaded for the given target resource let targetExists: boolean = false; @@ -866,7 +866,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex await Promise.all(fileModels.map(async model => { try { - await model.revert(options?.soft); + await model.revert(options); if (!model.isDirty()) { const result = mapResourceToResult.get(model.resource); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 7dba20135d7..f18c70d2aee 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -11,7 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ITextFileService, ModelState, ITextFileEditorModel, ISaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, ITextFileStreamContent, ILoadOptions, LoadReason, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, ModelState, ITextFileEditorModel, ISaveErrorHandler, ISaveParticipant, StateChange, ITextFileStreamContent, ILoadOptions, LoadReason, IResolvedTextFileEditorModel, ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { EncodingMode } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -29,7 +29,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { isEqual, isEqualOrParent, extname, basename, joinPath } from 'vs/base/common/resources'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Schemas } from 'vs/base/common/network'; -import { IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopyService, WorkingCopyCapabilities, SaveReason, IRevertOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IFilesConfigurationService, IAutoSaveConfiguration } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; export interface IBackupMetaData { @@ -252,9 +252,9 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.backupFileService.hasBackupSync(this.resource, this.versionId); } - async revert(soft?: boolean): Promise { + async revert(options?: IRevertOptions): Promise { if (!this.isResolved()) { - return; + return false; } // Cancel any running auto-save @@ -265,7 +265,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil const undo = this.setDirty(false); // Force read from disk unless reverting soft - if (!soft) { + const softUndo = options?.soft; + if (!softUndo) { try { await this.load({ forceReadFromDisk: true }); } catch (error) { @@ -284,6 +285,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (wasDirty) { this._onDidChangeDirty.fire(); } + + return true; } async load(options?: ILoadOptions): Promise { @@ -612,9 +615,9 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.autoSaveDisposable.value = toDisposable(() => clearTimeout(handle)); } - async save(options: ISaveOptions = Object.create(null)): Promise { + async save(options: ITextFileSaveOptions = Object.create(null)): Promise { if (!this.isResolved()) { - return; + return false; } this.logService.trace('save() - enter', this.resource); @@ -622,10 +625,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Cancel any currently running auto saves to make this the one that succeeds this.autoSaveDisposable.clear(); - return this.doSave(this.versionId, options); + await this.doSave(this.versionId, options); + + return true; } - private doSave(versionId: number, options: ISaveOptions): Promise { + private doSave(versionId: number, options: ITextFileSaveOptions): Promise { if (isUndefinedOrNull(options.reason)) { options.reason = SaveReason.EXPLICIT; } diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index d07dc93f41e..78c9bc6dcb8 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -6,7 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { Event, IWaitUntil } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IEncodingSupport, IRevertOptions, IModeSupport } from 'vs/workbench/common/editor'; +import { IEncodingSupport, IModeSupport } from 'vs/workbench/common/editor'; import { IBaseStatWithMetadata, IFileStatWithMetadata, IReadFileOptions, IWriteFileOptions, FileOperationError, FileOperationResult, FileOperation } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; @@ -14,7 +14,7 @@ import { ITextBufferFactory, ITextModel, ITextSnapshot } from 'vs/editor/common/ import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { isNative } from 'vs/base/common/platform'; -import { IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopy, ISaveOptions, SaveReason, IRevertOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService'; export const ITextFileService = createDecorator('textFileService'); @@ -323,13 +323,6 @@ export interface IResult { success?: boolean; } -export const enum SaveReason { - EXPLICIT = 1, - AUTO = 2, - FOCUS_CHANGE = 3, - WINDOW_CHANGE = 4 -} - export const enum LoadReason { EDITOR = 1, REFERENCE = 2, @@ -421,12 +414,9 @@ export interface ITextFileEditorModelManager { disposeModel(model: ITextFileEditorModel): void; } -export interface ISaveOptions { - force?: boolean; - reason?: SaveReason; +export interface ITextFileSaveOptions extends ISaveOptions { overwriteReadonly?: boolean; overwriteEncoding?: boolean; - skipSaveParticipants?: boolean; writeElevated?: boolean; availableFileSystems?: readonly string[]; } @@ -458,11 +448,11 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport updatePreferredEncoding(encoding: string | undefined): void; - save(options?: ISaveOptions): Promise; + save(options?: ITextFileSaveOptions): Promise; load(options?: ILoadOptions): Promise; - revert(soft?: boolean): Promise; + revert(options?: IRevertOptions): Promise; backup(target?: URI): Promise; diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index 8ccf5a25c5d..3d8c90ba250 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -263,7 +263,7 @@ suite('Files - TextFileEditorModel', () => { assert.equal(accessor.workingCopyService.dirtyCount, 1); assert.equal(accessor.workingCopyService.isDirty(model.resource), true); - await model.revert(true /* soft revert */); + await model.revert({ soft: true }); assert.ok(!model.isDirty()); assert.equal(model.textEditorModel!.getValue(), 'foo'); assert.equal(eventCounter, 1); @@ -300,7 +300,7 @@ suite('Files - TextFileEditorModel', () => { model.textEditorModel!.setValue('foo'); assert.ok(model.isDirty()); - await model.revert(true /* soft revert */); + await model.revert({ soft: true }); assert.ok(!model.isDirty()); model.onDidStateChange(e => { diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts index 0494cbf139b..f4db513644f 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -286,7 +286,7 @@ suite('Files - TextFileEditorModelManager', () => { model.textEditorModel!.setValue('make dirty'); manager.disposeModel((model as TextFileEditorModel)); assert.ok(!model.isDisposed()); - model.revert(true); + model.revert({ soft: true }); manager.disposeModel((model as TextFileEditorModel)); assert.ok(model.isDisposed()); manager.dispose(); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts index fe2f33adfa3..d01206271bb 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyService.ts @@ -19,6 +19,63 @@ export const enum WorkingCopyCapabilities { AutoSave = 1 << 1 } +export const enum SaveReason { + + /** + * Explicit user gesture. + */ + EXPLICIT = 1, + + /** + * Auto save after a timeout. + */ + AUTO = 2, + + /** + * Auto save after editor focus change. + */ + FOCUS_CHANGE = 3, + + /** + * Auto save after window change. + */ + WINDOW_CHANGE = 4 +} + +export interface ISaveOptions { + + /** + * An indicator how the save operation was triggered. + */ + reason?: SaveReason; + + /** + * Forces to load the contents of the working copy + * again even if the working copy is not dirty. + */ + force?: boolean; + + /** + * Instructs the save operation to skip any save participants. + */ + skipSaveParticipants?: boolean; +} + +export interface IRevertOptions { + + /** + * Forces to load the contents of the working copy + * again even if the working copy is not dirty. + */ + force?: boolean; + + /** + * A soft revert will clear dirty state of a working copy + * but will not attempt to load it from its persisted state. + */ + soft?: boolean; +} + export interface IWorkingCopy { //#region Dirty Tracking @@ -29,6 +86,16 @@ export interface IWorkingCopy { //#endregion + + //#region Save/Revert + + save(options?: ISaveOptions): Promise; + + revert(options?: IRevertOptions): Promise; + + //#endregion + + readonly resource: URI; readonly capabilities: WorkingCopyCapabilities; diff --git a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts index 7110cd28582..f9fe1649555 100644 --- a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopy, ISaveOptions, IRevertOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { URI } from 'vs/base/common/uri'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -41,6 +41,9 @@ suite('WorkingCopyService', () => { return this.dirty; } + async save(options?: ISaveOptions): Promise { return true; } + async revert(options?: IRevertOptions): Promise { return true; } + dispose(): void { this._onDispose.fire(); diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts index d85eae55ebf..0e4d2acb7bd 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts @@ -10,7 +10,7 @@ import { TextDocumentSaveReason, TextEdit, Position, EndOfLine } from 'vs/workbe import { MainThreadTextEditorsShape, IWorkspaceEditDto } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostDocumentSaveParticipant'; import { SingleProxyRPCProtocol } from './testRPCProtocol'; -import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { SaveReason } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import * as vscode from 'vscode'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import { NullLogService } from 'vs/platform/log/common/log'; diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts index 502d3fae8e9..c6122bedf48 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts @@ -13,7 +13,8 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { ITextFileService, SaveReason, IResolvedTextFileEditorModel, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, IResolvedTextFileEditorModel, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { SaveReason } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; class ServiceAccessor { From acb8bd592df141d11ad586d34a49ab880e3287fb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Nov 2019 15:40:58 +0100 Subject: [PATCH 73/88] custom editor - wire save/revert into custom editor input --- .../contrib/customEditor/browser/customEditorInput.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index d89fcb9b26f..d100c41d38e 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -16,6 +16,7 @@ import { IEditorInput, Verbosity } from 'vs/workbench/common/editor'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; import { CustomEditorModel } from './customEditorModel'; +import { ISaveOptions, IRevertOptions } from 'vs/workbench/services/workingCopy/common/workingCopyService'; export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { @@ -119,4 +120,12 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { public isDirty(): boolean { return this._model ? this._model.isDirty() : false; } + + public save(options?: ISaveOptions): Promise { + return this._model ? this._model.save(options) : Promise.resolve(false); + } + + public revert(options?: IRevertOptions): Promise { + return this._model ? this._model.revert(options) : Promise.resolve(false); + } } From 40d5fd27f6e0fb579eef00c73ecdc8d01afa0a97 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 15 Nov 2019 15:41:03 +0100 Subject: [PATCH 74/88] fixes #84810 --- src/vs/workbench/contrib/debug/browser/debugToolBar.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 781ac16a5de..76e38f1bab2 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -111,6 +111,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { private updateScheduler: RunOnceScheduler; private debugToolBarMenu: IMenu; private disposeOnUpdate: IDisposable | undefined; + private yCoordinate = 0; private isVisible = false; private isBuilt = false; @@ -264,9 +265,10 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { } } - private setYCoordinate(y = 0): void { + private setYCoordinate(y = this.yCoordinate): void { const titlebarOffset = this.layoutService.getTitleBarOffset(); this.$el.style.top = `${titlebarOffset + y}px`; + this.yCoordinate = y; } private setCoordinates(x?: number, y?: number): void { From c96aaab661a78cecf226343511e894eae3132dba Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Nov 2019 15:54:07 +0100 Subject: [PATCH 75/88] workspace - do not allow workspace files without .code-workspace suffix (#84818) --- src/vs/workbench/browser/actions/workspaceActions.ts | 4 ++-- .../workspaces/electron-browser/workspaceEditingService.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index b129dc792f8..c8941a826a1 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -22,7 +22,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; export class OpenFileAction extends Action { @@ -213,7 +213,7 @@ export class SaveWorkspaceAsAction extends Action { async run(): Promise { const configPathUri = await this.workspaceEditingService.pickNewWorkspacePath(); - if (configPathUri) { + if (configPathUri && hasWorkspaceFileExtension(configPathUri)) { switch (this.contextService.getWorkbenchState()) { case WorkbenchState.EMPTY: case WorkbenchState.FOLDER: diff --git a/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts index 63e66f3a6b5..eae32007ddc 100644 --- a/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; -import { IWorkspacesService, isUntitledWorkspace, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesService, isUntitledWorkspace, IWorkspaceIdentifier, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -124,7 +124,7 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi // Save: save workspace, but do not veto unload if path provided case ConfirmResult.SAVE: { const newWorkspacePath = await this.pickNewWorkspacePath(); - if (!newWorkspacePath) { + if (!newWorkspacePath || !hasWorkspaceFileExtension(newWorkspacePath)) { return true; // keep veto if no target was provided } From c1b6edad134d1d5cc794f3905566413191d7c2cb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 15 Nov 2019 15:56:13 +0100 Subject: [PATCH 76/88] experiment with showing a highlight when going to a definition, #84553 --- .../editor/contrib/gotoSymbol/goToCommands.ts | 25 ++++++++++++++++--- .../outline/browser/outlineNavigation.ts | 2 +- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index 5ec0555a5ef..b9fbc7182ce 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -119,8 +119,9 @@ abstract class SymbolNavigationAction extends EditorAction { } else { const next = model.firstReference()!; - const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide); - if (targetEditor && model.references.length > 1 && gotoLocation === 'gotoAndPeek') { + const peek = model.references.length > 1 && gotoLocation === 'gotoAndPeek'; + const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide, !peek); + if (peek && targetEditor) { this._openInPeek(targetEditor, model); } else { model.dispose(); @@ -134,7 +135,7 @@ abstract class SymbolNavigationAction extends EditorAction { } } - private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean): Promise { + private async _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean, highlight: boolean): Promise { // range is the target-selection-range when we have one // and the the fallback is the 'full' range let range: IRange | undefined = undefined; @@ -145,13 +146,29 @@ abstract class SymbolNavigationAction extends EditorAction { range = reference.range; } - return editorService.openCodeEditor({ + const targetEditor = await editorService.openCodeEditor({ resource: reference.uri, options: { selection: Range.collapseToStart(range), revealInCenterIfOutsideViewport: true } }, editor, sideBySide); + + if (!targetEditor) { + return undefined; + } + + if (highlight) { + const modelNow = targetEditor.getModel(); + const ids = targetEditor.deltaDecorations([], [{ range, options: { className: 'rangeHighlight' } }]); + setTimeout(() => { + if (targetEditor.getModel() === modelNow) { + targetEditor.deltaDecorations(ids, []); + } + }, 350); + } + + return targetEditor; } private _openInPeek(target: ICodeEditor, model: ReferencesModel) { diff --git a/src/vs/workbench/contrib/outline/browser/outlineNavigation.ts b/src/vs/workbench/contrib/outline/browser/outlineNavigation.ts index 7d22b68aa69..d7023790f93 100644 --- a/src/vs/workbench/contrib/outline/browser/outlineNavigation.ts +++ b/src/vs/workbench/contrib/outline/browser/outlineNavigation.ts @@ -147,7 +147,7 @@ export class OutlineNavigation implements IEditorContribution { if (modelNow === this._editor.getModel()) { this._editor.deltaDecorations(ids, []); } - }, 250); + }, 350); } } From 1624087eea278a8daa8d98e13d58bc690ae70a9e Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 15 Nov 2019 16:05:00 +0100 Subject: [PATCH 77/88] fixes #83387 --- src/vs/workbench/contrib/debug/browser/repl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 37ed56928be..88b1a920272 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -344,7 +344,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati } focus(): void { - this.replInput.focus(); + setTimeout(() => this.replInput.focus(), 0); } getActionViewItem(action: IAction): IActionViewItem | undefined { From b066bb2c50c17e7845bebf34ad4d39c73f5b4fd0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Nov 2019 16:44:27 +0100 Subject: [PATCH 78/88] custom editors - wire in "Revert" --- .../contrib/files/browser/fileCommands.ts | 68 ++++++++----------- .../workbench/contrib/files/browser/files.ts | 65 ++++++++++++++---- .../browser/testCustomEditors.ts | 4 ++ 3 files changed, 83 insertions(+), 54 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 7fb4fcbab7c..1cc2e85ae1d 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -29,7 +29,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { isWindows } from 'vs/base/common/platform'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { getResourceForCommand, getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; +import { getResourceForCommand, getMultiSelectedResources, getMultiSelectedEditors } from 'vs/workbench/contrib/files/browser/files'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { getMultiSelectedEditorContexts } from 'vs/workbench/browser/parts/editor/editorCommands'; import { Schemas } from 'vs/base/common/network'; @@ -44,6 +44,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { UNTITLED_WORKSPACE_NAME } from 'vs/platform/workspaces/common/workspaces'; import { withUndefinedAsNull, withNullAsUndefined } from 'vs/base/common/types'; +import { assign } from 'vs/base/common/objects'; // Commands @@ -124,8 +125,17 @@ async function save( return doSaveAs(resource, isSaveAs, options, editorService, fileService, untitledTextEditorService, textFileService, editorGroupService, environmentService); } - // Save - return doSave(resource, options, editorService, textFileService); + // Pin the active editor if we are saving it + const activeControl = editorService.activeControl; + const activeEditorResource = activeControl?.input?.getResource(); + if (activeControl && activeEditorResource && isEqual(activeEditorResource, resource)) { + activeControl.group.pinEditor(activeControl.input); + } + + // Just save (force a change to the file to trigger external watchers if any) + options = assign({ force: true }, options || Object.create(null)); + + return textFileService.save(resource, options); } async function doSaveAs( @@ -162,7 +172,7 @@ async function doSaveAs( // Force a change to the file to trigger external watchers if any // fixes https://github.com/Microsoft/vscode/issues/59655 - options = ensureForcedSave(options); + options = assign({ force: true }, options || Object.create(null)); target = await textFileService.saveAs(resource, undefined, options); } @@ -188,36 +198,6 @@ async function doSaveAs( return true; } -async function doSave( - resource: URI, - options: ISaveOptions | undefined, - editorService: IEditorService, - textFileService: ITextFileService -): Promise { - - // Pin the active editor if we are saving it - const activeControl = editorService.activeControl; - const activeEditorResource = activeControl?.input?.getResource(); - if (activeControl && activeEditorResource && isEqual(activeEditorResource, resource)) { - activeControl.group.pinEditor(activeControl.input); - } - - // Just save (force a change to the file to trigger external watchers if any) - options = ensureForcedSave(options); - - return textFileService.save(resource, options); -} - -function ensureForcedSave(options?: ISaveOptions): ISaveOptions { - if (!options) { - options = { force: true }; - } else { - options.force = true; - } - - return options; -} - async function saveAll(saveAllArguments: any, editorService: IEditorService, untitledTextEditorService: IUntitledTextEditorService, textFileService: ITextFileService, editorGroupService: IEditorGroupsService): Promise { @@ -268,17 +248,23 @@ async function saveAll(saveAllArguments: any, editorService: IEditorService, unt CommandsRegistry.registerCommand({ id: REVERT_FILE_COMMAND_ID, handler: async (accessor, resource: URI | object) => { - const editorService = accessor.get(IEditorService); - const textFileService = accessor.get(ITextFileService); const notificationService = accessor.get(INotificationService); - const resources = getMultiSelectedResources(resource, accessor.get(IListService), editorService) - .filter(resource => resource.scheme !== Schemas.untitled); + const listService = accessor.get(IListService); + const editorService = accessor.get(IEditorService); - if (resources.length) { + const editors = getMultiSelectedEditors(listService, editorService); + if (editors.length) { try { - await textFileService.revertAll(resources, { force: true }); + await Promise.all(editors.map(async editor => { + const resource = editor.getResource(); + if (resource && resource.scheme === Schemas.untitled) { + return; // we do not allow to revert untitled files + } + + return editor.revert({ force: true }); + })); } catch (error) { - notificationService.error(nls.localize('genericRevertError', "Failed to revert '{0}': {1}", resources.map(r => basename(r)).join(', '), toErrorMessage(error, false))); + notificationService.error(nls.localize('genericRevertResourcesError', "Failed to revert '{0}': {1}", editors.map(e => e.getName()).join(', '), toErrorMessage(error, false))); } } } diff --git a/src/vs/workbench/contrib/files/browser/files.ts b/src/vs/workbench/contrib/files/browser/files.ts index d492c77b3b0..534b0283a6c 100644 --- a/src/vs/workbench/contrib/files/browser/files.ts +++ b/src/vs/workbench/contrib/files/browser/files.ts @@ -6,20 +6,14 @@ import { URI } from 'vs/base/common/uri'; import { IListService } from 'vs/platform/list/browser/listService'; import { OpenEditor } from 'vs/workbench/contrib/files/common/files'; -import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; +import { toResource, SideBySideEditor, IEditorInput } from 'vs/workbench/common/editor'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { coalesce } from 'vs/base/common/arrays'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; -// Commands can get exeucted from a command pallete, from a context menu or from some list using a keybinding -// To cover all these cases we need to properly compute the resource on which the command is being executed -export function getResourceForCommand(resource: URI | object | undefined, listService: IListService, editorService: IEditorService): URI | undefined { - if (URI.isUri(resource)) { - return resource; - } - +function getFocus(listService: IListService): unknown | undefined { let list = listService.lastFocusedList; if (list?.getHTMLElement() === document.activeElement) { let focus: unknown; @@ -35,16 +29,38 @@ export function getResourceForCommand(resource: URI | object | undefined, listSe } } - if (focus instanceof ExplorerItem) { - return focus.resource; - } else if (focus instanceof OpenEditor) { - return focus.getResource(); - } + return focus; + } + + return undefined; +} + +// Commands can get exeucted from a command pallete, from a context menu or from some list using a keybinding +// To cover all these cases we need to properly compute the resource on which the command is being executed +export function getResourceForCommand(resource: URI | object | undefined, listService: IListService, editorService: IEditorService): URI | undefined { + if (URI.isUri(resource)) { + return resource; + } + + const focus = getFocus(listService); + if (focus instanceof ExplorerItem) { + return focus.resource; + } else if (focus instanceof OpenEditor) { + return focus.getResource(); } return editorService.activeEditor ? toResource(editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : undefined; } +export function getEditorForCommand(listService: IListService, editorService: IEditorService): IEditorInput | undefined { + const focus = getFocus(listService); + if (focus instanceof OpenEditor) { + return focus.editor; + } + + return editorService.activeEditor ? editorService.activeEditor : undefined; +} + export function getMultiSelectedResources(resource: URI | object | undefined, listService: IListService, editorService: IEditorService): Array { const list = listService.lastFocusedList; if (list?.getHTMLElement() === document.activeElement) { @@ -83,3 +99,26 @@ export function getMultiSelectedResources(resource: URI | object | undefined, li const result = getResourceForCommand(resource, listService, editorService); return !!result ? [result] : []; } + +export function getMultiSelectedEditors(listService: IListService, editorService: IEditorService): Array { + const list = listService.lastFocusedList; + if (list?.getHTMLElement() === document.activeElement) { + // Open editors view + if (list instanceof List) { + const selection = coalesce(list.getSelectedElements().filter(s => s instanceof OpenEditor).map((oe: OpenEditor) => oe.editor)); + const focusedElements = list.getFocusedElements(); + const focus = focusedElements.length ? focusedElements[0] : undefined; + let mainEditor: IEditorInput | undefined = undefined; + if (focus instanceof OpenEditor) { + mainEditor = focus.editor; + } + // We only respect the selection if it contains the main element. + if (selection.some(s => s === mainEditor)) { + return selection; + } + } + } + + const result = getEditorForCommand(listService, editorService); + return !!result ? [result] : []; +} diff --git a/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts b/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts index ab34523d391..e2f0bfcc1f7 100644 --- a/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts +++ b/src/vs/workbench/contrib/testCustomEditors/browser/testCustomEditors.ts @@ -143,6 +143,10 @@ class TestCustomEditorInput extends EditorInput implements IWorkingCopy { setValue(value: string) { if (this.model) { + if (this.model.value === value) { + return; + } + this.model.value = value; } From 71c0930d3488f6d0ba78c685a483861fbdf86497 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Nov 2019 17:06:15 +0100 Subject: [PATCH 79/88] revert - operate on groups and pin editor on revert --- .../contrib/files/browser/fileCommands.ts | 13 +++++++----- .../workbench/contrib/files/browser/files.ts | 21 +++++++++++-------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 1cc2e85ae1d..63059309699 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -247,24 +247,27 @@ async function saveAll(saveAllArguments: any, editorService: IEditorService, unt CommandsRegistry.registerCommand({ id: REVERT_FILE_COMMAND_ID, - handler: async (accessor, resource: URI | object) => { + handler: async accessor => { const notificationService = accessor.get(INotificationService); const listService = accessor.get(IListService); - const editorService = accessor.get(IEditorService); + const editorGroupsService = accessor.get(IEditorGroupsService); - const editors = getMultiSelectedEditors(listService, editorService); + const editors = getMultiSelectedEditors(listService, editorGroupsService); if (editors.length) { try { - await Promise.all(editors.map(async editor => { + await Promise.all(editors.map(async ({ groupId, editor }) => { const resource = editor.getResource(); if (resource && resource.scheme === Schemas.untitled) { return; // we do not allow to revert untitled files } + // Use revert as a hint to pin the editor + editorGroupsService.getGroup(groupId)?.pinEditor(editor); + return editor.revert({ force: true }); })); } catch (error) { - notificationService.error(nls.localize('genericRevertResourcesError', "Failed to revert '{0}': {1}", editors.map(e => e.getName()).join(', '), toErrorMessage(error, false))); + notificationService.error(nls.localize('genericRevertResourcesError', "Failed to revert '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); } } } diff --git a/src/vs/workbench/contrib/files/browser/files.ts b/src/vs/workbench/contrib/files/browser/files.ts index 534b0283a6c..6b99f39e922 100644 --- a/src/vs/workbench/contrib/files/browser/files.ts +++ b/src/vs/workbench/contrib/files/browser/files.ts @@ -6,12 +6,13 @@ import { URI } from 'vs/base/common/uri'; import { IListService } from 'vs/platform/list/browser/listService'; import { OpenEditor } from 'vs/workbench/contrib/files/common/files'; -import { toResource, SideBySideEditor, IEditorInput } from 'vs/workbench/common/editor'; +import { toResource, SideBySideEditor, IEditorIdentifier } from 'vs/workbench/common/editor'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { coalesce } from 'vs/base/common/arrays'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; function getFocus(listService: IListService): unknown | undefined { let list = listService.lastFocusedList; @@ -52,13 +53,15 @@ export function getResourceForCommand(resource: URI | object | undefined, listSe return editorService.activeEditor ? toResource(editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : undefined; } -export function getEditorForCommand(listService: IListService, editorService: IEditorService): IEditorInput | undefined { +export function getEditorForCommand(listService: IListService, editorGroupService: IEditorGroupsService): IEditorIdentifier | undefined { const focus = getFocus(listService); if (focus instanceof OpenEditor) { - return focus.editor; + return focus; } - return editorService.activeEditor ? editorService.activeEditor : undefined; + const activeGroup = editorGroupService.activeGroup; + + return activeGroup.activeEditor ? { groupId: activeGroup.id, editor: activeGroup.activeEditor } : undefined; } export function getMultiSelectedResources(resource: URI | object | undefined, listService: IListService, editorService: IEditorService): Array { @@ -100,17 +103,17 @@ export function getMultiSelectedResources(resource: URI | object | undefined, li return !!result ? [result] : []; } -export function getMultiSelectedEditors(listService: IListService, editorService: IEditorService): Array { +export function getMultiSelectedEditors(listService: IListService, editorGroupsService: IEditorGroupsService): Array { const list = listService.lastFocusedList; if (list?.getHTMLElement() === document.activeElement) { // Open editors view if (list instanceof List) { - const selection = coalesce(list.getSelectedElements().filter(s => s instanceof OpenEditor).map((oe: OpenEditor) => oe.editor)); + const selection = coalesce(list.getSelectedElements().filter(s => s instanceof OpenEditor)); const focusedElements = list.getFocusedElements(); const focus = focusedElements.length ? focusedElements[0] : undefined; - let mainEditor: IEditorInput | undefined = undefined; + let mainEditor: IEditorIdentifier | undefined = undefined; if (focus instanceof OpenEditor) { - mainEditor = focus.editor; + mainEditor = focus; } // We only respect the selection if it contains the main element. if (selection.some(s => s === mainEditor)) { @@ -119,6 +122,6 @@ export function getMultiSelectedEditors(listService: IListService, editorService } } - const result = getEditorForCommand(listService, editorService); + const result = getEditorForCommand(listService, editorGroupsService); return !!result ? [result] : []; } From 3e092938e8ba3d4331ab23c67eb0e62ee9bf3bd5 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 15 Nov 2019 11:25:15 +0100 Subject: [PATCH 80/88] Move the change accessor down to LinesLayout --- .../browser/viewParts/viewZones/viewZones.ts | 87 ++++++++-------- .../editor/common/viewLayout/linesLayout.ts | 33 ++++++- src/vs/editor/common/viewLayout/viewLayout.ts | 13 +-- src/vs/editor/common/viewModel/viewModel.ts | 16 +-- .../common/viewLayout/linesLayout.test.ts | 98 +++++++++++-------- 5 files changed, 138 insertions(+), 109 deletions(-) diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index c82db418cee..aca0e779c2f 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -13,6 +13,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; export interface IMyViewZone { whitespaceId: string; @@ -73,20 +74,22 @@ export class ViewZones extends ViewPart { // ---- begin view event handlers private _recomputeWhitespacesProps(): boolean { - let hadAChange = false; + return this._context.viewLayout.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => { + let hadAChange = false; - const keys = Object.keys(this._zones); - for (let i = 0, len = keys.length; i < len; i++) { - const id = keys[i]; - const zone = this._zones[id]; - const props = this._computeWhitespaceProps(zone.delegate); - if (this._context.viewLayout.changeWhitespace(id, props.afterViewLineNumber, props.heightInPx)) { - this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); - hadAChange = true; + const keys = Object.keys(this._zones); + for (let i = 0, len = keys.length; i < len; i++) { + const id = keys[i]; + const zone = this._zones[id]; + const props = this._computeWhitespaceProps(zone.delegate); + if (whitespaceAccessor.changeOneWhitespace(id, props.afterViewLineNumber, props.heightInPx)) { + this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); + hadAChange = true; + } } - } - return hadAChange; + return hadAChange; + }); } public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { @@ -190,40 +193,42 @@ export class ViewZones extends ViewPart { public changeViewZones(callback: (changeAccessor: IViewZoneChangeAccessor) => any): boolean { - let zonesHaveChanged = false; + return this._context.viewLayout.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => { + let zonesHaveChanged = false; - const changeAccessor: IViewZoneChangeAccessor = { - addZone: (zone: IViewZone): string => { - zonesHaveChanged = true; - return this.addZone(zone); - }, - removeZone: (id: string): void => { - if (!id) { - return; + const changeAccessor: IViewZoneChangeAccessor = { + addZone: (zone: IViewZone): string => { + zonesHaveChanged = true; + return this._addZone(whitespaceAccessor, zone); + }, + removeZone: (id: string): void => { + if (!id) { + return; + } + zonesHaveChanged = this._removeZone(whitespaceAccessor, id) || zonesHaveChanged; + }, + layoutZone: (id: string): void => { + if (!id) { + return; + } + zonesHaveChanged = this._layoutZone(whitespaceAccessor, id) || zonesHaveChanged; } - zonesHaveChanged = this.removeZone(id) || zonesHaveChanged; - }, - layoutZone: (id: string): void => { - if (!id) { - return; - } - zonesHaveChanged = this.layoutZone(id) || zonesHaveChanged; - } - }; + }; - safeInvoke1Arg(callback, changeAccessor); + safeInvoke1Arg(callback, changeAccessor); - // Invalidate changeAccessor - changeAccessor.addZone = invalidFunc; - changeAccessor.removeZone = invalidFunc; - changeAccessor.layoutZone = invalidFunc; + // Invalidate changeAccessor + changeAccessor.addZone = invalidFunc; + changeAccessor.removeZone = invalidFunc; + changeAccessor.layoutZone = invalidFunc; - return zonesHaveChanged; + return zonesHaveChanged; + }); } - public addZone(zone: IViewZone): string { + private _addZone(whitespaceAccessor: IWhitespaceChangeAccessor, zone: IViewZone): string { const props = this._computeWhitespaceProps(zone); - const whitespaceId = this._context.viewLayout.addWhitespace(props.afterViewLineNumber, this._getZoneOrdinal(zone), props.heightInPx, props.minWidthInPx); + const whitespaceId = whitespaceAccessor.insertWhitespace(props.afterViewLineNumber, this._getZoneOrdinal(zone), props.heightInPx, props.minWidthInPx); const myZone: IMyViewZone = { whitespaceId: whitespaceId, @@ -257,11 +262,11 @@ export class ViewZones extends ViewPart { return myZone.whitespaceId; } - public removeZone(id: string): boolean { + private _removeZone(whitespaceAccessor: IWhitespaceChangeAccessor, id: string): boolean { if (this._zones.hasOwnProperty(id)) { const zone = this._zones[id]; delete this._zones[id]; - this._context.viewLayout.removeWhitespace(zone.whitespaceId); + whitespaceAccessor.removeWhitespace(zone.whitespaceId); zone.domNode.removeAttribute('monaco-visible-view-zone'); zone.domNode.removeAttribute('monaco-view-zone'); @@ -280,13 +285,13 @@ export class ViewZones extends ViewPart { return false; } - public layoutZone(id: string): boolean { + private _layoutZone(whitespaceAccessor: IWhitespaceChangeAccessor, id: string): boolean { let changed = false; 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; + changed = whitespaceAccessor.changeOneWhitespace(zone.whitespaceId, props.afterViewLineNumber, props.heightInPx) || changed; // TODO@Alex: change `newOrdinal` too if (changed) { diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index d3f4d359431..14c7dd29481 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -15,6 +15,15 @@ export class EditorWhitespace { ) { } } +/** + * An accessor that allows for whtiespace to be added, removed or changed in bulk. + */ +export interface IWhitespaceChangeAccessor { + insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string; + changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean; + removeWhitespace(id: string): boolean; +} + /** * Layouting of objects that take vertical space (by having a height) and push down other objects. * @@ -146,6 +155,22 @@ export class LinesLayout { this._lineCount = lineCount; } + public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => T): T { + const accessor = { + insertWhitespace: (afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string => { + return this._insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); + }, + changeOneWhitespace: (id: string, newAfterLineNumber: number, newHeight: number): boolean => { + return this._changeOneWhitespace(id, newAfterLineNumber, newHeight); + }, + removeWhitespace: (id: string): boolean => { + return this._removeWhitespace(id); + } + }; + const r = callback(accessor); + return r; + } + /** * Insert a new whitespace of a certain height after a line number. * The whitespace has a "sticky" characteristic. @@ -155,7 +180,7 @@ 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): string { + private _insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { afterLineNumber = afterLineNumber | 0; ordinal = ordinal | 0; heightInPx = heightInPx | 0; @@ -197,7 +222,7 @@ export class LinesLayout { /** * Change properties associated with a certain whitespace. */ - public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { + private _changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { newAfterLineNumber = newAfterLineNumber | 0; newHeight = newHeight | 0; @@ -253,7 +278,7 @@ export class LinesLayout { const minWidth = this._minWidths[index]; // Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace - this.removeWhitespace(id); + this._removeWhitespace(id); // And add it again const insertionIndex = LinesLayout.findInsertionIndex(this._afterLineNumbers, newAfterLineNumber, this._ordinals, ordinal); @@ -271,7 +296,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: string): boolean { + private _removeWhitespace(id: string): boolean { if (this._whitespaceId2Index.hasOwnProperty(id)) { const index = this._whitespaceId2Index[id]; delete this._whitespaceId2Index[id]; diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index 6fd8a951a57..1fbc3db5462 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -8,7 +8,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { LinesLayout, EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; +import { LinesLayout, EditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel'; @@ -193,15 +193,8 @@ export class ViewLayout extends Disposable implements IViewLayout { } // ---- IVerticalLayoutProvider - - public addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): string { - return this._linesLayout.insertWhitespace(afterLineNumber, ordinal, height, minWidth); - } - public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { - return this._linesLayout.changeWhitespace(id, newAfterLineNumber, newHeight); - } - public removeWhitespace(id: string): boolean { - return this._linesLayout.removeWhitespace(id); + public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => T): T { + return this._linesLayout.changeWhitespace(callback); } public getVerticalOffsetForLineNumber(lineNumber: number): number { return this._linesLayout.getVerticalOffsetForLineNumber(lineNumber); diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 777195ac729..3d23b97c93c 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -13,7 +13,7 @@ import { INewScrollPosition } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, TextModelResolvedOptions } from 'vs/editor/common/model'; import { IViewEventListener } from 'vs/editor/common/view/viewEvents'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import { EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; +import { EditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; import { ITheme } from 'vs/platform/theme/common/themeService'; export interface IViewWhitespaceViewportData { @@ -69,20 +69,8 @@ export interface IViewLayout { getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null; // --------------- Begin vertical whitespace management + changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => T): T; - /** - * 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): string; - /** - * Change the properties of a whitespace. - */ - changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean; - /** - * Remove rendering space - */ - removeWhitespace(id: string): boolean; /** * Get the layout information for whitespaces currently in the viewport */ diff --git a/src/vs/editor/test/common/viewLayout/linesLayout.test.ts b/src/vs/editor/test/common/viewLayout/linesLayout.test.ts index a88c5c16c6d..12f4693761a 100644 --- a/src/vs/editor/test/common/viewLayout/linesLayout.test.ts +++ b/src/vs/editor/test/common/viewLayout/linesLayout.test.ts @@ -7,6 +7,24 @@ import { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout'; suite('Editor ViewLayout - LinesLayout', () => { + function insertWhitespace(linesLayout: LinesLayout, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { + return linesLayout.changeWhitespace((accessor) => { + return accessor.insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); + }); + } + + function changeOneWhitespace(linesLayout: LinesLayout, id: string, newAfterLineNumber: number, newHeight: number): boolean { + return linesLayout.changeWhitespace((accessor) => { + return accessor.changeOneWhitespace(id, newAfterLineNumber, newHeight); + }); + } + + function removeWhitespace(linesLayout: LinesLayout, id: string): boolean { + return linesLayout.changeWhitespace((accessor) => { + return accessor.removeWhitespace(id); + }); + } + test('LinesLayout 1', () => { // Start off with 10 lines @@ -39,7 +57,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(29), 3); // Add whitespace of height 5px after 2nd line - linesLayout.insertWhitespace(2, 0, 5, 0); + insertWhitespace(linesLayout, 2, 0, 5, 0); // lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] // whitespace: a(2,5) assert.equal(linesLayout.getLinesTotalHeight(), 105); @@ -63,8 +81,8 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(105), 10); // Add two more whitespaces of height 5px - linesLayout.insertWhitespace(3, 0, 5, 0); - linesLayout.insertWhitespace(4, 0, 5, 0); + insertWhitespace(linesLayout, 3, 0, 5, 0); + insertWhitespace(linesLayout, 4, 0, 5, 0); // lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] // whitespace: a(2,5), b(3, 5), c(4, 5) assert.equal(linesLayout.getLinesTotalHeight(), 115); @@ -120,7 +138,7 @@ suite('Editor ViewLayout - LinesLayout', () => { // Start off with 10 lines and one whitespace after line 2, of height 5 let linesLayout = new LinesLayout(10, 1); - let a = linesLayout.insertWhitespace(2, 0, 5, 0); + let a = insertWhitespace(linesLayout, 2, 0, 5, 0); // 10 lines // whitespace: - a(2,5) @@ -139,7 +157,7 @@ suite('Editor ViewLayout - LinesLayout', () => { // Change whitespace height // 10 lines // whitespace: - a(2,10) - linesLayout.changeWhitespace(a, 2, 10); + changeOneWhitespace(linesLayout, a, 2, 10); assert.equal(linesLayout.getLinesTotalHeight(), 20); assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1); @@ -155,7 +173,7 @@ suite('Editor ViewLayout - LinesLayout', () => { // Change whitespace position // 10 lines // whitespace: - a(5,10) - linesLayout.changeWhitespace(a, 5, 10); + changeOneWhitespace(linesLayout, a, 5, 10); assert.equal(linesLayout.getLinesTotalHeight(), 20); assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1); @@ -200,7 +218,7 @@ suite('Editor ViewLayout - LinesLayout', () => { // Remove whitespace // 10 lines - linesLayout.removeWhitespace(a); + removeWhitespace(linesLayout, a); assert.equal(linesLayout.getLinesTotalHeight(), 10); assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 0); assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 1); @@ -216,7 +234,7 @@ suite('Editor ViewLayout - LinesLayout', () => { test('LinesLayout getLineNumberAtOrAfterVerticalOffset', () => { let linesLayout = new LinesLayout(10, 1); - linesLayout.insertWhitespace(6, 0, 10, 0); + insertWhitespace(linesLayout, 6, 0, 10, 0); // 10 lines // whitespace: - a(6,10) @@ -265,7 +283,7 @@ suite('Editor ViewLayout - LinesLayout', () => { test('LinesLayout getCenteredLineInViewport', () => { let linesLayout = new LinesLayout(10, 1); - linesLayout.insertWhitespace(6, 0, 10, 0); + insertWhitespace(linesLayout, 6, 0, 10, 0); // 10 lines // whitespace: - a(6,10) @@ -348,7 +366,7 @@ suite('Editor ViewLayout - LinesLayout', () => { test('LinesLayout getLinesViewportData 1', () => { let linesLayout = new LinesLayout(10, 10); - linesLayout.insertWhitespace(6, 0, 100, 0); + insertWhitespace(linesLayout, 6, 0, 100, 0); // 10 lines // whitespace: - a(6,100) @@ -481,8 +499,8 @@ suite('Editor ViewLayout - LinesLayout', () => { test('LinesLayout getLinesViewportData 2 & getWhitespaceViewportData', () => { let linesLayout = new LinesLayout(10, 10); - let a = linesLayout.insertWhitespace(6, 0, 100, 0); - let b = linesLayout.insertWhitespace(7, 0, 50, 0); + let a = insertWhitespace(linesLayout, 6, 0, 100, 0); + let b = insertWhitespace(linesLayout, 7, 0, 50, 0); // 10 lines // whitespace: - a(6,100), b(7, 50) @@ -552,8 +570,8 @@ suite('Editor ViewLayout - LinesLayout', () => { test('LinesLayout getWhitespaceAtVerticalOffset', () => { let linesLayout = new LinesLayout(10, 10); - let a = linesLayout.insertWhitespace(6, 0, 100, 0); - let b = linesLayout.insertWhitespace(7, 0, 50, 0); + let a = insertWhitespace(linesLayout, 6, 0, 100, 0); + let b = insertWhitespace(linesLayout, 7, 0, 50, 0); let whitespace = linesLayout.getWhitespaceAtVerticalOffset(0); assert.equal(whitespace, null); @@ -597,7 +615,7 @@ suite('Editor ViewLayout - LinesLayout', () => { const linesLayout = new LinesLayout(100, 20); // Insert a whitespace after line number 2, of height 10 - const a = linesLayout.insertWhitespace(2, 0, 10, 0); + const a = insertWhitespace(linesLayout, 2, 0, 10, 0); // whitespaces: a(2, 10) assert.equal(linesLayout.getWhitespacesCount(), 1); assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); @@ -610,7 +628,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 10); // Insert a whitespace again after line number 2, of height 20 - let b = linesLayout.insertWhitespace(2, 0, 20, 0); + let b = insertWhitespace(linesLayout, 2, 0, 20, 0); // whitespaces: a(2, 10), b(2, 20) assert.equal(linesLayout.getWhitespacesCount(), 2); assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); @@ -626,7 +644,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 30); // Change last inserted whitespace height to 30 - linesLayout.changeWhitespace(b, 2, 30); + changeOneWhitespace(linesLayout, b, 2, 30); // whitespaces: a(2, 10), b(2, 30) assert.equal(linesLayout.getWhitespacesCount(), 2); assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); @@ -642,7 +660,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 40); // Remove last inserted whitespace - linesLayout.removeWhitespace(b); + removeWhitespace(linesLayout, b); // whitespaces: a(2, 10) assert.equal(linesLayout.getWhitespacesCount(), 1); assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); @@ -655,7 +673,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 10); // Add a whitespace before the first line of height 50 - b = linesLayout.insertWhitespace(0, 0, 50, 0); + b = insertWhitespace(linesLayout, 0, 0, 50, 0); // whitespaces: b(0, 50), a(2, 10) assert.equal(linesLayout.getWhitespacesCount(), 2); assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); @@ -671,7 +689,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(4), 60); // Add a whitespace after line 4 of height 20 - linesLayout.insertWhitespace(4, 0, 20, 0); + insertWhitespace(linesLayout, 4, 0, 20, 0); // whitespaces: b(0, 50), a(2, 10), c(4, 20) assert.equal(linesLayout.getWhitespacesCount(), 3); assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); @@ -691,7 +709,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 80); // Add a whitespace after line 3 of height 30 - linesLayout.insertWhitespace(3, 0, 30, 0); + insertWhitespace(linesLayout, 3, 0, 30, 0); // whitespaces: b(0, 50), a(2, 10), d(3, 30), c(4, 20) assert.equal(linesLayout.getWhitespacesCount(), 4); assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); @@ -714,7 +732,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 110); // Change whitespace after line 2 to height of 100 - linesLayout.changeWhitespace(a, 2, 100); + changeOneWhitespace(linesLayout, a, 2, 100); // whitespaces: b(0, 50), a(2, 100), d(3, 30), c(4, 20) assert.equal(linesLayout.getWhitespacesCount(), 4); assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); @@ -737,7 +755,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 200); // Remove whitespace after line 2 - linesLayout.removeWhitespace(a); + removeWhitespace(linesLayout, a); // whitespaces: b(0, 50), d(3, 30), c(4, 20) assert.equal(linesLayout.getWhitespacesCount(), 3); assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); @@ -757,7 +775,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(5), 100); // Remove whitespace before line 1 - linesLayout.removeWhitespace(b); + removeWhitespace(linesLayout, b); // whitespaces: d(3, 30), c(4, 20) assert.equal(linesLayout.getWhitespacesCount(), 2); assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); @@ -962,9 +980,9 @@ suite('Editor ViewLayout - LinesLayout', () => { test('LinesLayout changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber', () => { const linesLayout = new LinesLayout(100, 20); - const a = linesLayout.insertWhitespace(0, 0, 1, 0); - const b = linesLayout.insertWhitespace(7, 0, 1, 0); - const c = linesLayout.insertWhitespace(3, 0, 1, 0); + const a = insertWhitespace(linesLayout, 0, 0, 1, 0); + const b = insertWhitespace(linesLayout, 7, 0, 1, 0); + const c = insertWhitespace(linesLayout, 3, 0, 1, 0); assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 0); @@ -983,7 +1001,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getFirstWhitespaceIndexAfterLineNumber(8), -1); // -- // Do not really move a - linesLayout.changeWhitespace(a, 1, 1); + changeOneWhitespace(linesLayout, a, 1, 1); assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 1 assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 1); @@ -1003,7 +1021,7 @@ suite('Editor ViewLayout - LinesLayout', () => { // Do not really move a - linesLayout.changeWhitespace(a, 2, 1); + changeOneWhitespace(linesLayout, a, 2, 1); assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 2 assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 2); @@ -1023,7 +1041,7 @@ suite('Editor ViewLayout - LinesLayout', () => { // Change a to conflict with c => a gets placed after c - linesLayout.changeWhitespace(a, 3, 1); + changeOneWhitespace(linesLayout, a, 3, 1); assert.equal(linesLayout.getIdForWhitespaceIndex(0), c); // 3 assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); @@ -1043,7 +1061,7 @@ suite('Editor ViewLayout - LinesLayout', () => { // Make a no-op - linesLayout.changeWhitespace(c, 3, 1); + changeOneWhitespace(linesLayout, c, 3, 1); assert.equal(linesLayout.getIdForWhitespaceIndex(0), c); // 3 assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); @@ -1064,7 +1082,7 @@ suite('Editor ViewLayout - LinesLayout', () => { // Conflict c with b => c gets placed after b - linesLayout.changeWhitespace(c, 7, 1); + changeOneWhitespace(linesLayout, c, 7, 1); assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 3 assert.equal(linesLayout.getAfterLineNumberForWhitespaceIndex(0), 3); @@ -1086,32 +1104,32 @@ suite('Editor ViewLayout - LinesLayout', () => { test('LinesLayout Bug', () => { const linesLayout = new LinesLayout(100, 20); - const a = linesLayout.insertWhitespace(0, 0, 1, 0); - const b = linesLayout.insertWhitespace(7, 0, 1, 0); + const a = insertWhitespace(linesLayout, 0, 0, 1, 0); + const b = insertWhitespace(linesLayout, 7, 0, 1, 0); assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 assert.equal(linesLayout.getIdForWhitespaceIndex(1), b); // 7 - const c = linesLayout.insertWhitespace(3, 0, 1, 0); + const c = insertWhitespace(linesLayout, 3, 0, 1, 0); assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 assert.equal(linesLayout.getIdForWhitespaceIndex(1), c); // 3 assert.equal(linesLayout.getIdForWhitespaceIndex(2), b); // 7 - const d = linesLayout.insertWhitespace(2, 0, 1, 0); + const d = insertWhitespace(linesLayout, 2, 0, 1, 0); assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 - const e = linesLayout.insertWhitespace(8, 0, 1, 0); + const e = insertWhitespace(linesLayout, 8, 0, 1, 0); assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 assert.equal(linesLayout.getIdForWhitespaceIndex(3), b); // 7 assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8 - const f = linesLayout.insertWhitespace(11, 0, 1, 0); + const f = insertWhitespace(linesLayout, 11, 0, 1, 0); assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 @@ -1119,7 +1137,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getIdForWhitespaceIndex(4), e); // 8 assert.equal(linesLayout.getIdForWhitespaceIndex(5), f); // 11 - const g = linesLayout.insertWhitespace(10, 0, 1, 0); + const g = insertWhitespace(linesLayout, 10, 0, 1, 0); assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 assert.equal(linesLayout.getIdForWhitespaceIndex(1), d); // 2 assert.equal(linesLayout.getIdForWhitespaceIndex(2), c); // 3 @@ -1128,7 +1146,7 @@ suite('Editor ViewLayout - LinesLayout', () => { assert.equal(linesLayout.getIdForWhitespaceIndex(5), g); // 10 assert.equal(linesLayout.getIdForWhitespaceIndex(6), f); // 11 - const h = linesLayout.insertWhitespace(0, 0, 1, 0); + const h = insertWhitespace(linesLayout, 0, 0, 1, 0); assert.equal(linesLayout.getIdForWhitespaceIndex(0), a); // 0 assert.equal(linesLayout.getIdForWhitespaceIndex(1), h); // 0 assert.equal(linesLayout.getIdForWhitespaceIndex(2), d); // 2 From 2a49e82150196bfcc19fca3703af53d5b8ec2673 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 15 Nov 2019 16:15:07 +0100 Subject: [PATCH 81/88] Switch to objects in LinesLayout --- .../browser/viewParts/viewZones/viewZones.ts | 24 +- .../editor/common/viewLayout/linesLayout.ts | 375 ++++++++++-------- .../common/viewLayout/linesLayout.test.ts | 235 +++++------ 3 files changed, 330 insertions(+), 304 deletions(-) diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index aca0e779c2f..a2160d78173 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -13,7 +13,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; +import { IWhitespaceChangeAccessor, EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; export interface IMyViewZone { whitespaceId: string; @@ -74,6 +74,11 @@ export class ViewZones extends ViewPart { // ---- begin view event handlers private _recomputeWhitespacesProps(): boolean { + const whitespaces = this._context.viewLayout.getWhitespaces(); + const oldWhitespaces = new Map(); + for (const whitespace of whitespaces) { + oldWhitespaces.set(whitespace.id, whitespace); + } return this._context.viewLayout.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => { let hadAChange = false; @@ -82,7 +87,9 @@ export class ViewZones extends ViewPart { const id = keys[i]; const zone = this._zones[id]; const props = this._computeWhitespaceProps(zone.delegate); - if (whitespaceAccessor.changeOneWhitespace(id, props.afterViewLineNumber, props.heightInPx)) { + const oldWhitespace = oldWhitespaces.get(id); + if (oldWhitespace && (oldWhitespace.afterLineNumber !== props.afterViewLineNumber || oldWhitespace.heightInPx !== props.heightInPx)) { + whitespaceAccessor.changeOneWhitespace(id, props.afterViewLineNumber, props.heightInPx); this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); hadAChange = true; } @@ -286,20 +293,19 @@ export class ViewZones extends ViewPart { } private _layoutZone(whitespaceAccessor: IWhitespaceChangeAccessor, id: string): boolean { - let changed = false; if (this._zones.hasOwnProperty(id)) { const zone = this._zones[id]; const props = this._computeWhitespaceProps(zone.delegate); // const newOrdinal = this._getZoneOrdinal(zone.delegate); - changed = whitespaceAccessor.changeOneWhitespace(zone.whitespaceId, props.afterViewLineNumber, props.heightInPx) || changed; + whitespaceAccessor.changeOneWhitespace(zone.whitespaceId, props.afterViewLineNumber, props.heightInPx); // TODO@Alex: change `newOrdinal` too - if (changed) { - this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); - this.setShouldRender(); - } + this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); + this.setShouldRender(); + + return true; } - return changed; + return false; } public shouldSuppressMouseDownOnViewZone(id: string): boolean { diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index 14c7dd29481..ca6b2fcfacf 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -11,6 +11,7 @@ export class EditorWhitespace { constructor( public readonly id: string, public readonly afterLineNumber: number, + public readonly heightInPx: number, public readonly heightInLines: number, ) { } } @@ -20,8 +21,80 @@ export class EditorWhitespace { */ export interface IWhitespaceChangeAccessor { insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string; - changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean; - removeWhitespace(id: string): boolean; + changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): void; + removeWhitespace(id: string): void; +} + +interface IPendingInsert { id: string; afterLineNumber: number; ordinal: number; heightInPx: number; minWidth: number; } +interface IPendingChange { id: string; newAfterLineNumber: number; newHeight: number; } +interface IPendingRemove { id: string; } + +class PendingChanges { + private _hasPending: boolean; + private _inserts: IPendingInsert[]; + private _changes: IPendingChange[]; + private _removes: IPendingRemove[]; + + constructor() { + this._hasPending = false; + this._inserts = []; + this._changes = []; + this._removes = []; + } + + public insert(x: IPendingInsert): void { + this._hasPending = true; + this._inserts.push(x); + } + + public change(x: IPendingChange): void { + this._hasPending = true; + this._changes.push(x); + } + + public remove(x: IPendingRemove): void { + this._hasPending = true; + this._removes.push(x); + } + + public mustCommit(): boolean { + return this._hasPending; + } + + public commit(linesLayout: LinesLayout): void { + if (!this._hasPending) { + return; + } + + const inserts = this._inserts; + const changes = this._changes; + const removes = this._removes; + + this._hasPending = false; + this._inserts = []; + this._changes = []; + this._removes = []; + + linesLayout._commitPendingChanges(inserts, changes, removes); + } +} + +export class InternalWhitespace { + public id: string; + public afterLineNumber: number; + public ordinal: number; + public height: number; + public minWidth: number; + public prefixSum: number; + + constructor(id: string, afterLineNumber: number, ordinal: number, height: number, minWidth: number) { + this.id = id; + this.afterLineNumber = afterLineNumber; + this.ordinal = ordinal; + this.height = height; + this.minWidth = minWidth; + this.prefixSum = 0; + } } /** @@ -36,41 +109,15 @@ export class LinesLayout { private readonly _instanceId: string; - /** - * heights[i] is the height in pixels for whitespace at index i - */ - private readonly _heights: number[]; + private _pendingChanges: PendingChanges; + + private readonly _arr: InternalWhitespace[]; /** - * minWidths[i] is the min width in pixels for whitespace at index i - */ - private readonly _minWidths: number[]; - - /** - * afterLineNumbers[i] is the line number whitespace at index i is after - */ - private readonly _afterLineNumbers: number[]; - - /** - * ordinals[i] is the orinal of the whitespace at index i - */ - private readonly _ordinals: number[]; - - /** - * prefixSum[i] = SUM(heights[j]), 1 <= j <= i - */ - private readonly _prefixSum: number[]; - - /** - * prefixSum[i], 1 <= i <= prefixSumValidIndex can be trusted + * _arr[i].prefixSum, 1 <= i <= prefixSumValidIndex can be trusted */ private _prefixSumValidIndex: number; - /** - * ids[i] is the whitespace id of whitespace at index i - */ - private readonly _ids: string[]; - /** * index at which a whitespace is positioned (inside heights, afterLineNumbers, prefixSum members) */ @@ -98,12 +145,8 @@ export class LinesLayout { constructor(lineCount: number, lineHeight: number) { this._instanceId = strings.singleLetterHash(++LinesLayout.INSTANCE_COUNT); - this._heights = []; - this._minWidths = []; - this._ids = []; - this._afterLineNumbers = []; - this._ordinals = []; - this._prefixSum = []; + this._pendingChanges = new PendingChanges(); + this._arr = []; this._prefixSumValidIndex = -1; this._whitespaceId2Index = {}; this._lastWhitespaceId = 0; @@ -116,20 +159,20 @@ export class LinesLayout { * Find the insertion index for a new value inside a sorted array of values. * If the value is already present in the sorted array, the insertion index will be after the already existing value. */ - public static findInsertionIndex(sortedArray: number[], value: number, ordinals: number[], valueOrdinal: number): number { + public static findInsertionIndex(arr: InternalWhitespace[], afterLineNumber: number, ordinal: number): number { let low = 0; - let high = sortedArray.length; + let high = arr.length; while (low < high) { const mid = ((low + high) >>> 1); - if (value === sortedArray[mid]) { - if (valueOrdinal < ordinals[mid]) { + if (afterLineNumber === arr[mid].afterLineNumber) { + if (ordinal < arr[mid].ordinal) { high = mid; } else { low = mid + 1; } - } else if (value < sortedArray[mid]) { + } else if (afterLineNumber < arr[mid].afterLineNumber) { high = mid; } else { low = mid + 1; @@ -143,6 +186,7 @@ export class LinesLayout { * Change the height of a line in pixels. */ public setLineHeight(lineHeight: number): void { + this._checkPendingChanges(); this._lineHeight = lineHeight; } @@ -152,23 +196,59 @@ export class LinesLayout { * @param lineCount New number of lines. */ public onFlushed(lineCount: number): void { + this._checkPendingChanges(); this._lineCount = lineCount; } public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => T): T { - const accessor = { - insertWhitespace: (afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string => { - return this._insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); - }, - changeOneWhitespace: (id: string, newAfterLineNumber: number, newHeight: number): boolean => { - return this._changeOneWhitespace(id, newAfterLineNumber, newHeight); - }, - removeWhitespace: (id: string): boolean => { - return this._removeWhitespace(id); - } - }; - const r = callback(accessor); - return r; + try { + const accessor = { + insertWhitespace: (afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string => { + afterLineNumber = afterLineNumber | 0; + ordinal = ordinal | 0; + heightInPx = heightInPx | 0; + minWidth = minWidth | 0; + + const id = this._instanceId + (++this._lastWhitespaceId); + this._pendingChanges.insert({ id, afterLineNumber, ordinal, heightInPx, minWidth }); + return id; + // return this._insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); + }, + changeOneWhitespace: (id: string, newAfterLineNumber: number, newHeight: number): void => { + newAfterLineNumber = newAfterLineNumber | 0; + newHeight = newHeight | 0; + + this._pendingChanges.change({ id, newAfterLineNumber, newHeight }); + // this._changeOneWhitespace(id, newAfterLineNumber, newHeight); + }, + removeWhitespace: (id: string): void => { + this._pendingChanges.remove({ id }); + // this._removeWhitespace(id); + } + }; + return callback(accessor); + } finally { + this._pendingChanges.commit(this); + } + } + + public _commitPendingChanges(inserts: IPendingInsert[], changes: IPendingChange[], removes: IPendingRemove[]): void { + for (const insert of inserts) { + this._insertWhitespace(insert.id, insert.afterLineNumber, insert.ordinal, insert.heightInPx, insert.minWidth); + } + for (const change of changes) { + this._changeOneWhitespace(change.id, change.newAfterLineNumber, change.newHeight); + } + for (const remove of removes) { + this._removeWhitespace(remove.id); + } + // console.log(`TODO: `, inserts, changes, removes); + } + + private _checkPendingChanges(): void { + if (this._pendingChanges.mustCommit()) { + console.log(`I should commit pending changes!`);//TODO + } } /** @@ -180,32 +260,16 @@ 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 */ - private _insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { - afterLineNumber = afterLineNumber | 0; - ordinal = ordinal | 0; - heightInPx = heightInPx | 0; - minWidth = minWidth | 0; - - const id = this._instanceId + (++this._lastWhitespaceId); - const insertionIndex = LinesLayout.findInsertionIndex(this._afterLineNumbers, afterLineNumber, this._ordinals, ordinal); - this._insertWhitespaceAtIndex(id, insertionIndex, afterLineNumber, ordinal, heightInPx, minWidth); + private _insertWhitespace(id: string, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { + const whitespace = new InternalWhitespace(id, afterLineNumber, ordinal, heightInPx, minWidth); + const insertionIndex = LinesLayout.findInsertionIndex(this._arr, whitespace.afterLineNumber, whitespace.ordinal); + this._insertWhitespaceAtIndex(insertionIndex, whitespace); this._minWidth = -1; /* marker for not being computed */ return id; } - private _insertWhitespaceAtIndex(id: string, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): void { - insertIndex = insertIndex | 0; - afterLineNumber = afterLineNumber | 0; - ordinal = ordinal | 0; - heightInPx = heightInPx | 0; - minWidth = minWidth | 0; - - this._heights.splice(insertIndex, 0, heightInPx); - this._minWidths.splice(insertIndex, 0, minWidth); - this._ids.splice(insertIndex, 0, id); - this._afterLineNumbers.splice(insertIndex, 0, afterLineNumber); - this._ordinals.splice(insertIndex, 0, ordinal); - this._prefixSum.splice(insertIndex, 0, 0); + private _insertWhitespaceAtIndex(insertIndex: number, whitespace: InternalWhitespace): void { + this._arr.splice(insertIndex, 0, whitespace); const keys = Object.keys(this._whitespaceId2Index); for (const sid of keys) { @@ -215,79 +279,36 @@ export class LinesLayout { } } - this._whitespaceId2Index[id] = insertIndex; + this._whitespaceId2Index[whitespace.id] = insertIndex; this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1); } /** * Change properties associated with a certain whitespace. */ - private _changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { - newAfterLineNumber = newAfterLineNumber | 0; - newHeight = newHeight | 0; - - let hasChanges = false; - hasChanges = this.changeWhitespaceHeight(id, newHeight) || hasChanges; - hasChanges = this.changeWhitespaceAfterLineNumber(id, newAfterLineNumber) || hasChanges; - return hasChanges; - } - - /** - * Change the height of an existing whitespace - * - * @param id The whitespace to change - * @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: string, newHeightInPx: number): boolean { - newHeightInPx = newHeightInPx | 0; - + private _changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): void { if (this._whitespaceId2Index.hasOwnProperty(id)) { const index = this._whitespaceId2Index[id]; - if (this._heights[index] !== newHeightInPx) { - this._heights[index] = newHeightInPx; + if (this._arr[index].height !== newHeight) { + this._arr[index].height = newHeight; this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1); - return true; } - } - return false; - } - - /** - * Change the line number after which an existing whitespace flows. - * - * @param id The whitespace to change - * @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: string, newAfterLineNumber: number): boolean { - newAfterLineNumber = newAfterLineNumber | 0; - - if (this._whitespaceId2Index.hasOwnProperty(id)) { - const index = this._whitespaceId2Index[id]; - if (this._afterLineNumbers[index] !== newAfterLineNumber) { + if (this._arr[index].afterLineNumber !== newAfterLineNumber) { // `afterLineNumber` changed for this whitespace - // Record old ordinal - const ordinal = this._ordinals[index]; - - // Record old height - const heightInPx = this._heights[index]; - - // Record old min width - const minWidth = this._minWidths[index]; + // Record old whitespace + const whitespace = this._arr[index]; // Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace this._removeWhitespace(id); - // And add it again - const insertionIndex = LinesLayout.findInsertionIndex(this._afterLineNumbers, newAfterLineNumber, this._ordinals, ordinal); - this._insertWhitespaceAtIndex(id, insertionIndex, newAfterLineNumber, ordinal, heightInPx, minWidth); + whitespace.afterLineNumber = newAfterLineNumber; - return true; + // And add it again + const insertionIndex = LinesLayout.findInsertionIndex(this._arr, whitespace.afterLineNumber, whitespace.ordinal); + this._insertWhitespaceAtIndex(insertionIndex, whitespace); } } - return false; } /** @@ -296,27 +317,18 @@ export class LinesLayout { * @param id The whitespace to remove * @return Returns true if the whitespace is found and it is removed. */ - private _removeWhitespace(id: string): boolean { + private _removeWhitespace(id: string): void { if (this._whitespaceId2Index.hasOwnProperty(id)) { const index = this._whitespaceId2Index[id]; delete this._whitespaceId2Index[id]; this._removeWhitespaceAtIndex(index); this._minWidth = -1; /* marker for not being computed */ - return true; } - - return false; } private _removeWhitespaceAtIndex(removeIndex: number): void { - removeIndex = removeIndex | 0; - this._heights.splice(removeIndex, 1); - this._minWidths.splice(removeIndex, 1); - this._ids.splice(removeIndex, 1); - this._afterLineNumbers.splice(removeIndex, 1); - this._ordinals.splice(removeIndex, 1); - this._prefixSum.splice(removeIndex, 1); + this._arr.splice(removeIndex, 1); this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, removeIndex - 1); const keys = Object.keys(this._whitespaceId2Index); @@ -335,21 +347,22 @@ export class LinesLayout { * @param toLineNumber The line number at which the deletion ended, inclusive */ public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void { + this._checkPendingChanges(); fromLineNumber = fromLineNumber | 0; toLineNumber = toLineNumber | 0; this._lineCount -= (toLineNumber - fromLineNumber + 1); - for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) { - const afterLineNumber = this._afterLineNumbers[i]; + for (let i = 0, len = this._arr.length; i < len; i++) { + const afterLineNumber = this._arr[i].afterLineNumber; if (fromLineNumber <= afterLineNumber && afterLineNumber <= toLineNumber) { // The line this whitespace was after has been deleted // => move whitespace to before first deleted line - this._afterLineNumbers[i] = fromLineNumber - 1; + this._arr[i].afterLineNumber = fromLineNumber - 1; } else if (afterLineNumber > toLineNumber) { // The line this whitespace was after has been moved up // => move whitespace up - this._afterLineNumbers[i] -= (toLineNumber - fromLineNumber + 1); + this._arr[i].afterLineNumber -= (toLineNumber - fromLineNumber + 1); } } } @@ -361,15 +374,16 @@ export class LinesLayout { * @param toLineNumber The line number at which the insertion ended, inclusive. */ public onLinesInserted(fromLineNumber: number, toLineNumber: number): void { + this._checkPendingChanges(); fromLineNumber = fromLineNumber | 0; toLineNumber = toLineNumber | 0; this._lineCount += (toLineNumber - fromLineNumber + 1); - for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) { - const afterLineNumber = this._afterLineNumbers[i]; + for (let i = 0, len = this._arr.length; i < len; i++) { + const afterLineNumber = this._arr[i].afterLineNumber; if (fromLineNumber <= afterLineNumber) { - this._afterLineNumbers[i] += (toLineNumber - fromLineNumber + 1); + this._arr[i].afterLineNumber += (toLineNumber - fromLineNumber + 1); } } } @@ -378,10 +392,11 @@ export class LinesLayout { * Get the sum of all the whitespaces. */ public getWhitespacesTotalHeight(): number { - if (this._heights.length === 0) { + this._checkPendingChanges(); + if (this._arr.length === 0) { return 0; } - return this.getWhitespacesAccumulatedHeight(this._heights.length - 1); + return this.getWhitespacesAccumulatedHeight(this._arr.length - 1); } /** @@ -392,19 +407,20 @@ export class LinesLayout { * @return The sum of the heights of all whitespaces before the one at `index`, including the one at `index`. */ public getWhitespacesAccumulatedHeight(index: number): number { + this._checkPendingChanges(); index = index | 0; let startIndex = Math.max(0, this._prefixSumValidIndex + 1); if (startIndex === 0) { - this._prefixSum[0] = this._heights[0]; + this._arr[0].prefixSum = this._arr[0].height; startIndex++; } for (let i = startIndex; i <= index; i++) { - this._prefixSum[i] = this._prefixSum[i - 1] + this._heights[i]; + this._arr[i].prefixSum = this._arr[i - 1].prefixSum + this._arr[i].height; } this._prefixSumValidIndex = Math.max(this._prefixSumValidIndex, index); - return this._prefixSum[index]; + return this._arr[index].prefixSum; } /** @@ -413,6 +429,7 @@ export class LinesLayout { * @return The sum of heights for all objects. */ public getLinesTotalHeight(): number { + this._checkPendingChanges(); const linesHeight = this._lineHeight * this._lineCount; const whitespacesHeight = this.getWhitespacesTotalHeight(); return linesHeight + whitespacesHeight; @@ -424,6 +441,7 @@ export class LinesLayout { * @param lineNumber The line number */ public getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber: number): number { + this._checkPendingChanges(); lineNumber = lineNumber | 0; const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); @@ -439,17 +457,17 @@ export class LinesLayout { lineNumber = lineNumber | 0; // Find the whitespace before line number - const afterLineNumbers = this._afterLineNumbers; + const arr = this._arr; let low = 0; - let high = afterLineNumbers.length - 1; + let high = arr.length - 1; while (low <= high) { const delta = (high - low) | 0; const halfDelta = (delta / 2) | 0; const mid = (low + halfDelta) | 0; - if (afterLineNumbers[mid] < lineNumber) { - if (mid + 1 >= afterLineNumbers.length || afterLineNumbers[mid + 1] >= lineNumber) { + if (arr[mid].afterLineNumber < lineNumber) { + if (mid + 1 >= arr.length || arr[mid + 1].afterLineNumber >= lineNumber) { return mid; } else { low = (mid + 1) | 0; @@ -468,7 +486,7 @@ export class LinesLayout { const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); const firstWhitespaceAfterLineNumber = lastWhitespaceBeforeLineNumber + 1; - if (firstWhitespaceAfterLineNumber < this._heights.length) { + if (firstWhitespaceAfterLineNumber < this._arr.length) { return firstWhitespaceAfterLineNumber; } @@ -480,6 +498,7 @@ export class LinesLayout { * @return The index of the first whitespace with `afterLineNumber` >= `lineNumber` or -1 if no whitespace is found. */ public getFirstWhitespaceIndexAfterLineNumber(lineNumber: number): number { + this._checkPendingChanges(); lineNumber = lineNumber | 0; return this._findFirstWhitespaceAfterLineNumber(lineNumber); @@ -492,6 +511,7 @@ export class LinesLayout { * @return The sum of heights for all objects above `lineNumber`. */ public getVerticalOffsetForLineNumber(lineNumber: number): number { + this._checkPendingChanges(); lineNumber = lineNumber | 0; let previousLinesHeight: number; @@ -510,6 +530,7 @@ export class LinesLayout { * Returns if there is any whitespace in the document. */ public hasWhitespace(): boolean { + this._checkPendingChanges(); return this.getWhitespacesCount() > 0; } @@ -517,10 +538,11 @@ export class LinesLayout { * The maximum min width for all whitespaces. */ public getWhitespaceMinWidth(): number { + this._checkPendingChanges(); if (this._minWidth === -1) { let minWidth = 0; - for (let i = 0, len = this._minWidths.length; i < len; i++) { - minWidth = Math.max(minWidth, this._minWidths[i]); + for (let i = 0, len = this._arr.length; i < len; i++) { + minWidth = Math.max(minWidth, this._arr[i].minWidth); } this._minWidth = minWidth; } @@ -531,6 +553,7 @@ export class LinesLayout { * Check if `verticalOffset` is below all lines. */ public isAfterLines(verticalOffset: number): boolean { + this._checkPendingChanges(); const totalHeight = this.getLinesTotalHeight(); return verticalOffset > totalHeight; } @@ -544,6 +567,7 @@ export class LinesLayout { * @return The line number at or after vertical offset `verticalOffset`. */ public getLineNumberAtOrAfterVerticalOffset(verticalOffset: number): number { + this._checkPendingChanges(); verticalOffset = verticalOffset | 0; if (verticalOffset < 0) { @@ -587,6 +611,7 @@ export class LinesLayout { * @return A structure describing the lines positioned between `verticalOffset1` and `verticalOffset2`. */ public getLinesViewportData(verticalOffset1: number, verticalOffset2: number): IPartialViewLinesViewportData { + this._checkPendingChanges(); verticalOffset1 = verticalOffset1 | 0; verticalOffset2 = verticalOffset2 | 0; const lineHeight = this._lineHeight; @@ -704,6 +729,7 @@ export class LinesLayout { } public getVerticalOffsetForWhitespaceIndex(whitespaceIndex: number): number { + this._checkPendingChanges(); whitespaceIndex = whitespaceIndex | 0; const afterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex); @@ -725,6 +751,7 @@ export class LinesLayout { } public getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset: number): number { + this._checkPendingChanges(); verticalOffset = verticalOffset | 0; let minWhitespaceIndex = 0; @@ -768,6 +795,7 @@ export class LinesLayout { * @return Precisely the whitespace that is layouted at `verticaloffset` or null. */ public getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null { + this._checkPendingChanges(); verticalOffset = verticalOffset | 0; const candidateIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset); @@ -806,6 +834,7 @@ export class LinesLayout { * @return An array with all the whitespaces in the viewport. If no whitespace is in viewport, the array is empty. */ public getWhitespaceViewportData(verticalOffset1: number, verticalOffset2: number): IViewWhitespaceViewportData[] { + this._checkPendingChanges(); verticalOffset1 = verticalOffset1 | 0; verticalOffset2 = verticalOffset2 | 0; @@ -839,12 +868,14 @@ export class LinesLayout { * Get all whitespaces. */ public getWhitespaces(): EditorWhitespace[] { + this._checkPendingChanges(); let result: EditorWhitespace[] = []; - for (let i = 0; i < this._heights.length; i++) { + for (let i = 0; i < this._arr.length; i++) { result.push(new EditorWhitespace( - this._ids[i], - this._afterLineNumbers[i], - this._heights[i] / this._lineHeight + this._arr[i].id, + this._arr[i].afterLineNumber, + this._arr[i].height, + this._arr[i].height / this._lineHeight )); } return result; @@ -854,7 +885,8 @@ export class LinesLayout { * The number of whitespaces. */ public getWhitespacesCount(): number { - return this._heights.length; + this._checkPendingChanges(); + return this._arr.length; } /** @@ -864,9 +896,10 @@ export class LinesLayout { * @return `id` of whitespace at `index`. */ public getIdForWhitespaceIndex(index: number): string { + this._checkPendingChanges(); index = index | 0; - return this._ids[index]; + return this._arr[index].id; } /** @@ -876,9 +909,10 @@ export class LinesLayout { * @return `afterLineNumber` of whitespace at `index`. */ public getAfterLineNumberForWhitespaceIndex(index: number): number { + this._checkPendingChanges(); index = index | 0; - return this._afterLineNumbers[index]; + return this._arr[index].afterLineNumber; } /** @@ -888,8 +922,9 @@ export class LinesLayout { * @return `height` of whitespace at `index`. */ public getHeightForWhitespaceIndex(index: number): number { + this._checkPendingChanges(); index = index | 0; - return this._heights[index]; + return this._arr[index].height; } } diff --git a/src/vs/editor/test/common/viewLayout/linesLayout.test.ts b/src/vs/editor/test/common/viewLayout/linesLayout.test.ts index 12f4693761a..57ee0db5427 100644 --- a/src/vs/editor/test/common/viewLayout/linesLayout.test.ts +++ b/src/vs/editor/test/common/viewLayout/linesLayout.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 { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout'; +import { LinesLayout, InternalWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; suite('Editor ViewLayout - LinesLayout', () => { @@ -13,15 +13,15 @@ suite('Editor ViewLayout - LinesLayout', () => { }); } - function changeOneWhitespace(linesLayout: LinesLayout, id: string, newAfterLineNumber: number, newHeight: number): boolean { - return linesLayout.changeWhitespace((accessor) => { - return accessor.changeOneWhitespace(id, newAfterLineNumber, newHeight); + function changeOneWhitespace(linesLayout: LinesLayout, id: string, newAfterLineNumber: number, newHeight: number): void { + linesLayout.changeWhitespace((accessor) => { + accessor.changeOneWhitespace(id, newAfterLineNumber, newHeight); }); } - function removeWhitespace(linesLayout: LinesLayout, id: string): boolean { - return linesLayout.changeWhitespace((accessor) => { - return accessor.removeWhitespace(id); + function removeWhitespace(linesLayout: LinesLayout, id: string): void { + linesLayout.changeWhitespace((accessor) => { + accessor.removeWhitespace(id); }); } @@ -845,136 +845,121 @@ suite('Editor ViewLayout - LinesLayout', () => { test('LinesLayout findInsertionIndex', () => { - const makeArray = (size: number, fillValue: number) => { - let r: number[] = []; - for (let i = 0; i < size; i++) { - r[i] = fillValue; - } - return r; + const makeInternalWhitespace = (afterLineNumbers: number[], ordinal: number = 0) => { + return afterLineNumbers.map((afterLineNumber) => new InternalWhitespace('', afterLineNumber, ordinal, 0, 0)); }; - let arr: number[]; - let ordinals: number[]; + let arr: InternalWhitespace[]; - arr = []; - ordinals = makeArray(arr.length, 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 0); + arr = makeInternalWhitespace([]); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 0); - arr = [1]; - ordinals = makeArray(arr.length, 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); + arr = makeInternalWhitespace([1]); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1); - arr = [1, 3]; - ordinals = makeArray(arr.length, 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); + arr = makeInternalWhitespace([1, 3]); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2); - arr = [1, 3, 5]; - ordinals = makeArray(arr.length, 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 3); - assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); + arr = makeInternalWhitespace([1, 3, 5]); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3); - arr = [1, 3, 5]; - ordinals = makeArray(arr.length, 3); - assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); + arr = makeInternalWhitespace([1, 3, 5], 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3); - arr = [1, 3, 5, 7]; - ordinals = makeArray(arr.length, 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 3); - assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); - assert.equal(LinesLayout.findInsertionIndex(arr, 7, ordinals, 0), 4); - assert.equal(LinesLayout.findInsertionIndex(arr, 8, ordinals, 0), 4); + arr = makeInternalWhitespace([1, 3, 5, 7]); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 7, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 8, 0), 4); - arr = [1, 3, 5, 7, 9]; - ordinals = makeArray(arr.length, 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 3); - assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); - assert.equal(LinesLayout.findInsertionIndex(arr, 7, ordinals, 0), 4); - assert.equal(LinesLayout.findInsertionIndex(arr, 8, ordinals, 0), 4); - assert.equal(LinesLayout.findInsertionIndex(arr, 9, ordinals, 0), 5); - assert.equal(LinesLayout.findInsertionIndex(arr, 10, ordinals, 0), 5); + arr = makeInternalWhitespace([1, 3, 5, 7, 9]); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 7, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 8, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 9, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 10, 0), 5); - arr = [1, 3, 5, 7, 9, 11]; - ordinals = makeArray(arr.length, 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 3); - assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); - assert.equal(LinesLayout.findInsertionIndex(arr, 7, ordinals, 0), 4); - assert.equal(LinesLayout.findInsertionIndex(arr, 8, ordinals, 0), 4); - assert.equal(LinesLayout.findInsertionIndex(arr, 9, ordinals, 0), 5); - assert.equal(LinesLayout.findInsertionIndex(arr, 10, ordinals, 0), 5); - assert.equal(LinesLayout.findInsertionIndex(arr, 11, ordinals, 0), 6); - assert.equal(LinesLayout.findInsertionIndex(arr, 12, ordinals, 0), 6); + arr = makeInternalWhitespace([1, 3, 5, 7, 9, 11]); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 7, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 8, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 9, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 10, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 11, 0), 6); + assert.equal(LinesLayout.findInsertionIndex(arr, 12, 0), 6); - arr = [1, 3, 5, 7, 9, 11, 13]; - ordinals = makeArray(arr.length, 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 3); - assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); - assert.equal(LinesLayout.findInsertionIndex(arr, 7, ordinals, 0), 4); - assert.equal(LinesLayout.findInsertionIndex(arr, 8, ordinals, 0), 4); - assert.equal(LinesLayout.findInsertionIndex(arr, 9, ordinals, 0), 5); - assert.equal(LinesLayout.findInsertionIndex(arr, 10, ordinals, 0), 5); - assert.equal(LinesLayout.findInsertionIndex(arr, 11, ordinals, 0), 6); - assert.equal(LinesLayout.findInsertionIndex(arr, 12, ordinals, 0), 6); - assert.equal(LinesLayout.findInsertionIndex(arr, 13, ordinals, 0), 7); - assert.equal(LinesLayout.findInsertionIndex(arr, 14, ordinals, 0), 7); + arr = makeInternalWhitespace([1, 3, 5, 7, 9, 11, 13]); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 7, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 8, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 9, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 10, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 11, 0), 6); + assert.equal(LinesLayout.findInsertionIndex(arr, 12, 0), 6); + assert.equal(LinesLayout.findInsertionIndex(arr, 13, 0), 7); + assert.equal(LinesLayout.findInsertionIndex(arr, 14, 0), 7); - arr = [1, 3, 5, 7, 9, 11, 13, 15]; - ordinals = makeArray(arr.length, 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 0, ordinals, 0), 0); - assert.equal(LinesLayout.findInsertionIndex(arr, 1, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 2, ordinals, 0), 1); - assert.equal(LinesLayout.findInsertionIndex(arr, 3, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 4, ordinals, 0), 2); - assert.equal(LinesLayout.findInsertionIndex(arr, 5, ordinals, 0), 3); - assert.equal(LinesLayout.findInsertionIndex(arr, 6, ordinals, 0), 3); - assert.equal(LinesLayout.findInsertionIndex(arr, 7, ordinals, 0), 4); - assert.equal(LinesLayout.findInsertionIndex(arr, 8, ordinals, 0), 4); - assert.equal(LinesLayout.findInsertionIndex(arr, 9, ordinals, 0), 5); - assert.equal(LinesLayout.findInsertionIndex(arr, 10, ordinals, 0), 5); - assert.equal(LinesLayout.findInsertionIndex(arr, 11, ordinals, 0), 6); - assert.equal(LinesLayout.findInsertionIndex(arr, 12, ordinals, 0), 6); - assert.equal(LinesLayout.findInsertionIndex(arr, 13, ordinals, 0), 7); - assert.equal(LinesLayout.findInsertionIndex(arr, 14, ordinals, 0), 7); - assert.equal(LinesLayout.findInsertionIndex(arr, 15, ordinals, 0), 8); - assert.equal(LinesLayout.findInsertionIndex(arr, 16, ordinals, 0), 8); + arr = makeInternalWhitespace([1, 3, 5, 7, 9, 11, 13, 15]); + assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0); + assert.equal(LinesLayout.findInsertionIndex(arr, 1, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 2, 0), 1); + assert.equal(LinesLayout.findInsertionIndex(arr, 3, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 4, 0), 2); + assert.equal(LinesLayout.findInsertionIndex(arr, 5, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 6, 0), 3); + assert.equal(LinesLayout.findInsertionIndex(arr, 7, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 8, 0), 4); + assert.equal(LinesLayout.findInsertionIndex(arr, 9, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 10, 0), 5); + assert.equal(LinesLayout.findInsertionIndex(arr, 11, 0), 6); + assert.equal(LinesLayout.findInsertionIndex(arr, 12, 0), 6); + assert.equal(LinesLayout.findInsertionIndex(arr, 13, 0), 7); + assert.equal(LinesLayout.findInsertionIndex(arr, 14, 0), 7); + assert.equal(LinesLayout.findInsertionIndex(arr, 15, 0), 8); + assert.equal(LinesLayout.findInsertionIndex(arr, 16, 0), 8); }); test('LinesLayout changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber', () => { From 4e8f42499d72c7ce1afa97d2ddfbe2ec5f6779d8 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 15 Nov 2019 16:22:40 +0100 Subject: [PATCH 82/88] More type reuse --- .../editor/common/viewLayout/linesLayout.ts | 40 ++++++------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index ca6b2fcfacf..496c48d32ae 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -25,13 +25,12 @@ export interface IWhitespaceChangeAccessor { removeWhitespace(id: string): void; } -interface IPendingInsert { id: string; afterLineNumber: number; ordinal: number; heightInPx: number; minWidth: number; } interface IPendingChange { id: string; newAfterLineNumber: number; newHeight: number; } interface IPendingRemove { id: string; } class PendingChanges { private _hasPending: boolean; - private _inserts: IPendingInsert[]; + private _inserts: InternalWhitespace[]; private _changes: IPendingChange[]; private _removes: IPendingRemove[]; @@ -42,7 +41,7 @@ class PendingChanges { this._removes = []; } - public insert(x: IPendingInsert): void { + public insert(x: InternalWhitespace): void { this._hasPending = true; this._inserts.push(x); } @@ -210,7 +209,7 @@ export class LinesLayout { minWidth = minWidth | 0; const id = this._instanceId + (++this._lastWhitespaceId); - this._pendingChanges.insert({ id, afterLineNumber, ordinal, heightInPx, minWidth }); + this._pendingChanges.insert(new InternalWhitespace(id, afterLineNumber, ordinal, heightInPx, minWidth)); return id; // return this._insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); }, @@ -232,9 +231,12 @@ export class LinesLayout { } } - public _commitPendingChanges(inserts: IPendingInsert[], changes: IPendingChange[], removes: IPendingRemove[]): void { + public _commitPendingChanges(inserts: InternalWhitespace[], changes: IPendingChange[], removes: IPendingRemove[]): void { + // const magnitude = inserts.length + changes.length + removes.length; + // if (magnitude === 1) { + // // when only one thing for (const insert of inserts) { - this._insertWhitespace(insert.id, insert.afterLineNumber, insert.ordinal, insert.heightInPx, insert.minWidth); + this._insertWhitespace(insert); } for (const change of changes) { this._changeOneWhitespace(change.id, change.newAfterLineNumber, change.newHeight); @@ -242,6 +244,10 @@ export class LinesLayout { for (const remove of removes) { this._removeWhitespace(remove.id); } + return; + // } + + // console.log(`magnitude: ${magnitude} -- inserts: ${inserts.length}, changes: ${changes.length}, removes: ${removes.length}`); // console.log(`TODO: `, inserts, changes, removes); } @@ -251,21 +257,10 @@ export class LinesLayout { } } - /** - * Insert a new whitespace of a certain height after a line number. - * The whitespace has a "sticky" characteristic. - * Irrespective of edits above or below `afterLineNumber`, the whitespace will follow the initial line. - * - * @param afterLineNumber The conceptual position of this whitespace. The whitespace will follow this line as best as possible even when deleting/inserting lines above/below. - * @param heightInPx The height of the whitespace, in pixels. - * @return An id that can be used later to mutate or delete the whitespace - */ - private _insertWhitespace(id: string, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { - const whitespace = new InternalWhitespace(id, afterLineNumber, ordinal, heightInPx, minWidth); + private _insertWhitespace(whitespace: InternalWhitespace): void { const insertionIndex = LinesLayout.findInsertionIndex(this._arr, whitespace.afterLineNumber, whitespace.ordinal); this._insertWhitespaceAtIndex(insertionIndex, whitespace); this._minWidth = -1; /* marker for not being computed */ - return id; } private _insertWhitespaceAtIndex(insertIndex: number, whitespace: InternalWhitespace): void { @@ -283,9 +278,6 @@ export class LinesLayout { this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1); } - /** - * Change properties associated with a certain whitespace. - */ private _changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): void { if (this._whitespaceId2Index.hasOwnProperty(id)) { const index = this._whitespaceId2Index[id]; @@ -311,12 +303,6 @@ export class LinesLayout { } } - /** - * Remove an existing whitespace. - * - * @param id The whitespace to remove - * @return Returns true if the whitespace is found and it is removed. - */ private _removeWhitespace(id: string): void { if (this._whitespaceId2Index.hasOwnProperty(id)) { const index = this._whitespaceId2Index[id]; From 9c0ac421fc6c2b0df8a5b980e42b7d2943822d4a Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 15 Nov 2019 16:45:48 +0100 Subject: [PATCH 83/88] Avoid allocating for getWhitespaces() call --- src/vs/editor/browser/editorBrowser.ts | 4 +- .../browser/viewParts/viewZones/viewZones.ts | 6 +-- .../editor/browser/widget/codeEditorWidget.ts | 4 +- .../editor/browser/widget/diffEditorWidget.ts | 44 ++++++++++--------- .../editor/common/viewLayout/linesLayout.ts | 42 +++++++----------- src/vs/editor/common/viewLayout/viewLayout.ts | 4 +- src/vs/editor/common/viewModel/viewModel.ts | 4 +- .../common/viewLayout/linesLayout.test.ts | 6 +-- 8 files changed, 53 insertions(+), 61 deletions(-) diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 26b562f400f..bcd0995f843 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -16,7 +16,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; 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 { EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IDiffComputationResult } from 'vs/editor/common/services/editorWorkerService'; @@ -672,7 +672,7 @@ export interface ICodeEditor extends editorCommon.IEditor { * Get the view zones. * @internal */ - getWhitespaces(): EditorWhitespace[]; + getWhitespaces(): IEditorWhitespace[]; /** * Get the vertical position (top offset) for the line w.r.t. to the first line. diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index a2160d78173..5e28b7785ec 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -13,7 +13,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { IWhitespaceChangeAccessor, EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; +import { IWhitespaceChangeAccessor, IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; export interface IMyViewZone { whitespaceId: string; @@ -75,7 +75,7 @@ export class ViewZones extends ViewPart { private _recomputeWhitespacesProps(): boolean { const whitespaces = this._context.viewLayout.getWhitespaces(); - const oldWhitespaces = new Map(); + const oldWhitespaces = new Map(); for (const whitespace of whitespaces) { oldWhitespaces.set(whitespace.id, whitespace); } @@ -88,7 +88,7 @@ export class ViewZones extends ViewPart { const zone = this._zones[id]; const props = this._computeWhitespaceProps(zone.delegate); const oldWhitespace = oldWhitespaces.get(id); - if (oldWhitespace && (oldWhitespace.afterLineNumber !== props.afterViewLineNumber || oldWhitespace.heightInPx !== props.heightInPx)) { + if (oldWhitespace && (oldWhitespace.afterLineNumber !== props.afterViewLineNumber || oldWhitespace.height !== props.heightInPx)) { whitespaceAccessor.changeOneWhitespace(id, props.afterViewLineNumber, props.heightInPx); this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); hadAChange = true; diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 348487009ef..357eeab2a9f 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -40,7 +40,7 @@ import * as modes from 'vs/editor/common/modes'; import { editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity } from 'vs/editor/common/view/editorColorRegistry'; import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; -import { EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -451,7 +451,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._modelData.viewModel.getVisibleRanges(); } - public getWhitespaces(): EditorWhitespace[] { + public getWhitespaces(): IEditorWhitespace[] { if (!this._modelData) { return []; } diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index d97ef2e6f19..9dbc302244e 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -32,7 +32,7 @@ import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/s import { OverviewRulerZone } from 'vs/editor/common/view/overviewZoneManager'; import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; -import { EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { InlineDecoration, InlineDecorationType, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -68,7 +68,7 @@ interface IEditorsZones { } interface IDiffEditorWidgetStyle { - getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: EditorWhitespace[], modifiedWhitespaces: EditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones; + getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones; setEnableSplitViewResizing(enableSplitViewResizing: boolean): void; applyColors(theme: ITheme): boolean; layout(): number; @@ -91,7 +91,7 @@ class VisualEditorState { this._decorations = []; } - public getForeignViewZones(allViewZones: EditorWhitespace[]): EditorWhitespace[] { + public getForeignViewZones(allViewZones: IEditorWhitespace[]): IEditorWhitespace[] { return allViewZones.filter((z) => !this._zonesMap[String(z.id)]); } @@ -1303,7 +1303,7 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi return hasChanges; } - public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: EditorWhitespace[], modifiedWhitespaces: EditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones { + public getEditorsDiffDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsDiffDecorationsWithZones { // Get view zones modifiedWhitespaces = modifiedWhitespaces.sort((a, b) => { return a.afterLineNumber - b.afterLineNumber; @@ -1331,7 +1331,7 @@ abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWi }; } - protected abstract _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: EditorWhitespace[], modifiedForeignVZ: EditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones; + protected abstract _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones; protected abstract _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations; protected abstract _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations; @@ -1352,10 +1352,10 @@ interface IMyViewZone { class ForeignViewZonesIterator { private _index: number; - private readonly _source: EditorWhitespace[]; - public current: EditorWhitespace | null; + private readonly _source: IEditorWhitespace[]; + public current: IEditorWhitespace | null; - constructor(source: EditorWhitespace[]) { + constructor(source: IEditorWhitespace[]) { this._source = source; this._index = -1; this.current = null; @@ -1375,13 +1375,17 @@ class ForeignViewZonesIterator { abstract class ViewZonesComputer { private readonly lineChanges: editorCommon.ILineChange[]; - private readonly originalForeignVZ: EditorWhitespace[]; - private readonly modifiedForeignVZ: EditorWhitespace[]; + private readonly originalForeignVZ: IEditorWhitespace[]; + private readonly originalLineHeight: number; + private readonly modifiedForeignVZ: IEditorWhitespace[]; + private readonly modifiedLineHeight: number; - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: EditorWhitespace[], modifiedForeignVZ: EditorWhitespace[]) { + constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], originalLineHeight: number, modifiedForeignVZ: IEditorWhitespace[], modifiedLineHeight: number) { this.lineChanges = lineChanges; this.originalForeignVZ = originalForeignVZ; + this.originalLineHeight = originalLineHeight; this.modifiedForeignVZ = modifiedForeignVZ; + this.modifiedLineHeight = modifiedLineHeight; } public getViewZones(): IEditorsZones { @@ -1456,7 +1460,7 @@ abstract class ViewZonesComputer { stepOriginal.push({ afterLineNumber: viewZoneLineNumber, - heightInLines: modifiedForeignVZ.current.heightInLines, + heightInLines: modifiedForeignVZ.current.height / this.modifiedLineHeight, domNode: null, marginDomNode: marginDomNode }); @@ -1473,7 +1477,7 @@ abstract class ViewZonesComputer { } stepModified.push({ afterLineNumber: viewZoneLineNumber, - heightInLines: originalForeignVZ.current.heightInLines, + heightInLines: originalForeignVZ.current.height / this.originalLineHeight, domNode: null }); originalForeignVZ.advance(); @@ -1731,8 +1735,8 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffE return this._dataSource.getHeight(); } - protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: EditorWhitespace[], modifiedForeignVZ: EditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones { - let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ); + protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones { + let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, originalEditor.getOption(EditorOption.lineHeight), modifiedForeignVZ, modifiedEditor.getOption(EditorOption.lineHeight)); return c.getViewZones(); } @@ -1859,8 +1863,8 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffE class SideBySideViewZonesComputer extends ViewZonesComputer { - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: EditorWhitespace[], modifiedForeignVZ: EditorWhitespace[]) { - super(lineChanges, originalForeignVZ, modifiedForeignVZ); + constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], originalLineHeight: number, modifiedForeignVZ: IEditorWhitespace[], modifiedLineHeight: number) { + super(lineChanges, originalForeignVZ, originalLineHeight, modifiedForeignVZ, modifiedLineHeight); } protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null { @@ -1911,7 +1915,7 @@ class DiffEditorWidgetInline extends DiffEditorWidgetStyle implements IDiffEdito // Nothing to do.. } - protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: EditorWhitespace[], modifiedForeignVZ: EditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones { + protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones { let computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor, renderIndicators); return computer.getViewZones(); } @@ -2019,8 +2023,8 @@ class InlineViewZonesComputer extends ViewZonesComputer { private readonly modifiedEditorTabSize: number; private readonly renderIndicators: boolean; - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: EditorWhitespace[], modifiedForeignVZ: EditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) { - super(lineChanges, originalForeignVZ, modifiedForeignVZ); + constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) { + super(lineChanges, originalForeignVZ, originalEditor.getOption(EditorOption.lineHeight), modifiedForeignVZ, modifiedEditor.getOption(EditorOption.lineHeight)); this.originalModel = originalEditor.getModel()!; this.modifiedEditorOptions = modifiedEditor.getOptions(); this.modifiedEditorTabSize = modifiedEditor.getModel()!.getOptions().tabSize; diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index 496c48d32ae..8aab4c60d90 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -7,13 +7,10 @@ import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewL import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; import * as strings from 'vs/base/common/strings'; -export class EditorWhitespace { - constructor( - public readonly id: string, - public readonly afterLineNumber: number, - public readonly heightInPx: number, - public readonly heightInLines: number, - ) { } +export interface IEditorWhitespace { + readonly id: string; + readonly afterLineNumber: number; + readonly height: number; } /** @@ -30,7 +27,7 @@ interface IPendingRemove { id: string; } class PendingChanges { private _hasPending: boolean; - private _inserts: InternalWhitespace[]; + private _inserts: EditorWhitespace[]; private _changes: IPendingChange[]; private _removes: IPendingRemove[]; @@ -41,7 +38,7 @@ class PendingChanges { this._removes = []; } - public insert(x: InternalWhitespace): void { + public insert(x: EditorWhitespace): void { this._hasPending = true; this._inserts.push(x); } @@ -78,7 +75,7 @@ class PendingChanges { } } -export class InternalWhitespace { +export class EditorWhitespace implements IEditorWhitespace { public id: string; public afterLineNumber: number; public ordinal: number; @@ -110,7 +107,7 @@ export class LinesLayout { private _pendingChanges: PendingChanges; - private readonly _arr: InternalWhitespace[]; + private readonly _arr: EditorWhitespace[]; /** * _arr[i].prefixSum, 1 <= i <= prefixSumValidIndex can be trusted @@ -158,7 +155,7 @@ export class LinesLayout { * Find the insertion index for a new value inside a sorted array of values. * If the value is already present in the sorted array, the insertion index will be after the already existing value. */ - public static findInsertionIndex(arr: InternalWhitespace[], afterLineNumber: number, ordinal: number): number { + public static findInsertionIndex(arr: EditorWhitespace[], afterLineNumber: number, ordinal: number): number { let low = 0; let high = arr.length; @@ -209,7 +206,7 @@ export class LinesLayout { minWidth = minWidth | 0; const id = this._instanceId + (++this._lastWhitespaceId); - this._pendingChanges.insert(new InternalWhitespace(id, afterLineNumber, ordinal, heightInPx, minWidth)); + this._pendingChanges.insert(new EditorWhitespace(id, afterLineNumber, ordinal, heightInPx, minWidth)); return id; // return this._insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); }, @@ -231,7 +228,7 @@ export class LinesLayout { } } - public _commitPendingChanges(inserts: InternalWhitespace[], changes: IPendingChange[], removes: IPendingRemove[]): void { + public _commitPendingChanges(inserts: EditorWhitespace[], changes: IPendingChange[], removes: IPendingRemove[]): void { // const magnitude = inserts.length + changes.length + removes.length; // if (magnitude === 1) { // // when only one thing @@ -257,13 +254,13 @@ export class LinesLayout { } } - private _insertWhitespace(whitespace: InternalWhitespace): void { + private _insertWhitespace(whitespace: EditorWhitespace): void { const insertionIndex = LinesLayout.findInsertionIndex(this._arr, whitespace.afterLineNumber, whitespace.ordinal); this._insertWhitespaceAtIndex(insertionIndex, whitespace); this._minWidth = -1; /* marker for not being computed */ } - private _insertWhitespaceAtIndex(insertIndex: number, whitespace: InternalWhitespace): void { + private _insertWhitespaceAtIndex(insertIndex: number, whitespace: EditorWhitespace): void { this._arr.splice(insertIndex, 0, whitespace); const keys = Object.keys(this._whitespaceId2Index); @@ -853,18 +850,9 @@ export class LinesLayout { /** * Get all whitespaces. */ - public getWhitespaces(): EditorWhitespace[] { + public getWhitespaces(): IEditorWhitespace[] { this._checkPendingChanges(); - let result: EditorWhitespace[] = []; - for (let i = 0; i < this._arr.length; i++) { - result.push(new EditorWhitespace( - this._arr[i].id, - this._arr[i].afterLineNumber, - this._arr[i].height, - this._arr[i].height / this._lineHeight - )); - } - return result; + return this._arr.slice(0); } /** diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index 1fbc3db5462..115e4a1c85b 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -8,7 +8,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { LinesLayout, EditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; +import { LinesLayout, IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel'; @@ -228,7 +228,7 @@ export class ViewLayout extends Disposable implements IViewLayout { const visibleBox = this.getCurrentViewport(); return this._linesLayout.getWhitespaceViewportData(visibleBox.top, visibleBox.top + visibleBox.height); } - public getWhitespaces(): EditorWhitespace[] { + public getWhitespaces(): IEditorWhitespace[] { return this._linesLayout.getWhitespaces(); } diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 3d23b97c93c..a3abd74ee4e 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -13,7 +13,7 @@ import { INewScrollPosition } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, TextModelResolvedOptions } from 'vs/editor/common/model'; import { IViewEventListener } from 'vs/editor/common/view/viewEvents'; import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import { EditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; +import { IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; import { ITheme } from 'vs/platform/theme/common/themeService'; export interface IViewWhitespaceViewportData { @@ -61,7 +61,7 @@ export interface IViewLayout { getLinesViewportData(): IPartialViewLinesViewportData; getLinesViewportDataAtScrollTop(scrollTop: number): IPartialViewLinesViewportData; - getWhitespaces(): EditorWhitespace[]; + getWhitespaces(): IEditorWhitespace[]; isAfterLines(verticalOffset: number): boolean; getLineNumberAtVerticalOffset(verticalOffset: number): number; diff --git a/src/vs/editor/test/common/viewLayout/linesLayout.test.ts b/src/vs/editor/test/common/viewLayout/linesLayout.test.ts index 57ee0db5427..b6b26b7f35a 100644 --- a/src/vs/editor/test/common/viewLayout/linesLayout.test.ts +++ b/src/vs/editor/test/common/viewLayout/linesLayout.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 { LinesLayout, InternalWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; +import { LinesLayout, EditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; suite('Editor ViewLayout - LinesLayout', () => { @@ -846,10 +846,10 @@ suite('Editor ViewLayout - LinesLayout', () => { test('LinesLayout findInsertionIndex', () => { const makeInternalWhitespace = (afterLineNumbers: number[], ordinal: number = 0) => { - return afterLineNumbers.map((afterLineNumber) => new InternalWhitespace('', afterLineNumber, ordinal, 0, 0)); + return afterLineNumbers.map((afterLineNumber) => new EditorWhitespace('', afterLineNumber, ordinal, 0, 0)); }; - let arr: InternalWhitespace[]; + let arr: EditorWhitespace[]; arr = makeInternalWhitespace([]); assert.equal(LinesLayout.findInsertionIndex(arr, 0, 0), 0); From c0032079593cf751b318a589214037a9cd9d5085 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 15 Nov 2019 16:57:21 +0100 Subject: [PATCH 84/88] Remove _whitespaceId2Index --- .../editor/common/viewLayout/linesLayout.ts | 131 ++++++++---------- 1 file changed, 60 insertions(+), 71 deletions(-) diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index 8aab4c60d90..1e60452a560 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -105,7 +105,7 @@ export class LinesLayout { private readonly _instanceId: string; - private _pendingChanges: PendingChanges; + private readonly _pendingChanges: PendingChanges; private readonly _arr: EditorWhitespace[]; @@ -114,13 +114,6 @@ export class LinesLayout { */ private _prefixSumValidIndex: number; - /** - * index at which a whitespace is positioned (inside heights, afterLineNumbers, prefixSum members) - */ - private readonly _whitespaceId2Index: { - [id: string]: number; - }; - /** * last whitespace id issued */ @@ -144,7 +137,6 @@ export class LinesLayout { this._pendingChanges = new PendingChanges(); this._arr = []; this._prefixSumValidIndex = -1; - this._whitespaceId2Index = {}; this._lastWhitespaceId = 0; this._minWidth = -1; /* marker for not being computed */ this._lineCount = lineCount; @@ -229,23 +221,34 @@ export class LinesLayout { } public _commitPendingChanges(inserts: EditorWhitespace[], changes: IPendingChange[], removes: IPendingRemove[]): void { - // const magnitude = inserts.length + changes.length + removes.length; - // if (magnitude === 1) { - // // when only one thing - for (const insert of inserts) { - this._insertWhitespace(insert); + if (!false || inserts.length + changes.length + removes.length <= 1) { + // when only one thing happened, handle it "delicately" + for (const insert of inserts) { + this._insertWhitespace(insert); + this._minWidth = -1; /* marker for not being computed */ + } + for (const change of changes) { + this._changeOneWhitespace(change.id, change.newAfterLineNumber, change.newHeight); + } + for (const remove of removes) { + this._removeWhitespace(remove.id); + } + return; } - for (const change of changes) { - this._changeOneWhitespace(change.id, change.newAfterLineNumber, change.newHeight); - } - for (const remove of removes) { - this._removeWhitespace(remove.id); - } - return; - // } - // console.log(`magnitude: ${magnitude} -- inserts: ${inserts.length}, changes: ${changes.length}, removes: ${removes.length}`); - // console.log(`TODO: `, inserts, changes, removes); + // simply rebuild the entire datastructure + + const toRemove = new Set(); + for (const remove of removes) { + toRemove.add(remove.id); + } + + const toChange = new Map(); + for (const change of changes) { + toChange.set(change.id, change); + } + + this._minWidth = -1; /* marker for not being computed */ } private _checkPendingChanges(): void { @@ -255,72 +258,58 @@ export class LinesLayout { } private _insertWhitespace(whitespace: EditorWhitespace): void { - const insertionIndex = LinesLayout.findInsertionIndex(this._arr, whitespace.afterLineNumber, whitespace.ordinal); - this._insertWhitespaceAtIndex(insertionIndex, whitespace); - this._minWidth = -1; /* marker for not being computed */ - } - - private _insertWhitespaceAtIndex(insertIndex: number, whitespace: EditorWhitespace): void { + const insertIndex = LinesLayout.findInsertionIndex(this._arr, whitespace.afterLineNumber, whitespace.ordinal); this._arr.splice(insertIndex, 0, whitespace); - - const keys = Object.keys(this._whitespaceId2Index); - for (const sid of keys) { - const oldIndex = this._whitespaceId2Index[sid]; - if (oldIndex >= insertIndex) { - this._whitespaceId2Index[sid] = oldIndex + 1; - } - } - - this._whitespaceId2Index[whitespace.id] = insertIndex; this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1); } + private _findWhitespaceIndex(id: string): number { + const arr = this._arr; + for (let i = 0, len = arr.length; i < len; i++) { + if (arr[i].id === id) { + return i; + } + } + return -1; + } + private _changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): void { - if (this._whitespaceId2Index.hasOwnProperty(id)) { - const index = this._whitespaceId2Index[id]; - if (this._arr[index].height !== newHeight) { - this._arr[index].height = newHeight; - this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1); - } - if (this._arr[index].afterLineNumber !== newAfterLineNumber) { - // `afterLineNumber` changed for this whitespace + const index = this._findWhitespaceIndex(id); + if (index === -1) { + return; + } + if (this._arr[index].height !== newHeight) { + this._arr[index].height = newHeight; + this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1); + } + if (this._arr[index].afterLineNumber !== newAfterLineNumber) { + // `afterLineNumber` changed for this whitespace - // Record old whitespace - const whitespace = this._arr[index]; + // Record old whitespace + const whitespace = this._arr[index]; - // Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace - this._removeWhitespace(id); + // Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace + this._removeWhitespaceAtIndex(index); - whitespace.afterLineNumber = newAfterLineNumber; + whitespace.afterLineNumber = newAfterLineNumber; - // And add it again - const insertionIndex = LinesLayout.findInsertionIndex(this._arr, whitespace.afterLineNumber, whitespace.ordinal); - this._insertWhitespaceAtIndex(insertionIndex, whitespace); - } + // And add it again + this._insertWhitespace(whitespace); } } private _removeWhitespace(id: string): void { - if (this._whitespaceId2Index.hasOwnProperty(id)) { - const index = this._whitespaceId2Index[id]; - delete this._whitespaceId2Index[id]; - this._removeWhitespaceAtIndex(index); - this._minWidth = -1; /* marker for not being computed */ + const index = this._findWhitespaceIndex(id); + if (index === -1) { + return; } + this._removeWhitespaceAtIndex(index); + this._minWidth = -1; /* marker for not being computed */ } private _removeWhitespaceAtIndex(removeIndex: number): void { - this._arr.splice(removeIndex, 1); this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, removeIndex - 1); - - const keys = Object.keys(this._whitespaceId2Index); - for (const sid of keys) { - const oldIndex = this._whitespaceId2Index[sid]; - if (oldIndex >= removeIndex) { - this._whitespaceId2Index[sid] = oldIndex - 1; - } - } } /** From f0ec8373f329cc96e5d90ebffa6046089598ba4d Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 15 Nov 2019 17:14:19 +0100 Subject: [PATCH 85/88] Recompute whitespace entirely in case of larger changes (#84726) --- .../editor/common/viewLayout/linesLayout.ts | 85 ++++++++++--------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/src/vs/editor/common/viewLayout/linesLayout.ts b/src/vs/editor/common/viewLayout/linesLayout.ts index 1e60452a560..90f7335de7a 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -104,40 +104,20 @@ export class LinesLayout { private static INSTANCE_COUNT = 0; private readonly _instanceId: string; - private readonly _pendingChanges: PendingChanges; - - private readonly _arr: EditorWhitespace[]; - - /** - * _arr[i].prefixSum, 1 <= i <= prefixSumValidIndex can be trusted - */ - private _prefixSumValidIndex: number; - - /** - * last whitespace id issued - */ private _lastWhitespaceId: number; - + private _arr: EditorWhitespace[]; + private _prefixSumValidIndex: number; private _minWidth: number; - - /** - * Keep track of the total number of lines. - * This is useful for doing binary searches or for doing hit-testing. - */ private _lineCount: number; - - /** - * The height of a line in pixels. - */ private _lineHeight: number; constructor(lineCount: number, lineHeight: number) { this._instanceId = strings.singleLetterHash(++LinesLayout.INSTANCE_COUNT); this._pendingChanges = new PendingChanges(); + this._lastWhitespaceId = 0; this._arr = []; this._prefixSumValidIndex = -1; - this._lastWhitespaceId = 0; this._minWidth = -1; /* marker for not being computed */ this._lineCount = lineCount; this._lineHeight = lineHeight; @@ -200,18 +180,15 @@ export class LinesLayout { const id = this._instanceId + (++this._lastWhitespaceId); this._pendingChanges.insert(new EditorWhitespace(id, afterLineNumber, ordinal, heightInPx, minWidth)); return id; - // return this._insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); }, changeOneWhitespace: (id: string, newAfterLineNumber: number, newHeight: number): void => { newAfterLineNumber = newAfterLineNumber | 0; newHeight = newHeight | 0; this._pendingChanges.change({ id, newAfterLineNumber, newHeight }); - // this._changeOneWhitespace(id, newAfterLineNumber, newHeight); }, removeWhitespace: (id: string): void => { this._pendingChanges.remove({ id }); - // this._removeWhitespace(id); } }; return callback(accessor); @@ -221,17 +198,24 @@ export class LinesLayout { } public _commitPendingChanges(inserts: EditorWhitespace[], changes: IPendingChange[], removes: IPendingRemove[]): void { - if (!false || inserts.length + changes.length + removes.length <= 1) { + if (inserts.length > 0 || removes.length > 0) { + this._minWidth = -1; /* marker for not being computed */ + } + + if (inserts.length + changes.length + removes.length <= 1) { // when only one thing happened, handle it "delicately" for (const insert of inserts) { this._insertWhitespace(insert); - this._minWidth = -1; /* marker for not being computed */ } for (const change of changes) { this._changeOneWhitespace(change.id, change.newAfterLineNumber, change.newHeight); } for (const remove of removes) { - this._removeWhitespace(remove.id); + const index = this._findWhitespaceIndex(remove.id); + if (index === -1) { + continue; + } + this._removeWhitespace(index); } return; } @@ -248,12 +232,38 @@ export class LinesLayout { toChange.set(change.id, change); } - this._minWidth = -1; /* marker for not being computed */ + const applyRemoveAndChange = (whitespaces: EditorWhitespace[]): EditorWhitespace[] => { + let result: EditorWhitespace[] = []; + for (const whitespace of whitespaces) { + if (toRemove.has(whitespace.id)) { + continue; + } + if (toChange.has(whitespace.id)) { + const change = toChange.get(whitespace.id)!; + whitespace.afterLineNumber = change.newAfterLineNumber; + whitespace.height = change.newHeight; + } + result.push(whitespace); + } + return result; + }; + + const result = applyRemoveAndChange(this._arr).concat(applyRemoveAndChange(inserts)); + result.sort((a, b) => { + if (a.afterLineNumber === b.afterLineNumber) { + return a.ordinal - b.ordinal; + } + return a.afterLineNumber - b.afterLineNumber; + }); + + this._arr = result; + this._prefixSumValidIndex = -1; } private _checkPendingChanges(): void { if (this._pendingChanges.mustCommit()) { - console.log(`I should commit pending changes!`);//TODO + console.warn(`Commiting pending changes before change accessor leaves due to read access.`); + this._pendingChanges.commit(this); } } @@ -289,7 +299,7 @@ export class LinesLayout { const whitespace = this._arr[index]; // Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace - this._removeWhitespaceAtIndex(index); + this._removeWhitespace(index); whitespace.afterLineNumber = newAfterLineNumber; @@ -298,16 +308,7 @@ export class LinesLayout { } } - private _removeWhitespace(id: string): void { - const index = this._findWhitespaceIndex(id); - if (index === -1) { - return; - } - this._removeWhitespaceAtIndex(index); - this._minWidth = -1; /* marker for not being computed */ - } - - private _removeWhitespaceAtIndex(removeIndex: number): void { + private _removeWhitespace(removeIndex: number): void { this._arr.splice(removeIndex, 1); this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, removeIndex - 1); } From da112cdb5a1c74fa8355f63cb28d077a272fe56c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 15 Nov 2019 17:27:08 +0100 Subject: [PATCH 86/88] changes for #81574 --- src/vs/base/browser/dom.ts | 2 +- src/vs/base/browser/globalMouseMoveMonitor.ts | 4 +- .../editor/browser/controller/mouseHandler.ts | 2 +- .../browser/controller/pointerHandler.ts | 10 ++-- src/vs/editor/browser/editorDom.ts | 8 +-- .../browser/viewParts/lines/viewLine.ts | 54 ++++++++++--------- src/vs/editor/contrib/dnd/dnd.ts | 6 +-- .../standalone/browser/standaloneServices.ts | 2 +- 8 files changed, 47 insertions(+), 41 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index cbdf3775398..6a912ed7559 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -443,7 +443,7 @@ export interface DOMEvent { } const MINIMUM_TIME_MS = 16; -const DEFAULT_EVENT_MERGER: IEventMerger = function (lastEvent: DOMEvent, currentEvent: DOMEvent) { +const DEFAULT_EVENT_MERGER: IEventMerger = function (lastEvent: DOMEvent | null, currentEvent: DOMEvent) { return currentEvent; }; diff --git a/src/vs/base/browser/globalMouseMoveMonitor.ts b/src/vs/base/browser/globalMouseMoveMonitor.ts index b24a3db0e50..4ab9a6c2d33 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -16,7 +16,7 @@ export interface IStandardMouseMoveEventData { } export interface IEventMerger { - (lastEvent: R, currentEvent: MouseEvent): R; + (lastEvent: R | null, currentEvent: MouseEvent): R; } export interface IMouseMoveCallback { @@ -90,7 +90,7 @@ export class GlobalMouseMoveMonitor implements IDisposable { for (const element of windowChain) { this.hooks.add(dom.addDisposableThrottledListener(element.window.document, mouseMove, (data: R) => this.mouseMoveCallback!(data), - (lastEvent: R, currentEvent) => this.mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent) + (lastEvent: R | null, currentEvent) => this.mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent) )); this.hooks.add(dom.addDisposableListener(element.window.document, mouseUp, (e: MouseEvent) => this.stopMonitoring(true))); } diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 8c8bb743462..d235e6ba791 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -27,7 +27,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; * Merges mouse events when mouse move events are throttled */ export function createMouseMoveEventMerger(mouseTargetFactory: MouseTargetFactory | null) { - return function (lastEvent: EditorMouseEvent, currentEvent: EditorMouseEvent): EditorMouseEvent { + return function (lastEvent: EditorMouseEvent | null, currentEvent: EditorMouseEvent): EditorMouseEvent { let targetIsWidget = false; if (mouseTargetFactory) { targetIsWidget = mouseTargetFactory.mouseTargetIsWidget(currentEvent); diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 28ebcf990af..8818742feff 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -18,7 +18,7 @@ interface IThrottledGestureEvent { translationY: number; } -function gestureChangeEventMerger(lastEvent: IThrottledGestureEvent, currentEvent: MSGestureEvent): IThrottledGestureEvent { +function gestureChangeEventMerger(lastEvent: IThrottledGestureEvent | null, currentEvent: MSGestureEvent): IThrottledGestureEvent { const r = { translationY: currentEvent.translationY, translationX: currentEvent.translationX @@ -53,7 +53,7 @@ class MsPointerHandler extends MouseHandler implements IDisposable { const penGesture = new MSGesture(); touchGesture.target = this.viewHelper.linesContentDomNode; penGesture.target = this.viewHelper.linesContentDomNode; - this.viewHelper.linesContentDomNode.addEventListener('MSPointerDown', (e: MSPointerEvent) => { + this.viewHelper.linesContentDomNode.addEventListener('MSPointerDown', (e: MSPointerEvent) => { // Circumvent IE11 breaking change in e.pointerType & TypeScript's stale definitions const pointerType = e.pointerType; if (pointerType === ((e).MSPOINTER_TYPE_MOUSE || 'mouse')) { @@ -67,7 +67,7 @@ class MsPointerHandler extends MouseHandler implements IDisposable { penGesture.addPointer(e.pointerId); } }); - this._register(dom.addDisposableThrottledListener(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger)); + this._register(dom.addDisposableThrottledListener(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger)); this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'MSGestureTap', (e) => this._onCaptureGestureTap(e), true)); } }, 100); @@ -132,7 +132,7 @@ class StandardPointerHandler extends MouseHandler implements IDisposable { const penGesture = new MSGesture(); touchGesture.target = this.viewHelper.linesContentDomNode; penGesture.target = this.viewHelper.linesContentDomNode; - this.viewHelper.linesContentDomNode.addEventListener('pointerdown', (e: MSPointerEvent) => { + this.viewHelper.linesContentDomNode.addEventListener('pointerdown', (e: PointerEvent) => { const pointerType = e.pointerType; if (pointerType === 'mouse') { this._lastPointerType = 'mouse'; @@ -145,7 +145,7 @@ class StandardPointerHandler extends MouseHandler implements IDisposable { penGesture.addPointer(e.pointerId); } }); - this._register(dom.addDisposableThrottledListener(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger)); + this._register(dom.addDisposableThrottledListener(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger)); this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'MSGestureTap', (e) => this._onCaptureGestureTap(e), true)); } }, 100); diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 60df7e8f1b8..9401fdd259e 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -84,7 +84,7 @@ export class EditorMouseEvent extends StandardMouseEvent { } export interface EditorMouseEventMerger { - (lastEvent: EditorMouseEvent, currentEvent: EditorMouseEvent): EditorMouseEvent; + (lastEvent: EditorMouseEvent | null, currentEvent: EditorMouseEvent): EditorMouseEvent; } export class EditorMouseEventFactory { @@ -124,7 +124,7 @@ export class EditorMouseEventFactory { } public onMouseMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { - const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => { + const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { return merger(lastEvent, this._create(currentEvent)); }; return dom.addDisposableThrottledListener(target, 'mousemove', callback, myMerger, minimumTimeMs); @@ -162,7 +162,7 @@ export class EditorPointerEventFactory { } public onPointerMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { - const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => { + const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { return merger(lastEvent, this._create(currentEvent)); }; return dom.addDisposableThrottledListener(target, 'pointermove', callback, myMerger, minimumTimeMs); @@ -195,7 +195,7 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { this._globalMouseMoveMonitor.stopMonitoring(true); }, true); - const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => { + const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { return merger(lastEvent, new EditorMouseEvent(currentEvent, this._editorViewDomNode)); }; diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 655b93462d3..8840a014032 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -407,7 +407,7 @@ class FastRenderedViewLine implements IRenderedViewLine { */ class RenderedViewLine implements IRenderedViewLine { - public domNode: FastDomNode; + public domNode: FastDomNode | null; public readonly input: RenderLineInput; protected readonly _characterMapping: CharacterMapping; @@ -420,7 +420,7 @@ class RenderedViewLine implements IRenderedViewLine { */ private readonly _pixelOffsetCache: Int32Array | null; - constructor(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType) { + constructor(domNode: FastDomNode | null, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType) { this.domNode = domNode; this.input = renderLineInput; this._characterMapping = characterMapping; @@ -439,16 +439,19 @@ class RenderedViewLine implements IRenderedViewLine { // --- Reading from the DOM methods - protected _getReadingTarget(): HTMLElement { - return this.domNode.domNode.firstChild; + protected _getReadingTarget(myDomNode: FastDomNode): HTMLElement { + return myDomNode.domNode.firstChild; } /** * Width of the line in pixels */ public getWidth(): number { + if (!this.domNode) { + return 0; + } if (this._cachedWidth === -1) { - this._cachedWidth = this._getReadingTarget().offsetWidth; + this._cachedWidth = this._getReadingTarget(this.domNode).offsetWidth; } return this._cachedWidth; } @@ -464,14 +467,17 @@ class RenderedViewLine implements IRenderedViewLine { * Visible ranges for a model range */ public getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { + if (!this.domNode) { + return null; + } if (this._pixelOffsetCache !== null) { // the text is LTR - const startOffset = this._readPixelOffset(startColumn, context); + const startOffset = this._readPixelOffset(this.domNode, startColumn, context); if (startOffset === -1) { return null; } - const endOffset = this._readPixelOffset(endColumn, context); + const endOffset = this._readPixelOffset(this.domNode, endColumn, context); if (endOffset === -1) { return null; } @@ -479,23 +485,23 @@ class RenderedViewLine implements IRenderedViewLine { return [new HorizontalRange(startOffset, endOffset - startOffset)]; } - return this._readVisibleRangesForRange(startColumn, endColumn, context); + return this._readVisibleRangesForRange(this.domNode, startColumn, endColumn, context); } - protected _readVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { + protected _readVisibleRangesForRange(domNode: FastDomNode, startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { if (startColumn === endColumn) { - const pixelOffset = this._readPixelOffset(startColumn, context); + const pixelOffset = this._readPixelOffset(domNode, startColumn, context); if (pixelOffset === -1) { return null; } else { return [new HorizontalRange(pixelOffset, 0)]; } } else { - return this._readRawVisibleRangesForRange(startColumn, endColumn, context); + return this._readRawVisibleRangesForRange(domNode, startColumn, endColumn, context); } } - protected _readPixelOffset(column: number, context: DomReadingContext): number { + protected _readPixelOffset(domNode: FastDomNode, column: number, context: DomReadingContext): number { if (this._characterMapping.length === 0) { // This line has no content if (this._containsForeignElements === ForeignElementType.None) { @@ -520,18 +526,18 @@ class RenderedViewLine implements IRenderedViewLine { return cachedPixelOffset; } - const result = this._actualReadPixelOffset(column, context); + const result = this._actualReadPixelOffset(domNode, column, context); this._pixelOffsetCache[column] = result; return result; } - return this._actualReadPixelOffset(column, context); + return this._actualReadPixelOffset(domNode, column, context); } - private _actualReadPixelOffset(column: number, context: DomReadingContext): number { + private _actualReadPixelOffset(domNode: FastDomNode, column: number, context: DomReadingContext): number { if (this._characterMapping.length === 0) { // This line has no content - const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(), 0, 0, 0, 0, context.clientRectDeltaLeft, context.endNode); + const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), 0, 0, 0, 0, context.clientRectDeltaLeft, context.endNode); if (!r || r.length === 0) { return -1; } @@ -547,14 +553,14 @@ class RenderedViewLine implements IRenderedViewLine { const partIndex = CharacterMapping.getPartIndex(partData); const charOffsetInPart = CharacterMapping.getCharIndex(partData); - const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(), partIndex, charOffsetInPart, partIndex, charOffsetInPart, context.clientRectDeltaLeft, context.endNode); + const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), partIndex, charOffsetInPart, partIndex, charOffsetInPart, context.clientRectDeltaLeft, context.endNode); if (!r || r.length === 0) { return -1; } return r[0].left; } - private _readRawVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { + private _readRawVisibleRangesForRange(domNode: FastDomNode, startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { if (startColumn === 1 && endColumn === this._characterMapping.length) { // This branch helps IE with bidi text & gives a performance boost to other browsers when reading visible ranges for an entire line @@ -570,7 +576,7 @@ class RenderedViewLine implements IRenderedViewLine { const endPartIndex = CharacterMapping.getPartIndex(endPartData); const endCharOffsetInPart = CharacterMapping.getCharIndex(endPartData); - return RangeUtil.readHorizontalRanges(this._getReadingTarget(), startPartIndex, startCharOffsetInPart, endPartIndex, endCharOffsetInPart, context.clientRectDeltaLeft, context.endNode); + return RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), startPartIndex, startCharOffsetInPart, endPartIndex, endCharOffsetInPart, context.clientRectDeltaLeft, context.endNode); } /** @@ -591,8 +597,8 @@ class RenderedViewLine implements IRenderedViewLine { } class WebKitRenderedViewLine extends RenderedViewLine { - protected _readVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { - const output = super._readVisibleRangesForRange(startColumn, endColumn, context); + protected _readVisibleRangesForRange(domNode: FastDomNode, startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { + const output = super._readVisibleRangesForRange(domNode, startColumn, endColumn, context); if (!output || output.length === 0 || startColumn === endColumn || (startColumn === 1 && endColumn === this._characterMapping.length)) { return output; @@ -603,7 +609,7 @@ class WebKitRenderedViewLine extends RenderedViewLine { if (!this.input.containsRTL) { // This is an attempt to patch things up // Find position of last column - const endPixelOffset = this._readPixelOffset(endColumn, context); + const endPixelOffset = this._readPixelOffset(domNode, endColumn, context); if (endPixelOffset !== -1) { const lastRange = output[output.length - 1]; if (lastRange.left < endPixelOffset) { @@ -624,10 +630,10 @@ const createRenderedLine: (domNode: FastDomNode | null, renderLineI return createNormalRenderedLine; })(); -function createWebKitRenderedLine(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine { +function createWebKitRenderedLine(domNode: FastDomNode | null, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine { return new WebKitRenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements); } -function createNormalRenderedLine(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine { +function createNormalRenderedLine(domNode: FastDomNode | null, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine { return new RenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements); } diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index f9c0cd00902..6d4226cbe5e 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -8,7 +8,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Disposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ICodeEditor, IEditorMouseEvent, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IEditorMouseEvent, IMouseTarget, MouseTargetType, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { Position } from 'vs/editor/common/core/position'; @@ -50,7 +50,7 @@ export class DragAndDropController extends Disposable implements editorCommon.IE this._register(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e))); this._register(this._editor.onMouseDrag((e: IEditorMouseEvent) => this._onEditorMouseDrag(e))); - this._register(this._editor.onMouseDrop((e: IEditorMouseEvent) => this._onEditorMouseDrop(e))); + this._register(this._editor.onMouseDrop((e: IPartialEditorMouseEvent) => this._onEditorMouseDrop(e))); this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(e))); this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e))); this._register(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur())); @@ -143,7 +143,7 @@ export class DragAndDropController extends Disposable implements editorCommon.IE } } - private _onEditorMouseDrop(mouseEvent: IEditorMouseEvent): void { + private _onEditorMouseDrop(mouseEvent: IPartialEditorMouseEvent): void { if (mouseEvent.target && (this._hitContent(mouseEvent.target) || this._hitMargin(mouseEvent.target)) && mouseEvent.target.position) { let newCursorPosition = new Position(mouseEvent.target.position.lineNumber, mouseEvent.target.position.column); diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 1777683f5cc..b9abf518d33 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -89,7 +89,7 @@ export module StaticServices { let _all: LazyStaticService[] = []; - function define(serviceId: ServiceIdentifier, factory: (overrides: IEditorOverrideServices) => T): LazyStaticService { + function define(serviceId: ServiceIdentifier, factory: (overrides: IEditorOverrideServices | undefined) => T): LazyStaticService { let r = new LazyStaticService(serviceId, factory); _all.push(r); return r; From 44e755ba4b0dbd11d2910311dafc355a8b1430cd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Nov 2019 17:34:24 +0100 Subject: [PATCH 87/88] custom editors - support simple save() --- .../contrib/files/browser/fileCommands.ts | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 63059309699..1a4031d3849 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -496,9 +496,30 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.KEY_S, id: SAVE_FILE_COMMAND_ID, - handler: (accessor, resource: URI | object) => { + handler: async (accessor, resource: URI | object) => { + const listService = accessor.get(IListService); + const editorGroupsService = accessor.get(IEditorGroupsService); + const notificationService = accessor.get(INotificationService); + + const editors = getMultiSelectedEditors(listService, editorGroupsService); + if (editors.length && !editors.some(({ editor }) => editor.getResource()?.scheme === Schemas.untitled)) { + try { + await Promise.all(editors.map(async ({ groupId, editor }) => { + + // Use save as a hint to pin the editor + editorGroupsService.getGroup(groupId)?.pinEditor(editor); + + return editor.save({ force: true }); + })); + } catch (error) { + notificationService.error(nls.localize('genericRevertResourcesError', "Failed to revert '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false))); + } + + return; + } + const editorService = accessor.get(IEditorService); - const resources = getMultiSelectedResources(resource, accessor.get(IListService), editorService); + const resources = getMultiSelectedResources(resource, listService, editorService); if (resources.length === 1) { // If only one resource is selected explictly call save since the behavior is a bit different than save all #41841 From ca81ca47db3ff6e7f39fb514b52797937266bc22 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 15 Nov 2019 18:02:15 +0100 Subject: [PATCH 88/88] More changes for #81574 --- src/vs/base/browser/globalMouseMoveMonitor.ts | 2 +- src/vs/editor/browser/editorExtensions.ts | 4 ++-- src/vs/platform/remote/common/remoteAgentConnection.ts | 4 ++-- src/vs/workbench/api/common/extHost.api.impl.ts | 3 +-- .../browser/suggestEnabledInput/suggestEnabledInput.ts | 2 +- .../extensions/common/extensionHostProcessManager.ts | 5 ++++- .../workbench/services/mode/common/workbenchModeService.ts | 2 +- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/vs/base/browser/globalMouseMoveMonitor.ts b/src/vs/base/browser/globalMouseMoveMonitor.ts index 4ab9a6c2d33..cc8474f39ee 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -27,7 +27,7 @@ export interface IOnStopCallback { (): void; } -export function standardMouseMoveMerger(lastEvent: IStandardMouseMoveEventData, currentEvent: MouseEvent): IStandardMouseMoveEventData { +export function standardMouseMoveMerger(lastEvent: IStandardMouseMoveEventData | null, currentEvent: MouseEvent): IStandardMouseMoveEventData { let ev = new StandardMouseEvent(currentEvent); ev.preventDefault(); return { diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 1b81608b013..2a12d008b5c 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -307,7 +307,7 @@ export function registerEditorContribution(id EditorContributionRegistry.INSTANCE.registerEditorContribution(id, ctor); } -export function registerDiffEditorContribution(id: string, ctor: IDiffEditorContributionCtor): void { +export function registerDiffEditorContribution(id: string, ctor: { new(editor: IDiffEditor, ...services: Services): IEditorContribution }): void { EditorContributionRegistry.INSTANCE.registerDiffEditorContribution(id, ctor); } @@ -363,7 +363,7 @@ class EditorContributionRegistry { return this.editorContributions.slice(0); } - public registerDiffEditorContribution(id: string, ctor: IDiffEditorContributionCtor): void { + public registerDiffEditorContribution(id: string, ctor: { new(editor: IDiffEditor, ...services: Services): IEditorContribution }): void { this.diffEditorContributions.push({ id, ctor }); } diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts index b526d149fa3..eab85914921 100644 --- a/src/vs/platform/remote/common/remoteAgentConnection.ts +++ b/src/vs/platform/remote/common/remoteAgentConnection.ts @@ -89,8 +89,8 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio options.host, options.port, `reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, - (err: any, socket: ISocket) => { - if (err) { + (err: any, socket: ISocket | undefined) => { + if (err || !socket) { options.logService.error(`${logPrefix} socketFactory.connect() failed. Error:`); options.logService.error(err); e(err); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 448923b1044..e71a93d69e4 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -185,8 +185,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } return activeTextEditor.edit((edit: vscode.TextEditorEdit) => { - args.unshift(activeTextEditor, edit); - callback.apply(thisArg, args); + callback.apply(thisArg, [activeTextEditor, edit, ...args]); }).then((result) => { if (!result) { diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts index 223949c9ac6..b7f1355fdad 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts @@ -82,7 +82,7 @@ export interface ISuggestEnabledInputStyleOverrides extends IStyleOverrides { } type ISuggestEnabledInputStyles = { - [P in keyof ISuggestEnabledInputStyleOverrides]: Color; + [P in keyof ISuggestEnabledInputStyleOverrides]: Color | undefined; }; export function attachSuggestEnabledInputBoxStyler(widget: IThemable, themeService: IThemeService, style?: ISuggestEnabledInputStyleOverrides): IDisposable { diff --git a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts index cb638f64361..f3b6c2bf21d 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts @@ -406,7 +406,10 @@ export class MeasureExtHostLatencyAction extends Action { this._editorService.openEditor({ contents: measurements.map(MeasureExtHostLatencyAction._print).join('\n\n'), options: { pinned: true } } as IUntitledTextResourceInput); } - private static _print(m: ExtHostLatencyResult): string { + private static _print(m: ExtHostLatencyResult | null): string { + if (!m) { + return ''; + } return `${m.remoteAuthority ? `Authority: ${m.remoteAuthority}\n` : ``}Roundtrip latency: ${m.latency.toFixed(3)}ms\nUp: ${MeasureExtHostLatencyAction._printSpeed(m.up)}\nDown: ${MeasureExtHostLatencyAction._printSpeed(m.down)}\n`; } diff --git a/src/vs/workbench/services/mode/common/workbenchModeService.ts b/src/vs/workbench/services/mode/common/workbenchModeService.ts index f097879bc0d..c6033d4ef98 100644 --- a/src/vs/workbench/services/mode/common/workbenchModeService.ts +++ b/src/vs/workbench/services/mode/common/workbenchModeService.ts @@ -105,7 +105,7 @@ export class WorkbenchModeServiceImpl extends ModeServiceImpl { this._configurationService = configurationService; this._extensionService = extensionService; - languagesExtPoint.setHandler((extensions: IExtensionPointUser[]) => { + languagesExtPoint.setHandler((extensions: readonly IExtensionPointUser[]) => { let allValidLanguages: ILanguageExtensionPoint[] = []; for (let i = 0, len = extensions.length; i < len; i++) {