From 5d001b842b693d7dcb931f9b6518a9e5830f8eaf Mon Sep 17 00:00:00 2001 From: Dave Alongi Date: Fri, 15 Mar 2019 13:08:05 -0700 Subject: [PATCH 01/48] Extension URI Handlers: "Don't ask me again" checkbox when confirming --- .../inactiveExtensionUrlHandler.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts index 2713af6dad8..b44c3ddc468 100644 --- a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts @@ -22,6 +22,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; const FIVE_MINUTES = 5 * 60 * 1000; const THIRTY_SECONDS = 30 * 1000; const URL_TO_HANDLE = 'extensionUrlHandler.urlToHandle'; +const CONFIRMED_EXTENSIONS_STORAGE_KEY = 'extensionUrlHandler.confirmedExtensions'; function isExtensionId(value: string): boolean { return /^[a-z0-9][a-z0-9\-]*\.[a-z0-9][a-z0-9\-]*$/i.test(value); @@ -90,6 +91,9 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { return true; } + const confirmedExtensionIds = this.getConfirmedExtensionIds(); + confirmed = confirmed || confirmedExtensionIds.indexOf(ExtensionIdentifier.toKey(extensionId)) >= 0; + if (!confirmed) { let uriString = uri.toString(); @@ -99,6 +103,9 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { const result = await this.dialogService.confirm({ message: localize('confirmUrl', "Allow an extension to open this URL?", extensionId), + checkbox: { + label: localize('rememberConfirmUrl', "Don't ask again for this extension."), + }, detail: `${extension.displayName || extension.name} (${extensionId}) wants to open a URL:\n\n${uriString}`, primaryButton: localize('open', "&&Open"), type: 'question' @@ -107,6 +114,14 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { if (!result.confirmed) { return true; } + + if (result.checkboxChecked) { + this.storageService.store( + CONFIRMED_EXTENSIONS_STORAGE_KEY, + JSON.stringify([...confirmedExtensionIds, ExtensionIdentifier.toKey(extensionId)]), + StorageScope.GLOBAL, + ); + } } const handler = this.extensionHandlers.get(ExtensionIdentifier.toKey(extensionId)); @@ -153,6 +168,15 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { this.extensionHandlers.delete(ExtensionIdentifier.toKey(extensionId)); } + private getConfirmedExtensionIds(): Array { + const ignoredExtensions = this.storageService.get(CONFIRMED_EXTENSIONS_STORAGE_KEY, StorageScope.GLOBAL, '[]'); + try { + return JSON.parse(ignoredExtensions); + } catch (err) { + return []; + } + } + private async handleUnhandledURL(uri: URI, extensionIdentifier: IExtensionIdentifier): Promise { const installedExtensions = await this.extensionManagementService.getInstalled(); const extension = installedExtensions.filter(e => areSameExtensions(e.identifier, extensionIdentifier))[0]; From 8d1eb98d69cad195b70c48d2d4a0b10829d36955 Mon Sep 17 00:00:00 2001 From: Dave Alongi Date: Wed, 20 Mar 2019 10:28:34 -0700 Subject: [PATCH 02/48] Additionally honor a configuration setting --- .../extensions.contribution.ts | 5 ++ .../common/inactiveExtensionUrlHandler.ts | 67 ++++++++++++++----- 2 files changed, 56 insertions(+), 16 deletions(-) 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 73d6f8953f0..19efc2fd429 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -240,6 +240,11 @@ Registry.as(ConfigurationExtensions.Configuration) type: 'boolean', description: localize('extensionsCloseExtensionDetailsOnViewChange', "When enabled, editors with extension details will be automatically closed upon navigating away from the Extensions View."), default: false + }, + 'extensions.confirmedUriHandlerExtensionIds': { + type: 'array', + description: localize('handleUriConfirmedExtensions', "When extensions are listed here, a confirmation prompt will not be required before that extension can handle a URI."), + default: [] } } }); diff --git a/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts b/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts index ddb7f8e90e8..edba464569d 100644 --- a/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts @@ -7,6 +7,7 @@ import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { EnablementState, IExtensionEnablementService, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -22,6 +23,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; const FIVE_MINUTES = 5 * 60 * 1000; const THIRTY_SECONDS = 30 * 1000; const URL_TO_HANDLE = 'extensionUrlHandler.urlToHandle'; +const CONFIRMED_EXTENSIONS_CONFIGURATION_KEY = 'extensions.confirmedUriHandlerExtensionIds'; const CONFIRMED_EXTENSIONS_STORAGE_KEY = 'extensionUrlHandler.confirmedExtensions'; function isExtensionId(value: string): boolean { @@ -62,7 +64,9 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { @IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService, @IWindowService private readonly windowService: IWindowService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, - @IStorageService private readonly storageService: IStorageService + @IStorageService private readonly storageService: IStorageService, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { const interval = setInterval(() => this.garbageCollect(), THIRTY_SECONDS); const urlToHandleValue = this.storageService.get(URL_TO_HANDLE, StorageScope.WORKSPACE); @@ -92,7 +96,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } const confirmedExtensionIds = this.getConfirmedExtensionIds(); - confirmed = confirmed || confirmedExtensionIds.indexOf(ExtensionIdentifier.toKey(extensionId)) >= 0; + confirmed = confirmed || confirmedExtensionIds.has(ExtensionIdentifier.toKey(extensionId)); if (!confirmed) { let uriString = uri.toString(); @@ -116,11 +120,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } if (result.checkboxChecked) { - this.storageService.store( - CONFIRMED_EXTENSIONS_STORAGE_KEY, - JSON.stringify([...confirmedExtensionIds, ExtensionIdentifier.toKey(extensionId)]), - StorageScope.GLOBAL, - ); + this.addConfirmedExtensionIdToStorage(extensionId); } } @@ -168,15 +168,6 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { this.extensionHandlers.delete(ExtensionIdentifier.toKey(extensionId)); } - private getConfirmedExtensionIds(): Array { - const ignoredExtensions = this.storageService.get(CONFIRMED_EXTENSIONS_STORAGE_KEY, StorageScope.GLOBAL, '[]'); - try { - return JSON.parse(ignoredExtensions); - } catch (err) { - return []; - } - } - private async handleUnhandledURL(uri: URI, extensionIdentifier: IExtensionIdentifier): Promise { const installedExtensions = await this.extensionManagementService.getInstalled(); const extension = installedExtensions.filter(e => areSameExtensions(e.identifier, extensionIdentifier))[0]; @@ -290,6 +281,50 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { this.uriBuffer = uriBuffer; } + private getConfirmedExtensionIds(): Set { + return new Set([ + ...this.getConfirmedExtensionIdsFromStorage(), + ...this.getConfirmedExtensionIdsFromConfiguration(), + ].map( + extensionId => ExtensionIdentifier.toKey(extensionId) + )); + } + + private getConfirmedExtensionIdsFromConfiguration(): Array { + const confirmedExtensionIds = this.configurationService.getValue>( + CONFIRMED_EXTENSIONS_CONFIGURATION_KEY + ); + if (!Array.isArray(confirmedExtensionIds)) { + return []; + } + + return confirmedExtensionIds; + } + + private getConfirmedExtensionIdsFromStorage(): Array { + const confirmedExtensionIdsJson = this.storageService.get( + CONFIRMED_EXTENSIONS_STORAGE_KEY, + StorageScope.GLOBAL, + '[]', + ); + try { + return JSON.parse(confirmedExtensionIdsJson); + } catch (err) { + return []; + } + } + private addConfirmedExtensionIdToStorage(extensionId: string): void { + const existingConfirmedExtensionIds = this.getConfirmedExtensionIdsFromStorage(); + this.storageService.store( + CONFIRMED_EXTENSIONS_STORAGE_KEY, + JSON.stringify([ + ...existingConfirmedExtensionIds, + ExtensionIdentifier.toKey(extensionId), + ]), + StorageScope.GLOBAL, + ); + } + dispose(): void { this.disposable.dispose(); this.extensionHandlers.clear(); From 6ae15bb89542dcce780536b4ab865445a93f1a06 Mon Sep 17 00:00:00 2001 From: Dave Alongi Date: Wed, 20 Mar 2019 10:40:20 -0700 Subject: [PATCH 03/48] Update extensions.contribution.ts Improve description of configuration --- .../extensions/electron-browser/extensions.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 19efc2fd429..dc59610c50c 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -243,7 +243,7 @@ Registry.as(ConfigurationExtensions.Configuration) }, 'extensions.confirmedUriHandlerExtensionIds': { type: 'array', - description: localize('handleUriConfirmedExtensions', "When extensions are listed here, a confirmation prompt will not be required before that extension can handle a URI."), + description: localize('handleUriConfirmedExtensions', "When an extension is listed here, a confirmation prompt will not be shown when that extension handles a URI."), default: [] } } From 474b58464c296086048ba6a9ce2e2bda94fe9f7d Mon Sep 17 00:00:00 2001 From: Dave Alongi Date: Wed, 17 Apr 2019 07:39:51 -0700 Subject: [PATCH 04/48] Fix formatting that was broken during github.com conflict resolution --- .../extensions/electron-browser/extensions.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1a852603229..83a97292973 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -246,7 +246,7 @@ Registry.as(ConfigurationExtensions.Configuration) type: 'array', description: localize('handleUriConfirmedExtensions', "When an extension is listed here, a confirmation prompt will not be shown when that extension handles a URI."), default: [] - }, + }, 'extensions.extensionKind': { type: 'object', description: localize('extensions.extensionKind', "Configure ui or workspace extensions and allow them to enable locally or remotely in a remote window."), From e9dd4687435751144ca5c6139882f07dc3218ef3 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Mon, 22 Jul 2019 09:45:49 -0500 Subject: [PATCH 05/48] Add proposed DiagnosticTag.Deprecated enum member --- src/vs/vscode.proposed.d.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 5bca4d0d507..6a31141bb94 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1274,4 +1274,18 @@ declare module 'vscode' { } //#endregion + + //#region Deprecated support + + export enum DiagnosticTag { + /** + * Deprecated or obsolete code + * + * Can be used to style with strikeout or other "obsolete" styling. See: + * https://github.com/microsoft/vscode/issues/50972 + */ + Deprecated = 2, + } + + //#endregion } From f88f79b6e06040c45628551ea977f78269435228 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Mon, 22 Jul 2019 11:34:27 -0500 Subject: [PATCH 06/48] Add Deprecated enum member to extHostTypes --- src/vs/workbench/api/common/extHostTypes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index b8aebf92b45..de8d3eec8b3 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -773,6 +773,7 @@ export class SnippetString { export enum DiagnosticTag { Unnecessary = 1, + Deprecated = 2 } export enum DiagnosticSeverity { From 6b3ae43691a3729e4a988d1182abe6b7aba2f045 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Mon, 22 Jul 2019 11:52:48 -0500 Subject: [PATCH 07/48] Add Deprecated to MarkerTag --- src/vs/editor/common/standalone/standaloneEnums.ts | 3 ++- src/vs/monaco.d.ts | 3 ++- src/vs/platform/markers/common/markers.ts | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index df80861b2a8..46d295d2398 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -7,7 +7,8 @@ export enum MarkerTag { - Unnecessary = 1 + Unnecessary = 1, + Deprecated = 2 } export enum MarkerSeverity { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 945d10d7e14..982a16b87ef 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -27,7 +27,8 @@ declare namespace monaco { export enum MarkerTag { - Unnecessary = 1 + Unnecessary = 1, + Deprecated = 2 } export enum MarkerSeverity { diff --git a/src/vs/platform/markers/common/markers.ts b/src/vs/platform/markers/common/markers.ts index 9461b506c21..235c4b12f36 100644 --- a/src/vs/platform/markers/common/markers.ts +++ b/src/vs/platform/markers/common/markers.ts @@ -39,6 +39,7 @@ export interface IRelatedInformation { export const enum MarkerTag { Unnecessary = 1, + Deprecated = 2 } export enum MarkerSeverity { From 36c9106350a290fb0d128f6a3f24cedcbb9fcf63 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Mon, 22 Jul 2019 11:59:00 -0500 Subject: [PATCH 08/48] Add DiagnosticTag case for MarkerTag.Deprecated --- src/vs/workbench/api/common/extHostTypeConverters.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 80fef1bdda0..bc2951b6220 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -108,6 +108,8 @@ export namespace DiagnosticTag { switch (value) { case types.DiagnosticTag.Unnecessary: return MarkerTag.Unnecessary; + case types.DiagnosticTag.Deprecated: + return MarkerTag.Deprecated; } return undefined; } From f706130a5bfe3e4004c48198ca3db4800440356c Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Tue, 23 Jul 2019 09:08:56 -0500 Subject: [PATCH 09/48] Revert "Add DiagnosticTag case for MarkerTag.Deprecated" This reverts commit 36c9106350a290fb0d128f6a3f24cedcbb9fcf63. --- src/vs/workbench/api/common/extHostTypeConverters.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index bc2951b6220..80fef1bdda0 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -108,8 +108,6 @@ export namespace DiagnosticTag { switch (value) { case types.DiagnosticTag.Unnecessary: return MarkerTag.Unnecessary; - case types.DiagnosticTag.Deprecated: - return MarkerTag.Deprecated; } return undefined; } From 64982b604fc74d841abb737bd0e2f196aea51df4 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Tue, 23 Jul 2019 09:09:06 -0500 Subject: [PATCH 10/48] Revert "Add Deprecated to MarkerTag" This reverts commit 6b3ae43691a3729e4a988d1182abe6b7aba2f045. --- src/vs/editor/common/standalone/standaloneEnums.ts | 3 +-- src/vs/monaco.d.ts | 3 +-- src/vs/platform/markers/common/markers.ts | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 46d295d2398..df80861b2a8 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -7,8 +7,7 @@ export enum MarkerTag { - Unnecessary = 1, - Deprecated = 2 + Unnecessary = 1 } export enum MarkerSeverity { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 982a16b87ef..945d10d7e14 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -27,8 +27,7 @@ declare namespace monaco { export enum MarkerTag { - Unnecessary = 1, - Deprecated = 2 + Unnecessary = 1 } export enum MarkerSeverity { diff --git a/src/vs/platform/markers/common/markers.ts b/src/vs/platform/markers/common/markers.ts index 235c4b12f36..9461b506c21 100644 --- a/src/vs/platform/markers/common/markers.ts +++ b/src/vs/platform/markers/common/markers.ts @@ -39,7 +39,6 @@ export interface IRelatedInformation { export const enum MarkerTag { Unnecessary = 1, - Deprecated = 2 } export enum MarkerSeverity { From b4edff8f5b01013600241d22536ba305100dfb1e Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Tue, 23 Jul 2019 10:23:46 -0500 Subject: [PATCH 11/48] Revert "Revert "Add Deprecated to MarkerTag"" This reverts commit 64982b604fc74d841abb737bd0e2f196aea51df4. --- src/vs/editor/common/standalone/standaloneEnums.ts | 3 ++- src/vs/monaco.d.ts | 3 ++- src/vs/platform/markers/common/markers.ts | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index df80861b2a8..46d295d2398 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -7,7 +7,8 @@ export enum MarkerTag { - Unnecessary = 1 + Unnecessary = 1, + Deprecated = 2 } export enum MarkerSeverity { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 945d10d7e14..982a16b87ef 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -27,7 +27,8 @@ declare namespace monaco { export enum MarkerTag { - Unnecessary = 1 + Unnecessary = 1, + Deprecated = 2 } export enum MarkerSeverity { diff --git a/src/vs/platform/markers/common/markers.ts b/src/vs/platform/markers/common/markers.ts index 9461b506c21..235c4b12f36 100644 --- a/src/vs/platform/markers/common/markers.ts +++ b/src/vs/platform/markers/common/markers.ts @@ -39,6 +39,7 @@ export interface IRelatedInformation { export const enum MarkerTag { Unnecessary = 1, + Deprecated = 2 } export enum MarkerSeverity { From 1762f3f713a34e7ebc61e58188adc5917bb5d159 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Tue, 23 Jul 2019 10:24:00 -0500 Subject: [PATCH 12/48] Revert "Revert "Add DiagnosticTag case for MarkerTag.Deprecated"" This reverts commit f706130a5bfe3e4004c48198ca3db4800440356c. --- src/vs/workbench/api/common/extHostTypeConverters.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 80fef1bdda0..bc2951b6220 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -108,6 +108,8 @@ export namespace DiagnosticTag { switch (value) { case types.DiagnosticTag.Unnecessary: return MarkerTag.Unnecessary; + case types.DiagnosticTag.Deprecated: + return MarkerTag.Deprecated; } return undefined; } From 23c01b4ca5c6126aa459c0825fa38f4d73474c25 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Wed, 24 Jul 2019 13:20:17 -0500 Subject: [PATCH 13/48] Add deprecated classname --- src/vs/editor/browser/widget/codeEditorWidget.ts | 2 ++ src/vs/editor/common/model/intervalTree.ts | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 0cd342d195c..20da2f1c223 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1851,4 +1851,6 @@ registerThemingParticipant((theme, collector) => { if (unnecessaryBorder) { collector.addRule(`.${SHOW_UNUSED_ENABLED_CLASS} .monaco-editor .${ClassName.EditorUnnecessaryDecoration} { border-bottom: 2px dashed ${unnecessaryBorder}; }`); } + + collector.addRule(`.monaco-editor .${ClassName.EditorDeprecatedDecoration} { text-decoration: strikethrough; }`); }); diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts index 496e77b9ef4..b04d58b3101 100644 --- a/src/vs/editor/common/model/intervalTree.ts +++ b/src/vs/editor/common/model/intervalTree.ts @@ -17,7 +17,8 @@ export const enum ClassName { EditorWarningDecoration = 'squiggly-warning', EditorErrorDecoration = 'squiggly-error', EditorUnnecessaryDecoration = 'squiggly-unnecessary', - EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary' + EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary', + EditorDeprecatedDecoration = 'squiggly-deprecated' } export const enum NodeColor { From b71363ff77311f3d49fbdd5f693af70a99280db5 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Wed, 24 Jul 2019 13:40:14 -0500 Subject: [PATCH 14/48] Use inline decoration --- src/vs/editor/browser/widget/codeEditorWidget.ts | 2 +- src/vs/editor/common/model/intervalTree.ts | 2 +- src/vs/editor/common/services/markerDecorationsServiceImpl.ts | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 20da2f1c223..54c35262a68 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1852,5 +1852,5 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.${SHOW_UNUSED_ENABLED_CLASS} .monaco-editor .${ClassName.EditorUnnecessaryDecoration} { border-bottom: 2px dashed ${unnecessaryBorder}; }`); } - collector.addRule(`.monaco-editor .${ClassName.EditorDeprecatedDecoration} { text-decoration: strikethrough; }`); + collector.addRule(`.monaco-editor .${ClassName.EditorDeprecatedInlineDecoration} { text-decoration: strikethrough; }`); }); diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts index b04d58b3101..d400024e7c5 100644 --- a/src/vs/editor/common/model/intervalTree.ts +++ b/src/vs/editor/common/model/intervalTree.ts @@ -18,7 +18,7 @@ export const enum ClassName { EditorErrorDecoration = 'squiggly-error', EditorUnnecessaryDecoration = 'squiggly-unnecessary', EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary', - EditorDeprecatedDecoration = 'squiggly-deprecated' + EditorDeprecatedInlineDecoration = 'squiggly-inline-deprecated' } export const enum NodeColor { diff --git a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts index a319e8004b6..3da62fed16f 100644 --- a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts +++ b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts @@ -221,6 +221,9 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor if (marker.tags.indexOf(MarkerTag.Unnecessary) !== -1) { inlineClassName = ClassName.EditorUnnecessaryInlineDecoration; } + if (marker.tags.indexOf(MarkerTag.Deprecated) !== -1) { + inlineClassName = ClassName.EditorDeprecatedInlineDecoration; + } } return { From 895d6323b4a7686dbc9533c09bbc76fd741f195d Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Wed, 24 Jul 2019 13:40:44 -0500 Subject: [PATCH 15/48] Add CompletionItem.deprecated property from LSP --- src/vs/editor/common/modes.ts | 4 ++++ src/vs/monaco.d.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index fa05b9b968c..dbeb3fb1956 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -396,6 +396,10 @@ export interface CompletionItem { * an icon is chosen by the editor. */ kind: CompletionItemKind; + /** + * Indicates if this item is deprecated. + */ + deprecated?: boolean; /** * A human-readable string with additional information * about this item, like type or symbol information. diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 982a16b87ef..75692fbcceb 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4790,6 +4790,10 @@ declare namespace monaco.languages { * an icon is chosen by the editor. */ kind: CompletionItemKind; + /** + * Indicates if this item is deprecated. + */ + deprecated?: boolean; /** * A human-readable string with additional information * about this item, like type or symbol information. From 87ae3a38d337ade925a009cf86d82f11d36fe476 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Wed, 24 Jul 2019 14:46:50 -0500 Subject: [PATCH 16/48] Add deprecated support to SuggestDataDto --- src/vs/vscode.proposed.d.ts | 7 +++++++ src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts | 1 + src/vs/workbench/api/common/extHost.protocol.ts | 1 + src/vs/workbench/api/common/extHostLanguageFeatures.ts | 1 + 4 files changed, 10 insertions(+) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6a31141bb94..a495e65a268 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1277,6 +1277,13 @@ declare module 'vscode' { //#region Deprecated support + export interface CompletionItem { + /** + * Indicates if this item is deprecated. + */ + deprecated?: boolean; + } + export enum DiagnosticTag { /** * Deprecated or obsolete code diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 75af9eebf01..3541ba2eef0 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -341,6 +341,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha commitCharacters: data.k, additionalTextEdits: data.l, command: data.m, + deprecated: data.n, // not-standard _id: data.x, }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 8fb612d3a7c..1e30b620dca 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -937,6 +937,7 @@ export interface SuggestDataDto { k/* commitCharacters */?: string[]; l/* additionalTextEdits */?: ISingleEditOperation[]; m/* command */?: modes.Command; + n/* deprecated */?: boolean; // not-standard x?: ChainedCacheId; } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 884618cc457..6c7065f29f2 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -736,6 +736,7 @@ class SuggestAdapter { k: item.commitCharacters, l: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from), m: this._commands.toInternal(item.command, disposables), + n: item.deprecated }; // 'insertText'-logic From 3422bb0a26312c4139f3109a5dfefd75bbdb0c98 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Wed, 24 Jul 2019 14:47:13 -0500 Subject: [PATCH 17/48] Fix css rule to line-through --- src/vs/editor/browser/widget/codeEditorWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 54c35262a68..68e1746f63d 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1852,5 +1852,5 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.${SHOW_UNUSED_ENABLED_CLASS} .monaco-editor .${ClassName.EditorUnnecessaryDecoration} { border-bottom: 2px dashed ${unnecessaryBorder}; }`); } - collector.addRule(`.monaco-editor .${ClassName.EditorDeprecatedInlineDecoration} { text-decoration: strikethrough; }`); + collector.addRule(`.monaco-editor .${ClassName.EditorDeprecatedInlineDecoration} { text-decoration: line-through; }`); }); From 4a2b010e340412961bf64022eac3f68c70c52066 Mon Sep 17 00:00:00 2001 From: Kamran Ayub Date: Wed, 24 Jul 2019 15:10:18 -0500 Subject: [PATCH 18/48] Add inline deprecated styling to suggest widget --- src/vs/editor/common/model/intervalTree.ts | 2 +- src/vs/editor/contrib/suggest/media/suggest.css | 7 +++++++ src/vs/editor/contrib/suggest/suggestWidget.ts | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts index d400024e7c5..1815f7ee86f 100644 --- a/src/vs/editor/common/model/intervalTree.ts +++ b/src/vs/editor/common/model/intervalTree.ts @@ -18,7 +18,7 @@ export const enum ClassName { EditorErrorDecoration = 'squiggly-error', EditorUnnecessaryDecoration = 'squiggly-unnecessary', EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary', - EditorDeprecatedInlineDecoration = 'squiggly-inline-deprecated' + EditorDeprecatedInlineDecoration = 'inline-deprecated' } export const enum NodeColor { diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index 09594abb493..2f740dfad64 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -97,6 +97,13 @@ font-weight: bold; } +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .inline-deprecated { + text-decoration: none; /* override normal inline behavior due to HTML structure */ +} +.monaco-editor .suggest-widget .monaco-list .monaco-list-row .inline-deprecated span { + text-decoration: line-through; +} + /** Icon styles **/ .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close, diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 9e02b37f673..9eb1398848d 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -9,6 +9,7 @@ import { createMatches } from 'vs/base/common/filters'; import * as strings from 'vs/base/common/strings'; import { Event, Emitter } from 'vs/base/common/event'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { ClassName } from 'vs/editor/common/model/intervalTree'; import { IDisposable, dispose, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass, addDisposableListener } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IListEvent, IListRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list'; @@ -196,6 +197,10 @@ class Renderer implements IListRenderer ]; } + if (suggestion.label && suggestion.deprecated) { + labelOptions.extraClasses = (labelOptions.extraClasses || []).concat([ClassName.EditorDeprecatedInlineDecoration]); + } + data.iconLabel.setLabel(suggestion.label, undefined, labelOptions); data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); From e7070d00e6fed745adccccc79ee5a36f78a0eda2 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 25 Jul 2019 19:11:33 -0700 Subject: [PATCH 19/48] Fix #74715. --- src/vs/editor/common/model/textModelSearch.ts | 6 ++++++ .../test/common/model/textModelSearch.test.ts | 19 +++++++++++++++++++ test/electron/renderer.html | 4 ++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/model/textModelSearch.ts b/src/vs/editor/common/model/textModelSearch.ts index 1b63ef93f9f..ec2bb71ec30 100644 --- a/src/vs/editor/common/model/textModelSearch.ts +++ b/src/vs/editor/common/model/textModelSearch.ts @@ -545,6 +545,12 @@ export class Searcher { const matchStartIndex = m.index; const matchLength = m[0].length; if (matchStartIndex === this._prevMatchStartIndex && matchLength === this._prevMatchLength) { + if (matchLength === 0) { + // the search result is an empty string and won't advance `regex.lastIndex`, so `regex.exec` will stuck here + // we attempt to recover from that by advancing by one + this._searchRegex.lastIndex += 1; + continue; + } // Exit early if the regex matches the same range twice return null; } diff --git a/src/vs/editor/test/common/model/textModelSearch.test.ts b/src/vs/editor/test/common/model/textModelSearch.test.ts index 1dc5e82b52c..6476015cb5d 100644 --- a/src/vs/editor/test/common/model/textModelSearch.test.ts +++ b/src/vs/editor/test/common/model/textModelSearch.test.ts @@ -733,4 +733,23 @@ suite('TextModelSearch', () => { assert(isMultilineRegexSource('\\n')); assert(isMultilineRegexSource('foo\\W')); }); + + test('issue #74715. \\d* finds empty string and stops searching.', () => { + let model = TextModel.createFromString('10.243.30.10'); + + let searchParams = new SearchParams('\\d*', true, false, null); + + let actual = TextModelSearch.findMatches(model, searchParams, model.getFullModelRange(), true, 100); + assert.deepEqual(actual, [ + new FindMatch(new Range(1, 1, 1, 3), ['10']), + new FindMatch(new Range(1, 3, 1, 3), ['']), + new FindMatch(new Range(1, 4, 1, 7), ['243']), + new FindMatch(new Range(1, 7, 1, 7), ['']), + new FindMatch(new Range(1, 8, 1, 10), ['30']), + new FindMatch(new Range(1, 10, 1, 10), ['']), + new FindMatch(new Range(1, 11, 1, 13), ['10']) + ]); + + model.dispose(); + }); }); diff --git a/test/electron/renderer.html b/test/electron/renderer.html index 1eb67318bfa..10ddaaf3341 100644 --- a/test/electron/renderer.html +++ b/test/electron/renderer.html @@ -15,8 +15,8 @@ ui: 'tdd', timeout: 5000 }); - require('./renderer'); + require('/Users/penlv/code/vscode/test/electron/renderer'); - \ No newline at end of file + From f0f7220eed1ce0c2c967a9c5340c805e5db7f070 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 25 Jul 2019 20:32:04 -0700 Subject: [PATCH 20/48] bump distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f59a80c947f..0bdff88bdee 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.37.0", - "distro": "60ca9cac46049ff774e34d11e335d172587da608", + "distro": "ecf90e8c66d243c7ac15e30c24b3cc437d0700dc", "author": { "name": "Microsoft Corporation" }, From 6d82826e990407364d5c829b73602a19ecdf7d00 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 25 Jul 2019 20:42:36 -0700 Subject: [PATCH 21/48] Revert test require renderer --- test/electron/renderer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/electron/renderer.html b/test/electron/renderer.html index 10ddaaf3341..72454389597 100644 --- a/test/electron/renderer.html +++ b/test/electron/renderer.html @@ -15,7 +15,7 @@ ui: 'tdd', timeout: 5000 }); - require('/Users/penlv/code/vscode/test/electron/renderer'); + require('./renderer'); From 9ead7b9775dd7375f5272e60c747764eeeb9d672 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 26 Jul 2019 09:58:15 +0200 Subject: [PATCH 22/48] RemoteConnectionState not disconnected when initialization fails (for microsoft/vscode-remote-release#1029) --- .../electron-browser/remote.contribution.ts | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index 9f861987bc4..04975876713 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -49,7 +49,7 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc private windowCommandMenu: IMenu; private hasWindowActions: boolean = false; private remoteAuthority: string | undefined; - private disconnected: boolean = true; + private connectionState: 'initializing' | 'connected' | 'disconnected' | undefined = undefined; constructor( @IStatusbarService private readonly statusbarService: IStatusbarService, @@ -76,7 +76,8 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc if (this.remoteAuthority) { // Pending entry until extensions are ready this.renderWindowIndicator(nls.localize('host.open', "$(sync~spin) Opening Remote..."), undefined, WINDOW_ACTIONS_COMMAND_ID); - RemoteConnectionState.bindTo(this.contextKeyService).set('initializing'); + this.connectionState = 'initializing'; + RemoteConnectionState.bindTo(this.contextKeyService).set(this.connectionState); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '6_close', @@ -86,6 +87,23 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc }, order: 3.5 }); + + const connection = remoteAgentService.getConnection(); + if (connection) { + this._register(connection.onDidStateChange((e) => { + switch (e.type) { + case PersistentConnectionEventType.ConnectionLost: + case PersistentConnectionEventType.ReconnectionPermanentFailure: + case PersistentConnectionEventType.ReconnectionRunning: + case PersistentConnectionEventType.ReconnectionWait: + this.setDisconnected(true); + break; + case PersistentConnectionEventType.ConnectionGain: + this.setDisconnected(false); + break; + } + })); + } } extensionService.whenInstalledExtensionsRegistered().then(_ => { @@ -96,30 +114,14 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc this._register(this.windowCommandMenu.onDidChange(e => this.updateWindowActions())); this.updateWindowIndicator(); }); - - const connection = remoteAgentService.getConnection(); - if (connection) { - this._register(connection.onDidStateChange((e) => { - switch (e.type) { - case PersistentConnectionEventType.ConnectionLost: - case PersistentConnectionEventType.ReconnectionPermanentFailure: - case PersistentConnectionEventType.ReconnectionRunning: - case PersistentConnectionEventType.ReconnectionWait: - this.setDisconnected(true); - break; - case PersistentConnectionEventType.ConnectionGain: - this.setDisconnected(false); - break; - } - })); - } } private setDisconnected(isDisconnected: boolean): void { - if (this.disconnected !== isDisconnected) { - this.disconnected = isDisconnected; - RemoteConnectionState.bindTo(this.contextKeyService).set(isDisconnected ? 'disconnected' : 'connected'); - Deprecated_RemoteAuthorityContext.bindTo(this.contextKeyService).set(isDisconnected ? '' : this.remoteAuthority || `disconnected/${this.remoteAuthority}`); + const newState = isDisconnected ? 'disconnected' : 'connected'; + if (this.connectionState !== newState) { + this.connectionState = newState; + RemoteConnectionState.bindTo(this.contextKeyService).set(this.connectionState); + Deprecated_RemoteAuthorityContext.bindTo(this.contextKeyService).set(isDisconnected ? 'disconnected/${this.remoteAuthority!}' : this.remoteAuthority!); this.updateWindowIndicator(); } } @@ -128,7 +130,7 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc const windowActionCommand = (this.remoteAuthority || this.windowCommandMenu.getActions().length) ? WINDOW_ACTIONS_COMMAND_ID : undefined; if (this.remoteAuthority) { const hostLabel = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.remoteAuthority) || this.remoteAuthority; - if (!this.disconnected) { + if (this.connectionState !== 'disconnected') { this.renderWindowIndicator(`$(remote) ${hostLabel}`, nls.localize('host.tooltip', "Editing on {0}", hostLabel), windowActionCommand); } else { this.renderWindowIndicator(`$(alert) ${nls.localize('disconnectedFrom', "Disconnected from")} ${hostLabel}`, nls.localize('host.tooltipDisconnected', "Disconnected from {0}", hostLabel), windowActionCommand); @@ -468,4 +470,4 @@ Registry.as(ConfigurationExtensions.Configuration) } } } - }); \ No newline at end of file + }); From 3e9cac9b85d68766a4f3b45f17484bb979917a69 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 26 Jul 2019 11:30:06 +0200 Subject: [PATCH 23/48] properly cancel suggestion details requests, #77926 --- src/vs/editor/contrib/suggest/suggestWidget.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 9fa0cc0a87b..9d020eaf89c 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -672,7 +672,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate { - if (this.list.length < index) { + if (index >= this.list.length || item !== this.list.element(index)) { return; } @@ -689,11 +689,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate { - if (this.focusedItem === item) { - this.currentSuggestionDetails = null; - } - }); + }).catch(onUnexpectedError); } // emit an event From 0911c90ac4e7fbd8b46d339aa84ebb83a41c63a5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 26 Jul 2019 11:44:04 +0200 Subject: [PATCH 24/48] suggest - keep commit character when item isn't changing --- src/vs/editor/contrib/suggest/suggestCommitCharacters.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/vs/editor/contrib/suggest/suggestCommitCharacters.ts b/src/vs/editor/contrib/suggest/suggestCommitCharacters.ts index 8a6fca72612..20a7656532a 100644 --- a/src/vs/editor/contrib/suggest/suggestCommitCharacters.ts +++ b/src/vs/editor/contrib/suggest/suggestCommitCharacters.ts @@ -36,10 +36,17 @@ export class CommitCharacterController { private _onItem(selected: ISelectedSuggestion | undefined): void { if (!selected || !isNonEmptyArray(selected.item.completion.commitCharacters)) { + // no item or no commit characters this.reset(); return; } + if (this._active && this._active.item.item === selected.item) { + // still the same item + return; + } + + // keep item and its commit characters const acceptCharacters = new CharacterSet(); for (const ch of selected.item.completion.commitCharacters) { if (ch.length > 0) { From a7122371aaa76237b155cd38c57e9d66a5f47637 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 26 Jul 2019 11:56:20 +0200 Subject: [PATCH 25/48] dispo - add some more DisposableStore usage --- .../editor/contrib/suggest/suggestWidget.ts | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 9d020eaf89c..e6ac05dab25 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -50,7 +50,7 @@ interface ISuggestionTemplateData { iconLabel: IconLabel; typeLabel: HTMLElement; readMore: HTMLElement; - disposables: IDisposable[]; + disposables: DisposableStore; } /** @@ -106,8 +106,7 @@ class Renderer implements IListRenderer renderTemplate(container: HTMLElement): ISuggestionTemplateData { const data = Object.create(null); - const disposables = new DisposableStore(); - data.disposables = [disposables]; + data.disposables = new DisposableStore(); data.root = container; addClass(data.root, 'show-file-icons'); @@ -119,7 +118,7 @@ class Renderer implements IListRenderer const main = append(text, $('.main')); data.iconLabel = new IconLabel(main, { supportHighlights: true }); - disposables.add(data.iconLabel); + data.disposables.add(data.iconLabel); data.typeLabel = append(main, $('span.type-label')); @@ -147,7 +146,7 @@ class Renderer implements IListRenderer configureFont(); - disposables.add(Event.chain(this.editor.onDidChangeConfiguration.bind(this.editor)) + data.disposables.add(Event.chain(this.editor.onDidChangeConfiguration.bind(this.editor)) .filter(e => e.fontInfo || e.contribInfo) .on(configureFont, null)); @@ -218,7 +217,7 @@ class Renderer implements IListRenderer } disposeTemplate(templateData: ISuggestionTemplateData): void { - templateData.disposables = dispose(templateData.disposables); + templateData.disposables.dispose(); } } @@ -253,7 +252,7 @@ class SuggestionDetails { private type: HTMLElement; private docs: HTMLElement; private ariaLabel: string | null; - private disposables: IDisposable[]; + private readonly disposables: DisposableStore; private renderDisposeable: IDisposable; private borderWidth: number = 1; @@ -264,16 +263,16 @@ class SuggestionDetails { private readonly markdownRenderer: MarkdownRenderer, private readonly triggerKeybindingLabel: string, ) { - this.disposables = []; + this.disposables = new DisposableStore(); this.el = append(container, $('.details')); - this.disposables.push(toDisposable(() => container.removeChild(this.el))); + this.disposables.add(toDisposable(() => container.removeChild(this.el))); this.body = $('.body'); this.scrollbar = new DomScrollableElement(this.body, {}); append(this.el, this.scrollbar.getDomNode()); - this.disposables.push(this.scrollbar); + this.disposables.add(this.scrollbar); this.header = append(this.body, $('.header')); this.close = append(this.header, $('span.close')); @@ -414,7 +413,7 @@ class SuggestionDetails { } dispose(): void { - this.disposables = dispose(this.disposables); + this.disposables.dispose(); this.renderDisposeable = dispose(this.renderDisposeable); } } From 98fc1148c923e7fae1d9e4b7993965dd238be495 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 26 Jul 2019 14:18:57 +0200 Subject: [PATCH 26/48] :lipstick: --- .../common/inactiveExtensionUrlHandler.ts | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts b/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts index bf7bb20ec81..7330ceedefb 100644 --- a/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts @@ -96,8 +96,10 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { return true; } - const confirmedExtensionIds = this.getConfirmedExtensionIds(); - confirmed = confirmed || confirmedExtensionIds.has(ExtensionIdentifier.toKey(extensionId)); + if (!confirmed) { + const confirmedExtensionIds = this.getConfirmedExtensionIds(); + confirmed = confirmedExtensionIds.has(ExtensionIdentifier.toKey(extensionId)); + } if (!confirmed) { let uriString = uri.toString(); @@ -283,18 +285,17 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } private getConfirmedExtensionIds(): Set { - return new Set([ + const ids = [ ...this.getConfirmedExtensionIdsFromStorage(), ...this.getConfirmedExtensionIdsFromConfiguration(), - ].map( - extensionId => ExtensionIdentifier.toKey(extensionId) - )); + ].map(extensionId => ExtensionIdentifier.toKey(extensionId)); + + return new Set(ids); } private getConfirmedExtensionIdsFromConfiguration(): Array { - const confirmedExtensionIds = this.configurationService.getValue>( - CONFIRMED_EXTENSIONS_CONFIGURATION_KEY - ); + const confirmedExtensionIds = this.configurationService.getValue>(CONFIRMED_EXTENSIONS_CONFIGURATION_KEY); + if (!Array.isArray(confirmedExtensionIds)) { return []; } @@ -303,17 +304,15 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { } private getConfirmedExtensionIdsFromStorage(): Array { - const confirmedExtensionIdsJson = this.storageService.get( - CONFIRMED_EXTENSIONS_STORAGE_KEY, - StorageScope.GLOBAL, - '[]', - ); + const confirmedExtensionIdsJson = this.storageService.get(CONFIRMED_EXTENSIONS_STORAGE_KEY, StorageScope.GLOBAL, '[]'); + try { return JSON.parse(confirmedExtensionIdsJson); } catch (err) { return []; } } + private addConfirmedExtensionIdToStorage(extensionId: string): void { const existingConfirmedExtensionIds = this.getConfirmedExtensionIdsFromStorage(); this.storageService.store( From 8213baf0769d63d6bc65db25b93fc5fb91432173 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 26 Jul 2019 14:37:33 +0200 Subject: [PATCH 27/48] extract first repository.status call related to https://github.com/microsoft/vscode/issues/77787#issuecomment-515207062 --- extensions/git/src/model.ts | 1 + extensions/git/src/repository.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index c410dde86bd..da6a940428e 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -251,6 +251,7 @@ export class Model { const repository = new Repository(this.git.open(repositoryRoot, dotGit), this.globalState, this.outputChannel); this.open(repository); + await repository.status(); } catch (err) { if (err.gitErrorCode === GitErrorCodes.NotAGitRepository) { return; diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index a581f9f5fd4..36366ad7d09 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -713,7 +713,6 @@ export class Repository implements Disposable { this.disposables.push(progressManager); this.updateCommitTemplate(); - this.status(); } validateInput(text: string, position: number): SourceControlInputBoxValidation | undefined { From be873f71699b8617e088c8f5ecd57f56e6ad140f Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Fri, 26 Jul 2019 15:16:12 +0200 Subject: [PATCH 28/48] pass env and cwd to createTerminal; fixes #77111 --- .../workbench/api/node/extHostDebugService.ts | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index d9172447d73..be353ca5bde 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -341,19 +341,29 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } else { resolve(true); } - }).then(needNewTerminal => { + }).then(async needNewTerminal => { + + const configProvider = await this._configurationService.getConfigProvider(); + const shell = this._terminalService.getDefaultShell(configProvider); if (needNewTerminal || !this._integratedTerminalInstance) { - this._integratedTerminalInstance = this._terminalService.createTerminal(args.title || nls.localize('debug.terminal.title', "debuggee")); + const options: vscode.TerminalOptions = { + shellPath: shell, + // shellArgs: this._terminalService._getDefaultShellArgs(configProvider), + cwd: args.cwd, + name: args.title || nls.localize('debug.terminal.title', "debuggee"), + env: args.env + }; + delete args.cwd; + delete args.env; + this._integratedTerminalInstance = this._terminalService.createTerminalFromOptions(options); } const terminal: vscode.Terminal = this._integratedTerminalInstance; terminal.show(); - return this._integratedTerminalInstance.processId.then(async shellProcessId => { + return this._integratedTerminalInstance.processId.then(shellProcessId => { - const configProvider = await this._configurationService.getConfigProvider(); - const shell = this._terminalService.getDefaultShell(configProvider); const command = prepareCommand(args, shell, configProvider); terminal.sendText(command, true); From 4882ae5b9e5e5c21faaf95e28cd5f31dfe38892f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 26 Jul 2019 15:24:11 +0200 Subject: [PATCH 29/48] Revert "Add inline deprecated styling to suggest widget" This reverts commit 4a2b010e340412961bf64022eac3f68c70c52066. --- src/vs/editor/common/model/intervalTree.ts | 2 +- src/vs/editor/contrib/suggest/media/suggest.css | 7 ------- src/vs/editor/contrib/suggest/suggestWidget.ts | 5 ----- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts index 1815f7ee86f..d400024e7c5 100644 --- a/src/vs/editor/common/model/intervalTree.ts +++ b/src/vs/editor/common/model/intervalTree.ts @@ -18,7 +18,7 @@ export const enum ClassName { EditorErrorDecoration = 'squiggly-error', EditorUnnecessaryDecoration = 'squiggly-unnecessary', EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary', - EditorDeprecatedInlineDecoration = 'inline-deprecated' + EditorDeprecatedInlineDecoration = 'squiggly-inline-deprecated' } export const enum NodeColor { diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index 2f740dfad64..09594abb493 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -97,13 +97,6 @@ font-weight: bold; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .inline-deprecated { - text-decoration: none; /* override normal inline behavior due to HTML structure */ -} -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .inline-deprecated span { - text-decoration: line-through; -} - /** Icon styles **/ .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close, diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 9eb1398848d..9e02b37f673 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -9,7 +9,6 @@ import { createMatches } from 'vs/base/common/filters'; import * as strings from 'vs/base/common/strings'; import { Event, Emitter } from 'vs/base/common/event'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { ClassName } from 'vs/editor/common/model/intervalTree'; import { IDisposable, dispose, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass, addDisposableListener } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IListEvent, IListRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list'; @@ -197,10 +196,6 @@ class Renderer implements IListRenderer ]; } - if (suggestion.label && suggestion.deprecated) { - labelOptions.extraClasses = (labelOptions.extraClasses || []).concat([ClassName.EditorDeprecatedInlineDecoration]); - } - data.iconLabel.setLabel(suggestion.label, undefined, labelOptions); data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, ''); From 53006c0af48632dd79268bab6534abd8f13b7e3b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 26 Jul 2019 15:24:21 +0200 Subject: [PATCH 30/48] Revert "Add deprecated support to SuggestDataDto" This reverts commit 87ae3a38d337ade925a009cf86d82f11d36fe476. --- src/vs/vscode.proposed.d.ts | 7 ------- src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts | 1 - src/vs/workbench/api/common/extHost.protocol.ts | 1 - src/vs/workbench/api/common/extHostLanguageFeatures.ts | 1 - 4 files changed, 10 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index a495e65a268..6a31141bb94 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1277,13 +1277,6 @@ declare module 'vscode' { //#region Deprecated support - export interface CompletionItem { - /** - * Indicates if this item is deprecated. - */ - deprecated?: boolean; - } - export enum DiagnosticTag { /** * Deprecated or obsolete code diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 3541ba2eef0..75af9eebf01 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -341,7 +341,6 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha commitCharacters: data.k, additionalTextEdits: data.l, command: data.m, - deprecated: data.n, // not-standard _id: data.x, }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 1e30b620dca..8fb612d3a7c 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -937,7 +937,6 @@ export interface SuggestDataDto { k/* commitCharacters */?: string[]; l/* additionalTextEdits */?: ISingleEditOperation[]; m/* command */?: modes.Command; - n/* deprecated */?: boolean; // not-standard x?: ChainedCacheId; } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 6c7065f29f2..884618cc457 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -736,7 +736,6 @@ class SuggestAdapter { k: item.commitCharacters, l: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from), m: this._commands.toInternal(item.command, disposables), - n: item.deprecated }; // 'insertText'-logic From 29f6ec0b34516474babf46710770a735887a2c85 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 26 Jul 2019 15:24:30 +0200 Subject: [PATCH 31/48] Revert "Add CompletionItem.deprecated property from LSP" This reverts commit 895d6323b4a7686dbc9533c09bbc76fd741f195d. --- src/vs/editor/common/modes.ts | 4 ---- src/vs/monaco.d.ts | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index dbeb3fb1956..fa05b9b968c 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -396,10 +396,6 @@ export interface CompletionItem { * an icon is chosen by the editor. */ kind: CompletionItemKind; - /** - * Indicates if this item is deprecated. - */ - deprecated?: boolean; /** * A human-readable string with additional information * about this item, like type or symbol information. diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 75692fbcceb..982a16b87ef 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4790,10 +4790,6 @@ declare namespace monaco.languages { * an icon is chosen by the editor. */ kind: CompletionItemKind; - /** - * Indicates if this item is deprecated. - */ - deprecated?: boolean; /** * A human-readable string with additional information * about this item, like type or symbol information. From a154f8f45185fc4d87b5d159c9a6e6678623d190 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 26 Jul 2019 15:33:27 +0200 Subject: [PATCH 32/48] fixes #77345 --- extensions/git/src/repository.ts | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 36366ad7d09..196a1bddade 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -519,8 +519,8 @@ class DotGitWatcher implements IFileWatcher { this.transientDisposables.push(upstreamWatcher); upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables); } catch (err) { - if (env.logLevel <= LogLevel.Info) { - this.outputChannel.appendLine(`Failed to watch ref '${upstreamPath}'. Ref is most likely packed.`); + if (env.logLevel <= LogLevel.Error) { + this.outputChannel.appendLine(`Failed to watch ref '${upstreamPath}', is most likely packed.\n${err.stack || err}`); } } } @@ -651,19 +651,30 @@ export class Repository implements Disposable { const onWorkspaceRepositoryFileChange = filterEvent(onWorkspaceFileChange, uri => isDescendant(repository.root, uri.fsPath)); const onWorkspaceWorkingTreeFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => !/\/\.git($|\/)/.test(uri.path)); - const dotGitFileWatcher = new DotGitWatcher(this, outputChannel); - this.disposables.push(dotGitFileWatcher); + let onDotGitFileChange: Event; + + try { + const dotGitFileWatcher = new DotGitWatcher(this, outputChannel); + onDotGitFileChange = dotGitFileWatcher.event; + this.disposables.push(dotGitFileWatcher); + } catch (err) { + if (env.logLevel <= LogLevel.Error) { + outputChannel.appendLine(`Failed to watch '${this.dotGit}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`); + } + + onDotGitFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => /\/\.git($|\/)/.test(uri.path)); + } // FS changes should trigger `git status`: // - any change inside the repository working tree // - any change whithin the first level of the `.git` folder, except the folder itself and `index.lock` - const onFileChange = anyEvent(onWorkspaceWorkingTreeFileChange, dotGitFileWatcher.event); + const onFileChange = anyEvent(onWorkspaceWorkingTreeFileChange, onDotGitFileChange); onFileChange(this.onFileChange, this, this.disposables); // Relevate repository changes should trigger virtual document change events - dotGitFileWatcher.event(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables); + onDotGitFileChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables); - this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, dotGitFileWatcher.event, outputChannel)); + this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, onDotGitFileChange, outputChannel)); const root = Uri.file(repository.root); this._sourceControl = scm.createSourceControl('git', 'Git', root); From c96cf5d4d9cf0bcab5d10a6511dbe57d41f4245a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 26 Jul 2019 16:08:27 +0200 Subject: [PATCH 33/48] fixes #76508 --- build/gulpfile.reh.js | 2 +- build/lib/extensions.js | 2 +- build/lib/extensions.ts | 2 +- build/lib/typings/gulp-remote-src.d.ts | 4 +-- package.json | 2 +- yarn.lock | 39 ++++++++++++++++++++------ 6 files changed, 37 insertions(+), 14 deletions(-) diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index 8784602db3e..7085c4f8f50 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -17,7 +17,7 @@ const gunzip = require('gulp-gunzip'); const untar = require('gulp-untar'); const File = require('vinyl'); const fs = require('fs'); -const remote = require('gulp-remote-src'); +const remote = require('gulp-remote-retry-src'); const rename = require('gulp-rename'); const filter = require('gulp-filter'); const cp = require('child_process'); diff --git a/build/lib/extensions.js b/build/lib/extensions.js index a73f8cf5864..73d2c7acef3 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -13,7 +13,7 @@ const File = require("vinyl"); const vsce = require("vsce"); const stats_1 = require("./stats"); const util2 = require("./util"); -const remote = require("gulp-remote-src"); +const remote = require("gulp-remote-retry-src"); const vzip = require('gulp-vinyl-zip'); const filter = require("gulp-filter"); const rename = require("gulp-rename"); diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 94bc81c15df..4b185aff681 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -13,7 +13,7 @@ import * as File from 'vinyl'; import * as vsce from 'vsce'; import { createStatsStream } from './stats'; import * as util2 from './util'; -import remote = require('gulp-remote-src'); +import remote = require('gulp-remote-retry-src'); const vzip = require('gulp-vinyl-zip'); import filter = require('gulp-filter'); import rename = require('gulp-rename'); diff --git a/build/lib/typings/gulp-remote-src.d.ts b/build/lib/typings/gulp-remote-src.d.ts index 6ea57f84fe5..ff9026b79bb 100644 --- a/build/lib/typings/gulp-remote-src.d.ts +++ b/build/lib/typings/gulp-remote-src.d.ts @@ -1,4 +1,4 @@ -declare module 'gulp-remote-src' { +declare module 'gulp-remote-retry-src' { import stream = require("stream"); @@ -20,4 +20,4 @@ declare module 'gulp-remote-src' { } export = remote; -} \ No newline at end of file +} diff --git a/package.json b/package.json index 0bdff88bdee..f973590748b 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "gulp-gunzip": "^1.0.0", "gulp-json-editor": "^2.5.0", "gulp-plumber": "^1.2.0", - "gulp-remote-src": "^0.4.4", + "gulp-remote-retry-src": "^0.6.0", "gulp-rename": "^1.2.0", "gulp-replace": "^0.5.4", "gulp-shell": "^0.6.5", diff --git a/yarn.lock b/yarn.lock index 6c206d6fa00..f1c162556e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3907,14 +3907,15 @@ gulp-plumber@^1.2.0: plugin-error "^0.1.2" through2 "^2.0.3" -gulp-remote-src@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/gulp-remote-src/-/gulp-remote-src-0.4.4.tgz#4a4d18fac0ffedde94a7855953de90db00a1d1b1" - integrity sha512-mo7lGgZmNXyTbcUzfjSnUVkx1pnqqiwv/pPaIrYdTO77hq0WNTxXLAzQdoYOnyJ0mfVLNmNl9AGqWLiAzTPMMA== +gulp-remote-retry-src@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/gulp-remote-retry-src/-/gulp-remote-retry-src-0.6.0.tgz#fdcb5d5c9e67c31ae378a2a886ddad3d47913bb1" + integrity sha512-lFxpwwbM/GEIdYiNumxiUcPHZUROFJaF1zTBne1H8b3Pwx6Te6O9uEYp++JZPP62jdheOWcHUTBREiMkpdbm4Q== dependencies: event-stream "3.3.4" node.extend "~1.1.2" request "^2.88.0" + requestretry "^4.0.0" through2 "~2.0.3" vinyl "~2.0.1" @@ -4161,6 +4162,13 @@ has@^1.0.1: dependencies: function-bind "^1.0.2" +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + hash-base@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" @@ -6088,11 +6096,12 @@ node-pty@0.9.0-beta19: nan "^2.13.2" node.extend@~1.1.2: - version "1.1.6" - resolved "https://registry.yarnpkg.com/node.extend/-/node.extend-1.1.6.tgz#a7b882c82d6c93a4863a5504bd5de8ec86258b96" - integrity sha1-p7iCyC1sk6SGOlUEvV3o7IYli5Y= + version "1.1.8" + resolved "https://registry.yarnpkg.com/node.extend/-/node.extend-1.1.8.tgz#0aab3e63789f4e6d68b42bc00073ad1881243cf0" + integrity sha512-L/dvEBwyg3UowwqOUTyDsGBU6kjBQOpOhshio9V3i3BMPv5YUb9+mWNN8MK0IbWqT0AqaTSONZf0aTuMMahWgA== dependencies: - is "^3.1.0" + has "^1.0.3" + is "^3.2.1" noop-logger@^0.1.1: version "0.1.1" @@ -7737,6 +7746,15 @@ request@^2.86.0, request@^2.88.0: tunnel-agent "^0.6.0" uuid "^3.3.2" +requestretry@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/requestretry/-/requestretry-4.0.0.tgz#4e9e7280a7d8561bf33e9925264cf026e2be3e89" + integrity sha512-ST8m0+5FQH2FA+gbzUQyOQjUwHf22kbPQnd6TexveR0p+2UV1YYBg+Roe7BnKQ1Bb/+LtJwwm0QzxK2NA20Cug== + dependencies: + extend "^3.0.2" + lodash "^4.17.10" + when "^3.7.7" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -9684,6 +9702,11 @@ webpack@^4.16.5, webpack@^4.7.0: watchpack "^1.5.0" webpack-sources "^1.0.1" +when@^3.7.7: + version "3.7.8" + resolved "https://registry.yarnpkg.com/when/-/when-3.7.8.tgz#c7130b6a7ea04693e842cdc9e7a1f2aa39a39f82" + integrity sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I= + whet.extend@~0.9.9: version "0.9.9" resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" From e788b154d1d95e99b5dd01eca2eae365cfdeb729 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 26 Jul 2019 11:25:05 +0200 Subject: [PATCH 34/48] Fix usage of context keys --- src/vs/platform/browser/contextScopedHistoryWidget.ts | 6 +++--- src/vs/workbench/contrib/scm/browser/scm.contribution.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/browser/contextScopedHistoryWidget.ts b/src/vs/platform/browser/contextScopedHistoryWidget.ts index 94d4de658b5..5e5e8603683 100644 --- a/src/vs/platform/browser/contextScopedHistoryWidget.ts +++ b/src/vs/platform/browser/contextScopedHistoryWidget.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IContextKeyService, ContextKeyDefinedExpr, ContextKeyExpr, ContextKeyAndExpr, ContextKeyEqualsExpr, RawContextKey, IContextKey, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, ContextKeyExpr, RawContextKey, IContextKey, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey'; import { HistoryInputBox, IHistoryInputOptions } from 'vs/base/browser/ui/inputbox/inputBox'; import { FindInput, IFindInputOptions } from 'vs/base/browser/ui/findinput/findInput'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; @@ -66,7 +66,7 @@ export class ContextScopedFindInput extends FindInput { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'history.showPrevious', weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(new ContextKeyDefinedExpr(HistoryNavigationWidgetContext), new ContextKeyEqualsExpr(HistoryNavigationEnablementContext, true)), + when: ContextKeyExpr.and(ContextKeyExpr.has(HistoryNavigationWidgetContext), ContextKeyExpr.equals(HistoryNavigationEnablementContext, true)), primary: KeyCode.UpArrow, secondary: [KeyMod.Alt | KeyCode.UpArrow], handler: (accessor, arg2) => { @@ -81,7 +81,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'history.showNext', weight: KeybindingWeight.WorkbenchContrib, - when: new ContextKeyAndExpr([new ContextKeyDefinedExpr(HistoryNavigationWidgetContext), new ContextKeyEqualsExpr(HistoryNavigationEnablementContext, true)]), + when: ContextKeyExpr.and(ContextKeyExpr.has(HistoryNavigationWidgetContext), ContextKeyExpr.equals(HistoryNavigationEnablementContext, true)), primary: KeyCode.DownArrow, secondary: [KeyMod.Alt | KeyCode.DownArrow], handler: (accessor, arg2) => { diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index db9568a6eb4..beef3cdc9fb 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -18,7 +18,7 @@ import { SCMViewlet } from 'vs/workbench/contrib/scm/browser/scmViewlet'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { ContextKeyDefinedExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -115,7 +115,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'scm.acceptInput', description: { description: localize('scm accept', "SCM: Accept Input"), args: [] }, weight: KeybindingWeight.WorkbenchContrib, - when: new ContextKeyDefinedExpr('scmRepository'), + when: ContextKeyExpr.has('scmRepository'), primary: KeyMod.CtrlCmd | KeyCode.Enter, handler: accessor => { const contextKeyService = accessor.get(IContextKeyService); @@ -134,4 +134,4 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -registerSingleton(ISCMService, SCMService); \ No newline at end of file +registerSingleton(ISCMService, SCMService); From 893635042ccd4dbdf919a4493a062330aec2cbc7 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 26 Jul 2019 16:35:45 +0200 Subject: [PATCH 35/48] Fixes #22778: Add `or` for when clause contexts --- .../standalone/browser/simpleServices.ts | 2 +- .../platform/contextkey/common/contextkey.ts | 394 ++++++++++++++---- .../contextkey/test/common/contextkey.test.ts | 31 +- .../keybinding/common/keybindingResolver.ts | 40 +- .../test/common/keybindingResolver.test.ts | 83 ++-- .../parts/editor/editor.contribution.ts | 2 +- .../debug/browser/debug.contribution.ts | 2 +- .../files/browser/fileActions.contribution.ts | 2 +- .../keybinding/browser/keybindingService.ts | 4 +- 9 files changed, 387 insertions(+), 173 deletions(-) diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 576092c9865..d333ce03746 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -356,7 +356,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { private _toNormalizedKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { let result: ResolvedKeybindingItem[] = [], resultLen = 0; for (const item of items) { - const when = (item.when ? item.when.normalize() : undefined); + const when = item.when || undefined; const keybinding = item.keybinding; if (!keybinding) { diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index 289ba8668ce..8c01c1452d8 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -13,7 +13,9 @@ export const enum ContextKeyExprType { Equals = 3, NotEquals = 4, And = 5, - Regex = 6 + Regex = 6, + NotRegex = 7, + Or = 8 } export interface IContextKeyExprMapper { @@ -27,27 +29,31 @@ export interface IContextKeyExprMapper { export abstract class ContextKeyExpr { public static has(key: string): ContextKeyExpr { - return new ContextKeyDefinedExpr(key); + return ContextKeyDefinedExpr.create(key); } public static equals(key: string, value: any): ContextKeyExpr { - return new ContextKeyEqualsExpr(key, value); + return ContextKeyEqualsExpr.create(key, value); } public static notEquals(key: string, value: any): ContextKeyExpr { - return new ContextKeyNotEqualsExpr(key, value); + return ContextKeyNotEqualsExpr.create(key, value); } public static regex(key: string, value: RegExp): ContextKeyExpr { - return new ContextKeyRegexExpr(key, value); + return ContextKeyRegexExpr.create(key, value); } public static not(key: string): ContextKeyExpr { - return new ContextKeyNotExpr(key); + return ContextKeyNotExpr.create(key); } - public static and(...expr: Array): ContextKeyExpr { - return new ContextKeyAndExpr(expr); + public static and(...expr: Array): ContextKeyExpr | undefined { + return ContextKeyAndExpr.create(expr); + } + + public static or(...expr: Array): ContextKeyExpr | undefined { + return ContextKeyOrExpr.create(expr); } public static deserialize(serialized: string | null | undefined, strict: boolean = false): ContextKeyExpr | undefined { @@ -55,9 +61,17 @@ export abstract class ContextKeyExpr { return undefined; } + return this._deserializeOrExpression(serialized, strict); + } + + private static _deserializeOrExpression(serialized: string, strict: boolean): ContextKeyExpr | undefined { + let pieces = serialized.split('||'); + return ContextKeyOrExpr.create(pieces.map(p => this._deserializeAndExpression(p, strict))); + } + + private static _deserializeAndExpression(serialized: string, strict: boolean): ContextKeyExpr | undefined { let pieces = serialized.split('&&'); - let result = new ContextKeyAndExpr(pieces.map(p => this._deserializeOne(p, strict))); - return result.normalize(); + return ContextKeyAndExpr.create(pieces.map(p => this._deserializeOne(p, strict))); } private static _deserializeOne(serializedOne: string, strict: boolean): ContextKeyExpr { @@ -65,24 +79,24 @@ export abstract class ContextKeyExpr { if (serializedOne.indexOf('!=') >= 0) { let pieces = serializedOne.split('!='); - return new ContextKeyNotEqualsExpr(pieces[0].trim(), this._deserializeValue(pieces[1], strict)); + return ContextKeyNotEqualsExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict)); } if (serializedOne.indexOf('==') >= 0) { let pieces = serializedOne.split('=='); - return new ContextKeyEqualsExpr(pieces[0].trim(), this._deserializeValue(pieces[1], strict)); + return ContextKeyEqualsExpr.create(pieces[0].trim(), this._deserializeValue(pieces[1], strict)); } if (serializedOne.indexOf('=~') >= 0) { let pieces = serializedOne.split('=~'); - return new ContextKeyRegexExpr(pieces[0].trim(), this._deserializeRegexValue(pieces[1], strict)); + return ContextKeyRegexExpr.create(pieces[0].trim(), this._deserializeRegexValue(pieces[1], strict)); } if (/^\!\s*/.test(serializedOne)) { - return new ContextKeyNotExpr(serializedOne.substr(1).trim()); + return ContextKeyNotExpr.create(serializedOne.substr(1).trim()); } - return new ContextKeyDefinedExpr(serializedOne); + return ContextKeyDefinedExpr.create(serializedOne); } private static _deserializeValue(serializedValue: string, strict: boolean): any { @@ -143,10 +157,10 @@ export abstract class ContextKeyExpr { public abstract getType(): ContextKeyExprType; public abstract equals(other: ContextKeyExpr): boolean; public abstract evaluate(context: IContext): boolean; - public abstract normalize(): ContextKeyExpr | undefined; public abstract serialize(): string; public abstract keys(): string[]; public abstract map(mapFnc: IContextKeyExprMapper): ContextKeyExpr; + public abstract negate(): ContextKeyExpr; } function cmp(a: ContextKeyExpr, b: ContextKeyExpr): number { @@ -166,13 +180,21 @@ function cmp(a: ContextKeyExpr, b: ContextKeyExpr): number { return (a).cmp(b); case ContextKeyExprType.Regex: return (a).cmp(b); + case ContextKeyExprType.NotRegex: + return (a).cmp(b); + case ContextKeyExprType.And: + return (a).cmp(b); default: throw new Error('Unknown ContextKeyExpr!'); } } export class ContextKeyDefinedExpr implements ContextKeyExpr { - constructor(protected key: string) { + public static create(key: string): ContextKeyExpr { + return new ContextKeyDefinedExpr(key); + } + + protected constructor(protected key: string) { } public getType(): ContextKeyExprType { @@ -200,10 +222,6 @@ export class ContextKeyDefinedExpr implements ContextKeyExpr { return (!!context.getValue(this.key)); } - public normalize(): ContextKeyExpr { - return this; - } - public serialize(): string { return this.key; } @@ -215,10 +233,25 @@ export class ContextKeyDefinedExpr implements ContextKeyExpr { public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { return mapFnc.mapDefined(this.key); } + + public negate(): ContextKeyExpr { + return ContextKeyNotExpr.create(this.key); + } } export class ContextKeyEqualsExpr implements ContextKeyExpr { - constructor(private readonly key: string, private readonly value: any) { + + public static create(key: string, value: any): ContextKeyExpr { + if (typeof value === 'boolean') { + if (value) { + return ContextKeyDefinedExpr.create(key); + } + return ContextKeyNotExpr.create(key); + } + return new ContextKeyEqualsExpr(key, value); + } + + private constructor(private readonly key: string, private readonly value: any) { } public getType(): ContextKeyExprType { @@ -255,21 +288,7 @@ export class ContextKeyEqualsExpr implements ContextKeyExpr { /* tslint:enable:triple-equals */ } - public normalize(): ContextKeyExpr { - if (typeof this.value === 'boolean') { - if (this.value) { - return new ContextKeyDefinedExpr(this.key); - } - return new ContextKeyNotExpr(this.key); - } - return this; - } - public serialize(): string { - if (typeof this.value === 'boolean') { - return this.normalize().serialize(); - } - return this.key + ' == \'' + this.value + '\''; } @@ -280,10 +299,25 @@ export class ContextKeyEqualsExpr implements ContextKeyExpr { public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { return mapFnc.mapEquals(this.key, this.value); } + + public negate(): ContextKeyExpr { + return ContextKeyNotEqualsExpr.create(this.key, this.value); + } } export class ContextKeyNotEqualsExpr implements ContextKeyExpr { - constructor(private key: string, private value: any) { + + public static create(key: string, value: any): ContextKeyExpr { + if (typeof value === 'boolean') { + if (value) { + return ContextKeyNotExpr.create(key); + } + return ContextKeyDefinedExpr.create(key); + } + return new ContextKeyNotEqualsExpr(key, value); + } + + private constructor(private key: string, private value: any) { } public getType(): ContextKeyExprType { @@ -320,21 +354,7 @@ export class ContextKeyNotEqualsExpr implements ContextKeyExpr { /* tslint:enable:triple-equals */ } - public normalize(): ContextKeyExpr { - if (typeof this.value === 'boolean') { - if (this.value) { - return new ContextKeyNotExpr(this.key); - } - return new ContextKeyDefinedExpr(this.key); - } - return this; - } - public serialize(): string { - if (typeof this.value === 'boolean') { - return this.normalize().serialize(); - } - return this.key + ' != \'' + this.value + '\''; } @@ -345,10 +365,19 @@ export class ContextKeyNotEqualsExpr implements ContextKeyExpr { public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { return mapFnc.mapNotEquals(this.key, this.value); } + + public negate(): ContextKeyExpr { + return ContextKeyEqualsExpr.create(this.key, this.value); + } } export class ContextKeyNotExpr implements ContextKeyExpr { - constructor(private key: string) { + + public static create(key: string): ContextKeyExpr { + return new ContextKeyNotExpr(key); + } + + private constructor(private key: string) { } public getType(): ContextKeyExprType { @@ -376,10 +405,6 @@ export class ContextKeyNotExpr implements ContextKeyExpr { return (!context.getValue(this.key)); } - public normalize(): ContextKeyExpr { - return this; - } - public serialize(): string { return '!' + this.key; } @@ -391,11 +416,19 @@ export class ContextKeyNotExpr implements ContextKeyExpr { public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { return mapFnc.mapNot(this.key); } + + public negate(): ContextKeyExpr { + return ContextKeyDefinedExpr.create(this.key); + } } export class ContextKeyRegexExpr implements ContextKeyExpr { - constructor(private key: string, private regexp: RegExp | null) { + public static create(key: string, regexp: RegExp | null): ContextKeyExpr { + return new ContextKeyRegexExpr(key, regexp); + } + + private constructor(private key: string, private regexp: RegExp | null) { // } @@ -435,10 +468,6 @@ export class ContextKeyRegexExpr implements ContextKeyExpr { return this.regexp ? this.regexp.test(value) : false; } - public normalize(): ContextKeyExpr { - return this; - } - public serialize(): string { const value = this.regexp ? `/${this.regexp.source}/${this.regexp.ignoreCase ? 'i' : ''}` @@ -450,22 +479,99 @@ export class ContextKeyRegexExpr implements ContextKeyExpr { return [this.key]; } - public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { + public map(mapFnc: IContextKeyExprMapper): ContextKeyRegexExpr { return mapFnc.mapRegex(this.key, this.regexp); } + + public negate(): ContextKeyExpr { + return ContextKeyNotRegexExpr.create(this); + } +} + +export class ContextKeyNotRegexExpr implements ContextKeyExpr { + + public static create(actual: ContextKeyRegexExpr): ContextKeyExpr { + return new ContextKeyNotRegexExpr(actual); + } + + private constructor(private readonly _actual: ContextKeyRegexExpr) { + // + } + + public getType(): ContextKeyExprType { + return ContextKeyExprType.NotRegex; + } + + public cmp(other: ContextKeyNotRegexExpr): number { + return this._actual.cmp(other._actual); + } + + public equals(other: ContextKeyExpr): boolean { + if (other instanceof ContextKeyNotRegexExpr) { + return this._actual.equals(other._actual); + } + return false; + } + + public evaluate(context: IContext): boolean { + return !this._actual.evaluate(context); + } + + public serialize(): string { + throw new Error('Method not implemented.'); + } + + public keys(): string[] { + return this._actual.keys(); + } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { + return new ContextKeyNotRegexExpr(this._actual.map(mapFnc)); + } + + public negate(): ContextKeyExpr { + return this._actual; + } } export class ContextKeyAndExpr implements ContextKeyExpr { - public readonly expr: ContextKeyExpr[]; - constructor(expr: Array) { - this.expr = ContextKeyAndExpr._normalizeArr(expr); + public static create(_expr: Array): ContextKeyExpr | undefined { + const expr = ContextKeyAndExpr._normalizeArr(_expr); + if (expr.length === 0) { + return undefined; + } + + if (expr.length === 1) { + return expr[0]; + } + + return new ContextKeyAndExpr(expr); + } + + private constructor(public readonly expr: ContextKeyExpr[]) { } public getType(): ContextKeyExprType { return ContextKeyExprType.And; } + public cmp(other: ContextKeyAndExpr): number { + if (this.expr.length < other.expr.length) { + return -1; + } + if (this.expr.length > other.expr.length) { + return 1; + } + for (let i = 0, len = this.expr.length; i < len; i++) { + const r = cmp(this.expr[i], other.expr[i]); + if (r !== 0) { + return r; + } + } + return 0; + } + public equals(other: ContextKeyExpr): boolean { if (other instanceof ContextKeyAndExpr) { if (this.expr.length !== other.expr.length) { @@ -500,16 +606,16 @@ export class ContextKeyAndExpr implements ContextKeyExpr { continue; } - e = e.normalize(); - if (!e) { - continue; - } - if (e instanceof ContextKeyAndExpr) { expr = expr.concat(e.expr); continue; } + if (e instanceof ContextKeyOrExpr) { + // Not allowed, because we don't have parens! + throw new Error(`It is not allowed to have an or expression here due to lack of parens!`); + } + expr.push(e); } @@ -519,29 +625,7 @@ export class ContextKeyAndExpr implements ContextKeyExpr { return expr; } - public normalize(): ContextKeyExpr | undefined { - if (this.expr.length === 0) { - return undefined; - } - - if (this.expr.length === 1) { - return this.expr[0]; - } - - return this; - } - public serialize(): string { - if (this.expr.length === 0) { - return ''; - } - if (this.expr.length === 1) { - const normalized = this.normalize(); - if (!normalized) { - return ''; - } - return normalized.serialize(); - } return this.expr.map(e => e.serialize()).join(' && '); } @@ -556,6 +640,132 @@ export class ContextKeyAndExpr implements ContextKeyExpr { public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { return new ContextKeyAndExpr(this.expr.map(expr => expr.map(mapFnc))); } + + public negate(): ContextKeyExpr { + let result: ContextKeyExpr[] = []; + for (let expr of this.expr) { + result.push(expr.negate()); + } + return ContextKeyOrExpr.create(result)!; + } +} + +export class ContextKeyOrExpr implements ContextKeyExpr { + + public static create(_expr: Array): ContextKeyExpr | undefined { + const expr = ContextKeyOrExpr._normalizeArr(_expr); + if (expr.length === 0) { + return undefined; + } + + if (expr.length === 1) { + return expr[0]; + } + + return new ContextKeyOrExpr(expr); + } + + private constructor(public readonly expr: ContextKeyExpr[]) { + } + + public getType(): ContextKeyExprType { + return ContextKeyExprType.Or; + } + + public equals(other: ContextKeyExpr): boolean { + if (other instanceof ContextKeyOrExpr) { + if (this.expr.length !== other.expr.length) { + return false; + } + for (let i = 0, len = this.expr.length; i < len; i++) { + if (!this.expr[i].equals(other.expr[i])) { + return false; + } + } + return true; + } + return false; + } + + public evaluate(context: IContext): boolean { + for (let i = 0, len = this.expr.length; i < len; i++) { + if (this.expr[i].evaluate(context)) { + return true; + } + } + return false; + } + + private static _normalizeArr(arr: Array): ContextKeyExpr[] { + let expr: ContextKeyExpr[] = []; + + if (arr) { + for (let i = 0, len = arr.length; i < len; i++) { + let e: ContextKeyExpr | null | undefined = arr[i]; + if (!e) { + continue; + } + + if (e instanceof ContextKeyOrExpr) { + expr = expr.concat(e.expr); + continue; + } + + expr.push(e); + } + + expr.sort(cmp); + } + + return expr; + } + + public serialize(): string { + return this.expr.map(e => e.serialize()).join(' || '); + } + + public keys(): string[] { + const result: string[] = []; + for (let expr of this.expr) { + result.push(...expr.keys()); + } + return result; + } + + public map(mapFnc: IContextKeyExprMapper): ContextKeyExpr { + return new ContextKeyOrExpr(this.expr.map(expr => expr.map(mapFnc))); + } + + public negate(): ContextKeyExpr { + let result: ContextKeyExpr[] = []; + for (let expr of this.expr) { + result.push(expr.negate()); + } + + const terminals = (node: ContextKeyExpr) => { + if (node instanceof ContextKeyOrExpr) { + return node.expr; + } + return [node]; + }; + + // We don't support parens, so here we distribute the AND over the OR terminals + // We always take the first 2 AND pairs and distribute them + while (result.length > 1) { + const LEFT = result.shift()!; + const RIGHT = result.shift()!; + + const all: ContextKeyExpr[] = []; + for (const left of terminals(LEFT)) { + for (const right of terminals(RIGHT)) { + all.push(ContextKeyExpr.and(left, right)!); + } + } + result.unshift(ContextKeyExpr.or(...all)!); + } + + return result[0]; + } } export class RawContextKey extends ContextKeyDefinedExpr { diff --git a/src/vs/platform/contextkey/test/common/contextkey.test.ts b/src/vs/platform/contextkey/test/common/contextkey.test.ts index cb12470e3c2..9db3782da31 100644 --- a/src/vs/platform/contextkey/test/common/contextkey.test.ts +++ b/src/vs/platform/contextkey/test/common/contextkey.test.ts @@ -27,7 +27,7 @@ suite('ContextKeyExpr', () => { ContextKeyExpr.notEquals('c2', 'cc2'), ContextKeyExpr.not('d1'), ContextKeyExpr.not('d2') - ); + )!; let b = ContextKeyExpr.and( ContextKeyExpr.equals('b2', 'bb2'), ContextKeyExpr.notEquals('c1', 'cc1'), @@ -40,7 +40,7 @@ suite('ContextKeyExpr', () => { ContextKeyExpr.has('a1'), ContextKeyExpr.and(ContextKeyExpr.equals('and.a', true)), ContextKeyExpr.not('d2') - ); + )!; assert(a.equals(b), 'expressions should be equal'); }); @@ -50,10 +50,10 @@ suite('ContextKeyExpr', () => { let key1IsFalse = ContextKeyExpr.equals('key1', false); let key1IsNotTrue = ContextKeyExpr.notEquals('key1', true); - assert.ok(key1IsTrue.normalize()!.equals(ContextKeyExpr.has('key1'))); - assert.ok(key1IsNotFalse.normalize()!.equals(ContextKeyExpr.has('key1'))); - assert.ok(key1IsFalse.normalize()!.equals(ContextKeyExpr.not('key1'))); - assert.ok(key1IsNotTrue.normalize()!.equals(ContextKeyExpr.not('key1'))); + assert.ok(key1IsTrue.equals(ContextKeyExpr.has('key1'))); + assert.ok(key1IsNotFalse.equals(ContextKeyExpr.has('key1'))); + assert.ok(key1IsFalse.equals(ContextKeyExpr.not('key1'))); + assert.ok(key1IsNotTrue.equals(ContextKeyExpr.not('key1'))); }); test('evaluate', () => { @@ -93,5 +93,24 @@ suite('ContextKeyExpr', () => { testExpression('a && !b && c == 5', true && !false && '5' == '5'); testExpression('d =~ /e.*/', false); /* tslint:enable:triple-equals */ + + // precedence test: false && true || true === true because && is evaluated first + testExpression('b && a || a', true); + + testExpression('a || b', true); + testExpression('b || b', false); + testExpression('b && a || a && b', false); + }); + + test('negate', () => { + function testNegate(expr: string, expected: string): void { + const actual = ContextKeyExpr.deserialize(expr)!.negate().serialize(); + assert.strictEqual(actual, expected); + } + testNegate('a', '!a'); + testNegate('a && b || c', '!a && !c || !b && !c'); + testNegate('a && b || c || d', '!a && !c && !d || !b && !c && !d'); + testNegate('!a && !b || !c && !d', 'a && c || a && d || b && c || b && d'); + testNegate('!a && !b || !c && !d || !e && !f', 'a && c && e || a && c && f || a && d && e || a && d && f || b && c && e || b && c && f || b && d && e || b && d && f'); }); }); diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index 1436cfa6604..29d559eab4d 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -6,7 +6,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { MenuRegistry } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; -import { ContextKeyAndExpr, ContextKeyExpr, IContext } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, IContext, ContextKeyOrExpr } from 'vs/platform/contextkey/common/contextkey'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { keys } from 'vs/base/common/map'; @@ -171,7 +171,6 @@ export class KeybindingResolver { /** * Returns true if it is provable `a` implies `b`. - * **Precondition**: Assumes `a` and `b` are normalized! */ public static whenIsEntirelyIncluded(a: ContextKeyExpr | null | undefined, b: ContextKeyExpr | null | undefined): boolean { if (!b) { @@ -181,26 +180,35 @@ export class KeybindingResolver { return false; } - const aExpressions: ContextKeyExpr[] = ((a instanceof ContextKeyAndExpr) ? a.expr : [a]); - const bExpressions: ContextKeyExpr[] = ((b instanceof ContextKeyAndExpr) ? b.expr : [b]); + return this._implies(a, b); + } - let aIndex = 0; - for (const bExpr of bExpressions) { - let bExprMatched = false; - while (!bExprMatched && aIndex < aExpressions.length) { - let aExpr = aExpressions[aIndex]; - if (aExpr.equals(bExpr)) { - bExprMatched = true; - } - aIndex++; + /** + * Returns true if it is provable `p` implies `q`. + */ + private static _implies(p: ContextKeyExpr, q: ContextKeyExpr): boolean { + const notP = p.negate(); + + const terminals = (node: ContextKeyExpr) => { + if (node instanceof ContextKeyOrExpr) { + return node.expr; } + return [node]; + }; - if (!bExprMatched) { - return false; + let expr = terminals(notP).concat(terminals(q)); + for (let i = 0; i < expr.length; i++) { + const a = expr[i]; + const notA = a.negate(); + for (let j = i + 1; j < expr.length; j++) { + const b = expr[j]; + if (notA.equals(b)) { + return true; + } } } - return true; + return false; } public getDefaultBoundCommands(): Map { diff --git a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts index a4488cffb17..c85003be1ac 100644 --- a/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingResolver.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { KeyChord, KeyCode, KeyMod, SimpleKeybinding, createKeybinding, createSimpleKeybinding } from 'vs/base/common/keyCodes'; import { OS } from 'vs/base/common/platform'; -import { ContextKeyAndExpr, ContextKeyExpr, IContext } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, IContext } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; @@ -20,13 +20,13 @@ function createContext(ctx: any) { suite('KeybindingResolver', () => { - function kbItem(keybinding: number, command: string, commandArgs: any, when: ContextKeyExpr, isDefault: boolean): ResolvedKeybindingItem { + function kbItem(keybinding: number, command: string, commandArgs: any, when: ContextKeyExpr | undefined, isDefault: boolean): ResolvedKeybindingItem { const resolvedKeybinding = (keybinding !== 0 ? new USLayoutResolvedKeybinding(createKeybinding(keybinding, OS)!, OS) : undefined); return new ResolvedKeybindingItem( resolvedKeybinding, command, commandArgs, - when ? when.normalize() : undefined, + when, isDefault ); } @@ -191,64 +191,41 @@ suite('KeybindingResolver', () => { }); test('contextIsEntirelyIncluded', () => { - let assertIsIncluded = (a: ContextKeyExpr[], b: ContextKeyExpr[]) => { - let tmpA = new ContextKeyAndExpr(a).normalize(); - let tmpB = new ContextKeyAndExpr(b).normalize(); - assert.equal(KeybindingResolver.whenIsEntirelyIncluded(tmpA, tmpB), true); + const assertIsIncluded = (a: string | null, b: string | null) => { + assert.equal(KeybindingResolver.whenIsEntirelyIncluded(ContextKeyExpr.deserialize(a), ContextKeyExpr.deserialize(b)), true); }; - let assertIsNotIncluded = (a: ContextKeyExpr[], b: ContextKeyExpr[]) => { - let tmpA = new ContextKeyAndExpr(a).normalize(); - let tmpB = new ContextKeyAndExpr(b).normalize(); - assert.equal(KeybindingResolver.whenIsEntirelyIncluded(tmpA, tmpB), false); + const assertIsNotIncluded = (a: string | null, b: string | null) => { + assert.equal(KeybindingResolver.whenIsEntirelyIncluded(ContextKeyExpr.deserialize(a), ContextKeyExpr.deserialize(b)), false); }; - let key1IsTrue = ContextKeyExpr.equals('key1', true); - let key1IsNotFalse = ContextKeyExpr.notEquals('key1', false); - let key1IsFalse = ContextKeyExpr.equals('key1', false); - let key1IsNotTrue = ContextKeyExpr.notEquals('key1', true); - let key2IsTrue = ContextKeyExpr.equals('key2', true); - let key2IsNotFalse = ContextKeyExpr.notEquals('key2', false); - let key3IsTrue = ContextKeyExpr.equals('key3', true); - let key4IsTrue = ContextKeyExpr.equals('key4', true); - assertIsIncluded([key1IsTrue], null!); - assertIsIncluded([key1IsTrue], []); - assertIsIncluded([key1IsTrue], [key1IsTrue]); - assertIsIncluded([key1IsTrue], [key1IsNotFalse]); + assertIsIncluded('key1', null); + assertIsIncluded('key1', ''); + assertIsIncluded('key1', 'key1'); + assertIsIncluded('!key1', ''); + assertIsIncluded('!key1', '!key1'); + assertIsIncluded('key2', ''); + assertIsIncluded('key2', 'key2'); + assertIsIncluded('key1 && key1 && key2 && key2', 'key2'); + assertIsIncluded('key1 && key2', 'key2'); + assertIsIncluded('key1 && key2', 'key1'); + assertIsIncluded('key1 && key2', ''); + assertIsIncluded('key1', 'key1 || key2'); + assertIsIncluded('key1 || !key1', 'key2 || !key2'); + assertIsIncluded('key1', 'key1 || key2 && key3'); - assertIsIncluded([key1IsFalse], []); - assertIsIncluded([key1IsFalse], [key1IsFalse]); - assertIsIncluded([key1IsFalse], [key1IsNotTrue]); - - assertIsIncluded([key2IsNotFalse], []); - assertIsIncluded([key2IsNotFalse], [key2IsNotFalse]); - assertIsIncluded([key2IsNotFalse], [key2IsTrue]); - - assertIsIncluded([key1IsTrue, key2IsNotFalse], [key2IsTrue]); - assertIsIncluded([key1IsTrue, key2IsNotFalse], [key2IsNotFalse]); - assertIsIncluded([key1IsTrue, key2IsNotFalse], [key1IsTrue]); - assertIsIncluded([key1IsTrue, key2IsNotFalse], [key1IsNotFalse]); - assertIsIncluded([key1IsTrue, key2IsNotFalse], []); - - assertIsNotIncluded([key1IsTrue], [key1IsFalse]); - assertIsNotIncluded([key1IsTrue], [key1IsNotTrue]); - assertIsNotIncluded([key1IsNotFalse], [key1IsFalse]); - assertIsNotIncluded([key1IsNotFalse], [key1IsNotTrue]); - - assertIsNotIncluded([key1IsFalse], [key1IsTrue]); - assertIsNotIncluded([key1IsFalse], [key1IsNotFalse]); - assertIsNotIncluded([key1IsNotTrue], [key1IsTrue]); - assertIsNotIncluded([key1IsNotTrue], [key1IsNotFalse]); - - assertIsNotIncluded([key1IsTrue, key2IsNotFalse], [key3IsTrue]); - assertIsNotIncluded([key1IsTrue, key2IsNotFalse], [key4IsTrue]); - assertIsNotIncluded([key1IsTrue], [key2IsTrue]); - assertIsNotIncluded([], [key2IsTrue]); - assertIsNotIncluded(null!, [key2IsTrue]); + assertIsNotIncluded('key1', '!key1'); + assertIsNotIncluded('!key1', 'key1'); + assertIsNotIncluded('key1 && key2', 'key3'); + assertIsNotIncluded('key1 && key2', 'key4'); + assertIsNotIncluded('key1', 'key2'); + assertIsNotIncluded('key1 || key2', 'key2'); + assertIsNotIncluded('', 'key2'); + assertIsNotIncluded(null, 'key2'); }); test('resolve command', function () { - function _kbItem(keybinding: number, command: string, when: ContextKeyExpr): ResolvedKeybindingItem { + function _kbItem(keybinding: number, command: string, when: ContextKeyExpr | undefined): ResolvedKeybindingItem { return kbItem(keybinding, command, null, when, true); } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 53629222ca3..354ef330025 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -448,7 +448,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands. interface IEditorToolItem { id: string; title: string; iconDark: string; iconLight: string; } -function appendEditorToolItem(primary: IEditorToolItem, when: ContextKeyExpr, order: number, alternative?: IEditorToolItem): void { +function appendEditorToolItem(primary: IEditorToolItem, when: ContextKeyExpr | undefined, order: number, alternative?: IEditorToolItem): void { const item: IMenuItem = { command: { id: primary.id, diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 84afaa1d2f1..9a1431da67e 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -534,7 +534,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { // Touch Bar if (isMacintosh) { - const registerTouchBarEntry = (id: string, title: string, order: number, when: ContextKeyExpr, icon: string) => { + const registerTouchBarEntry = (id: string, title: string, order: number, when: ContextKeyExpr | undefined, icon: string) => { MenuRegistry.appendMenuItem(MenuId.TouchBarContext, { command: { id, diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 044c0db7733..8b29304e715 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -203,7 +203,7 @@ appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, Re appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ContextKeyExpr.and(IsWebContext.toNegated(), ResourceContextKey.Scheme.isEqualTo(Schemas.userData))); appendEditorTitleContextMenuItem(REVEAL_IN_EXPLORER_COMMAND_ID, nls.localize('revealInSideBar', "Reveal in Side Bar"), ResourceContextKey.IsFileSystemResource); -function appendEditorTitleContextMenuItem(id: string, title: string, when: ContextKeyExpr, group?: string): void { +function appendEditorTitleContextMenuItem(id: string, title: string, when: ContextKeyExpr | undefined, group?: string): void { // Menu MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 4e64ebdc0c6..dec59cfca2c 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -300,7 +300,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { private _resolveKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { let result: ResolvedKeybindingItem[] = [], resultLen = 0; for (const item of items) { - const when = (item.when ? item.when.normalize() : undefined); + const when = item.when || undefined; const keybinding = item.keybinding; if (!keybinding) { // This might be a removal keybinding item in user settings => accept it @@ -323,7 +323,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { private _resolveUserKeybindingItems(items: IUserKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { let result: ResolvedKeybindingItem[] = [], resultLen = 0; for (const item of items) { - const when = (item.when ? item.when.normalize() : undefined); + const when = item.when || undefined; const parts = item.parts; if (parts.length === 0) { // This might be a removal keybinding item in user settings => accept it From 141617c5b5793b2f7313e8295581afdf88480014 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 26 Jul 2019 16:37:31 +0200 Subject: [PATCH 36/48] Remove dimmed auto closed character --- src/vs/editor/browser/widget/media/editor.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/editor/browser/widget/media/editor.css b/src/vs/editor/browser/widget/media/editor.css index 83c0859bd29..fb9e5f5e8b8 100644 --- a/src/vs/editor/browser/widget/media/editor.css +++ b/src/vs/editor/browser/widget/media/editor.css @@ -41,6 +41,8 @@ top: 0; } +/* .monaco-editor .auto-closed-character { opacity: 0.3; } +*/ From f9be8224e709c60d94d50eeb2086ccc41909749d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 26 Jul 2019 16:51:22 +0200 Subject: [PATCH 37/48] tweak jsdoc, #56694 --- src/vs/vscode.proposed.d.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index d410fcedffd..fc19f4d513a 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1295,10 +1295,9 @@ declare module 'vscode' { export enum DiagnosticTag { /** - * Deprecated or obsolete code + * Deprecated or obsolete code. * - * Can be used to style with strikeout or other "obsolete" styling. See: - * https://github.com/microsoft/vscode/issues/50972 + * Diagnostics with this tag are rendered with a strike through. */ Deprecated = 2, } From 55c8530ec421e0766f1032830576965169083275 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Fri, 26 Jul 2019 17:11:58 +0200 Subject: [PATCH 38/48] properly pass non-key/value cli arguments; fixes #76989 --- src/vs/workbench/contrib/debug/browser/rawDebugSession.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index b67785c3d98..a0f2120b920 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -587,7 +587,13 @@ export class RawDebugSession { } } else { - args._.push(a2); + const match = /^--(.+)$/.exec(a2); + if (match && match.length === 2) { + const key = match[1]; + (args)[key] = true; + } else { + args._.push(a2); + } } } } From 1c6047158b540dd5e38e75bf9793bc9527d26e4b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 26 Jul 2019 17:16:04 +0200 Subject: [PATCH 39/48] distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f973590748b..709608cbc69 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.37.0", - "distro": "ecf90e8c66d243c7ac15e30c24b3cc437d0700dc", + "distro": "7a8caddf61783ec1867857444d613a317aca500a", "author": { "name": "Microsoft Corporation" }, @@ -158,4 +158,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} +} \ No newline at end of file From 1545b1bb59c8cd266365011b2204b2a98deb8163 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 23 Jul 2019 12:54:45 +0200 Subject: [PATCH 40/48] allow extension tips based on location --- src/vs/platform/product/common/product.ts | 2 +- .../electron-browser/extensionTipsService.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 45c31ca0f3d..b5451aea32c 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -72,7 +72,7 @@ export interface IProductConfiguration { readonly recommendationsUrl: string; }; extensionTips: { [id: string]: string; }; - extensionImportantTips: { [id: string]: { name: string; pattern: string; }; }; + extensionImportantTips: { [id: string]: { name: string; pattern: string; isLocationPattern?: boolean, isExtensionPack?: boolean }; }; readonly exeBasedExtensionTips: { [id: string]: { friendlyName: string, windowsPath?: string, recommendations: readonly string[] }; }; readonly extensionKeywords: { [extension: string]: readonly string[]; }; readonly extensionAllowedBadgeProviders: readonly string[]; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts index f4af7a1c9b6..1b08aea0f93 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts @@ -597,7 +597,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe const now = Date.now(); forEach(this._availableRecommendations, entry => { let { key: pattern, value: ids } = entry; - if (match(pattern, model.uri.path)) { + if (match(pattern, model.uri.toString())) { for (let id of ids) { if (caseInsensitiveGet(product.extensionImportantTips, id)) { recommendationsToSuggest.push(id); @@ -670,12 +670,12 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (!entry) { return false; } - const name = entry['name']; - + const name = entry.name; let message = localize('reallyRecommended2', "The '{0}' extension is recommended for this file type.", name); - // Temporary fix for the only extension pack we recommend. See https://github.com/Microsoft/vscode/issues/35364 - if (id === 'vscjava.vscode-java-pack') { + if (entry.isExtensionPack) { message = localize('reallyRecommendedExtensionPack', "The '{0}' extension pack is recommended for this file type.", name); + } else if (entry.isLocationPattern) { + message = localize('reallyRecommendedLocationPattern', "The '{0}' extension is recommended for files at this location.", name); } const setIgnoreRecommendationsConfig = (configVal: boolean) => { From 5cc02113634f9ab8ae9ba4adb00d29dcff14a2c2 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 26 Jul 2019 18:02:52 +0200 Subject: [PATCH 41/48] recommend important exe extensions --- src/vs/platform/product/common/product.ts | 12 +- .../extensions/browser/extensionsActions.ts | 2 +- .../electron-browser/extensionTipsService.ts | 219 +++++++++++++----- 3 files changed, 175 insertions(+), 58 deletions(-) diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index b5451aea32c..d39dd65c08f 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -72,8 +72,8 @@ export interface IProductConfiguration { readonly recommendationsUrl: string; }; extensionTips: { [id: string]: string; }; - extensionImportantTips: { [id: string]: { name: string; pattern: string; isLocationPattern?: boolean, isExtensionPack?: boolean }; }; - readonly exeBasedExtensionTips: { [id: string]: { friendlyName: string, windowsPath?: string, recommendations: readonly string[] }; }; + extensionImportantTips: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; }; + readonly exeBasedExtensionTips: { [id: string]: IExeBasedExtensionTip; }; readonly extensionKeywords: { [extension: string]: readonly string[]; }; readonly extensionAllowedBadgeProviders: readonly string[]; readonly extensionAllowedProposedApi: readonly string[]; @@ -120,6 +120,14 @@ export interface IProductConfiguration { readonly uiExtensions?: readonly string[]; } +export interface IExeBasedExtensionTip { + friendlyName: string; + windowsPath?: string; + recommendations: readonly string[]; + important?: boolean; + exeFriendlyName?: string; +} + export interface ISurveyData { surveyId: string; surveyUrl: string; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index df4be0957a3..fdb0056d228 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1694,7 +1694,7 @@ export class InstallRecommendedExtensionAction extends Action { return this.viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { - viewlet.search('@recommended '); + viewlet.search(`@id:${this.extensionId}`); viewlet.focus(); return this.extensionWorkbenchService.queryGallery({ names: [this.extensionId], source: 'install-recommendation', pageSize: 1 }, CancellationToken.None) .then(pager => { diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts index 1b08aea0f93..b64e6d66977 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionTipsService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { join } from 'vs/base/common/path'; +import { join, basename } from 'vs/base/common/path'; import { forEach } from 'vs/base/common/collections'; import { Disposable } from 'vs/base/common/lifecycle'; import { match } from 'vs/base/common/glob'; @@ -43,6 +43,8 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { extname } from 'vs/base/common/resources'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IExeBasedExtensionTip } from 'vs/platform/product/common/product'; +import { timeout } from 'vs/base/common/async'; const milliSecondsInADay = 1000 * 60 * 60 * 24; const choiceNever = localize('neverShowAgain', "Don't Show Again"); @@ -71,7 +73,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe _serviceBrand: any; private _fileBasedRecommendations: { [id: string]: { recommendedTime: number, sources: ExtensionRecommendationSource[] }; } = Object.create(null); - private _exeBasedRecommendations: { [id: string]: string; } = Object.create(null); + private _exeBasedRecommendations: { [id: string]: IExeBasedExtensionTip; } = Object.create(null); + private _importantExeBasedRecommendations: { [id: string]: IExeBasedExtensionTip; } = Object.create(null); private _availableRecommendations: { [pattern: string]: string[] } = Object.create(null); private _allWorkspaceRecommendedExtensions: IExtensionRecommendation[] = []; private _dynamicWorkspaceRecommendations: string[] = []; @@ -187,7 +190,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe forEach(this._exeBasedRecommendations, entry => output[entry.key.toLowerCase()] = { reasonId: ExtensionRecommendationReason.Executable, - reasonText: localize('exeBasedRecommendation', "This extension is recommended because you have {0} installed.", entry.value) + reasonText: localize('exeBasedRecommendation', "This extension is recommended because you have {0} installed.", entry.value.friendlyName) }); forEach(this._fileBasedRecommendations, entry => output[entry.key.toLowerCase()] = { @@ -496,6 +499,100 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe //#endregion + //#region important exe based extension + + private async promptForImportantExeBasedExtension(): Promise { + + const storageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; + const config = this.configurationService.getValue(ConfigurationKey); + + if (config.ignoreRecommendations + || config.showRecommendationsOnlyOnDemand + || this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) { + return false; + } + + const installed = await this.extensionManagementService.getInstalled(ExtensionType.User); + let recommendationsToSuggest = Object.keys(this._importantExeBasedRecommendations); + recommendationsToSuggest = this.filterAllIgnoredInstalledAndNotAllowed(recommendationsToSuggest, installed); + if (recommendationsToSuggest.length === 0) { + return false; + } + + const id = recommendationsToSuggest[0]; + const tip = this._importantExeBasedRecommendations[id]; + const message = localize('exeRecommended', "The '{0}' extension is recommended as you have {1} installed on your system.", tip.friendlyName!, tip.exeFriendlyName || basename(tip.windowsPath!)); + + this.notificationService.prompt(Severity.Info, message, + [{ + label: localize('install', 'Install'), + run: () => { + /* __GDPR__ + "exeExtensionRecommendations:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'install', extensionId: name }); + this.instantiationService.createInstance(InstallRecommendedExtensionAction, id).run(); + } + }, { + label: localize('showRecommendations', "Show Recommendations"), + run: () => { + /* __GDPR__ + "exeExtensionRecommendations:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'show', extensionId: name }); + + const recommendationsAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); + recommendationsAction.run(); + recommendationsAction.dispose(); + } + }, { + label: choiceNever, + isSecondary: true, + run: () => { + this.addToImportantRecommendationsIgnore(id); + /* __GDPR__ + "exeExtensionRecommendations:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: name }); + this.notificationService.prompt( + Severity.Info, + localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), + [{ + label: localize('ignoreAll', "Yes, Ignore All"), + run: () => this.setIgnoreRecommendationsConfig(true) + }, { + label: localize('no', "No"), + run: () => this.setIgnoreRecommendationsConfig(false) + }] + ); + } + }], + { + sticky: true, + onCancel: () => { + /* __GDPR__ + "exeExtensionRecommendations:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'cancelled', extensionId: name }); + } + } + ); + + return true; + } + //#region fileBasedRecommendations getFileBasedRecommendations(): IExtensionRecommendation[] { @@ -646,21 +743,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } private async promptRecommendedExtensionForFileType(recommendationsToSuggest: string[], installed: ILocalExtension[]): Promise { - const importantRecommendationsIgnoreList = JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]')); - const installedExtensionsIds = installed.reduce((result, i) => { result.add(i.identifier.id.toLowerCase()); return result; }, new Set()); - recommendationsToSuggest = recommendationsToSuggest.filter(id => { - if (importantRecommendationsIgnoreList.indexOf(id) !== -1) { - return false; - } - if (!this.isExtensionAllowedToBeRecommended(id)) { - return false; - } - if (installedExtensionsIds.has(id.toLowerCase())) { - return false; - } - return true; - }); + recommendationsToSuggest = this.filterAllIgnoredInstalledAndNotAllowed(recommendationsToSuggest, installed); if (recommendationsToSuggest.length === 0) { return false; } @@ -674,18 +758,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe let message = localize('reallyRecommended2', "The '{0}' extension is recommended for this file type.", name); if (entry.isExtensionPack) { message = localize('reallyRecommendedExtensionPack', "The '{0}' extension pack is recommended for this file type.", name); - } else if (entry.isLocationPattern) { - message = localize('reallyRecommendedLocationPattern', "The '{0}' extension is recommended for files at this location.", name); } - const setIgnoreRecommendationsConfig = (configVal: boolean) => { - this.configurationService.updateValue('extensions.ignoreRecommendations', configVal, ConfigurationTarget.USER); - if (configVal) { - const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; - this.storageService.store(ignoreWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); - } - }; - this.notificationService.prompt(Severity.Info, message, [{ label: localize('install', 'Install'), @@ -718,12 +792,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe label: choiceNever, isSecondary: true, run: () => { - importantRecommendationsIgnoreList.push(id); - this.storageService.store( - 'extensionsAssistant/importantRecommendationsIgnore', - JSON.stringify(importantRecommendationsIgnoreList), - StorageScope.GLOBAL - ); + this.addToImportantRecommendationsIgnore(id); /* __GDPR__ "extensionRecommendations:popup" : { "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, @@ -736,10 +805,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), [{ label: localize('ignoreAll', "Yes, Ignore All"), - run: () => setIgnoreRecommendationsConfig(true) + run: () => this.setIgnoreRecommendationsConfig(true) }, { label: localize('no', "No"), - run: () => setIgnoreRecommendationsConfig(false) + run: () => this.setIgnoreRecommendationsConfig(false) }] ); } @@ -831,6 +900,42 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe ); } + private filterAllIgnoredInstalledAndNotAllowed(recommendationsToSuggest: string[], installed: ILocalExtension[]): string[] { + + const importantRecommendationsIgnoreList = JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]')); + const installedExtensionsIds = installed.reduce((result, i) => { result.add(i.identifier.id.toLowerCase()); return result; }, new Set()); + return recommendationsToSuggest.filter(id => { + if (importantRecommendationsIgnoreList.indexOf(id) !== -1) { + return false; + } + if (!this.isExtensionAllowedToBeRecommended(id)) { + return false; + } + if (installedExtensionsIds.has(id.toLowerCase())) { + return false; + } + return true; + }); + } + + private addToImportantRecommendationsIgnore(id: string) { + const importantRecommendationsIgnoreList = JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]')); + importantRecommendationsIgnoreList.push(id); + this.storageService.store( + 'extensionsAssistant/importantRecommendationsIgnore', + JSON.stringify(importantRecommendationsIgnoreList), + StorageScope.GLOBAL + ); + } + + private setIgnoreRecommendationsConfig(configVal: boolean) { + this.configurationService.updateValue('extensions.ignoreRecommendations', configVal, ConfigurationTarget.USER); + if (configVal) { + const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; + this.storageService.store(ignoreWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); + } + } + //#endregion //#region otherRecommendations @@ -857,18 +962,18 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } private fetchProactiveRecommendations(calledDuringStartup?: boolean): Promise { - let fetchPromise = Promise.resolve(undefined); + let fetchPromise = Promise.resolve(undefined); if (!this.proactiveRecommendationsFetched) { this.proactiveRecommendationsFetched = true; - // Executable based recommendations carry out a lot of file stats, so run them after 10 secs - // So that the startup is not affected + // Executable based recommendations carry out a lot of file stats, delay the resolution so that the startup is not affected + // 10 sec for regular extensions + // 3 secs for important - fetchPromise = new Promise((c, e) => { - setTimeout(() => { - Promise.all([this.fetchExecutableRecommendations(), this.fetchDynamicWorkspaceRecommendations()]).then(() => c(undefined)); - }, calledDuringStartup ? 10000 : 0); - }); + const importantExeBasedRecommendations = timeout(calledDuringStartup ? 3000 : 0).then(_ => this.fetchExecutableRecommendations(true)); + importantExeBasedRecommendations.then(_ => this.promptForImportantExeBasedExtension()); + + fetchPromise = timeout(calledDuringStartup ? 10000 : 0).then(_ => Promise.all([this.fetchDynamicWorkspaceRecommendations(), this.fetchExecutableRecommendations(false), importantExeBasedRecommendations])); } return fetchPromise; @@ -877,20 +982,22 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe /** * If user has any of the tools listed in product.exeBasedExtensionTips, fetch corresponding recommendations */ - private fetchExecutableRecommendations(): Promise { + private fetchExecutableRecommendations(important: boolean): Promise { const homeDir = os.homedir(); let foundExecutables: Set = new Set(); - let findExecutable = (exeName: string, path: string) => { + let findExecutable = (exeName: string, tip: IExeBasedExtensionTip, path: string) => { return pfs.fileExists(path).then(exists => { if (exists && !foundExecutables.has(exeName)) { foundExecutables.add(exeName); - (product.exeBasedExtensionTips[exeName]['recommendations'] || []) - .forEach(extensionId => { - if (product.exeBasedExtensionTips[exeName]['friendlyName']) { - this._exeBasedRecommendations[extensionId.toLowerCase()] = product.exeBasedExtensionTips[exeName]['friendlyName']; + (tip['recommendations'] || []).forEach(extensionId => { + if (tip.friendlyName) { + if (important) { + this._importantExeBasedRecommendations[extensionId.toLowerCase()] = tip; } - }); + this._exeBasedRecommendations[extensionId.toLowerCase()] = tip; + } + }); } }); }; @@ -901,8 +1008,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (typeof entry.value !== 'object' || !Array.isArray(entry.value['recommendations'])) { return; } - - let exeName = entry.key; + if (important !== !!entry.value.important) { + return; + } + const exeName = entry.key; if (process.platform === 'win32') { let windowsPath = entry.value['windowsPath']; if (!windowsPath || typeof windowsPath !== 'string') { @@ -913,10 +1022,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe .replace('%ProgramFiles%', process.env['ProgramFiles']!) .replace('%APPDATA%', process.env['APPDATA']!) .replace('%WINDIR%', process.env['WINDIR']!); - promises.push(findExecutable(exeName, windowsPath)); + promises.push(findExecutable(exeName, entry.value, windowsPath)); } else { - promises.push(findExecutable(exeName, join('/usr/local/bin', exeName))); - promises.push(findExecutable(exeName, join(homeDir, exeName))); + promises.push(findExecutable(exeName, entry.value, join('/usr/local/bin', exeName))); + promises.push(findExecutable(exeName, entry.value, join(homeDir, exeName))); } }); From 916287fdd467e3b4a1bf431ac80aa36eddf4bbdb Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 26 Jul 2019 16:26:58 +0200 Subject: [PATCH 42/48] remove EditorPart._preferredSize related to #67367 --- .../browser/parts/editor/editorPart.ts | 34 ------------------- .../test/browser/editorGroupsService.test.ts | 11 ------ 2 files changed, 45 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index a42cf0aed2f..53611687ce5 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -112,13 +112,8 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro private _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; } | undefined>()); get onDidSizeConstraintsChange(): Event<{ width: number; height: number; } | undefined> { return Event.any(this.onDidSetGridWidget.event, this._onDidSizeConstraintsChange.event); } - private readonly _onDidPreferredSizeChange: Emitter = this._register(new Emitter()); - readonly onDidPreferredSizeChange: Event = this._onDidPreferredSizeChange.event; - //#endregion - private _preferredSize: Dimension | undefined; - private readonly workspaceMemento: MementoObject; private readonly globalMemento: MementoObject; @@ -366,9 +361,6 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro const newOrientation = (orientation === GroupOrientation.HORIZONTAL) ? Orientation.HORIZONTAL : Orientation.VERTICAL; if (this.gridWidget.orientation !== newOrientation) { this.gridWidget.orientation = newOrientation; - - // Mark preferred size as changed - this.resetPreferredSize(); } } @@ -423,9 +415,6 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro // Update container this.updateContainer(); - // Mark preferred size as changed - this.resetPreferredSize(); - // Events for groups that got added this.getGroups(GroupsOrder.GRID_APPEARANCE).forEach(groupView => { if (currentGroupViews.indexOf(groupView) === -1) { @@ -490,9 +479,6 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro // Update container this.updateContainer(); - // Mark preferred size as changed - this.resetPreferredSize(); - // Event this._onDidAddGroup.fire(newGroupView); @@ -661,9 +647,6 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro // Update container this.updateContainer(); - // Mark preferred size as changed - this.resetPreferredSize(); - // Event this._onDidRemoveGroup.fire(groupView); } @@ -764,23 +747,6 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro get onDidChange(): Event { return this.centeredLayoutWidget.onDidChange; } readonly priority: LayoutPriority = LayoutPriority.High; - get preferredSize(): Dimension { - if (!this._preferredSize) { - this._preferredSize = new Dimension(this.gridWidget.minimumWidth, this.gridWidget.minimumHeight); - } - - return this._preferredSize; - } - - private resetPreferredSize(): void { - - // Reset (will be computed upon next access) - this._preferredSize = undefined; - - // Event - this._onDidPreferredSizeChange.fire(); - } - private get gridSeparatorBorder(): Color { return this.theme.getColor(EDITOR_GROUP_BORDER) || this.theme.getColor(contrastBorder) || Color.transparent; } 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 b577d40f181..477fdfc5bc7 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -118,11 +118,6 @@ suite('EditorGroupsService', () => { groupMovedCounter++; }); - let preferredSizeChangeCounter = 0; - const preferredSizeChangeListener = part.onDidPreferredSizeChange(() => { - preferredSizeChangeCounter++; - }); - // always a root group const rootGroup = part.groups[0]; assert.equal(part.groups.length, 1); @@ -141,7 +136,6 @@ suite('EditorGroupsService', () => { assert.equal(part.groups.length, 2); assert.equal(part.count, 2); assert.ok(part.activeGroup === rootGroup); - assert.equal(preferredSizeChangeCounter, 1); assert.equal(rootGroup.label, 'Group 1'); assert.equal(rightGroup.label, 'Group 2'); @@ -189,7 +183,6 @@ suite('EditorGroupsService', () => { assert.equal(part.groups.length, 3); assert.ok(part.activeGroup === rightGroup); assert.ok(!downGroup.activeControl); - assert.equal(preferredSizeChangeCounter, 2); assert.equal(rootGroup.label, 'Group 1'); assert.equal(rightGroup.label, 'Group 2'); assert.equal(downGroup.label, 'Group 3'); @@ -208,13 +201,11 @@ suite('EditorGroupsService', () => { part.moveGroup(downGroup, rightGroup, GroupDirection.DOWN); assert.equal(groupMovedCounter, 1); - assert.equal(preferredSizeChangeCounter, 2); part.removeGroup(downGroup); assert.ok(!part.getGroup(downGroup.id)); assert.equal(didDispose, true); assert.equal(groupRemovedCounter, 1); - assert.equal(preferredSizeChangeCounter, 3); assert.equal(part.groups.length, 2); assert.ok(part.activeGroup === rightGroup); assert.equal(rootGroup.label, 'Group 1'); @@ -254,13 +245,11 @@ suite('EditorGroupsService', () => { assert.ok(part.activeGroup === rootGroup); part.setGroupOrientation(part.orientation === GroupOrientation.HORIZONTAL ? GroupOrientation.VERTICAL : GroupOrientation.HORIZONTAL); - assert.equal(preferredSizeChangeCounter, 5); activeGroupChangeListener.dispose(); groupAddedListener.dispose(); groupRemovedListener.dispose(); groupMovedListener.dispose(); - preferredSizeChangeListener.dispose(); part.dispose(); }); From dcdab4125be3ed7b121078b7a5efe1ebe0be9946 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 26 Jul 2019 16:53:59 +0200 Subject: [PATCH 43/48] hide away gridview types --- .../base/browser/ui/centered/centeredViewLayout.ts | 2 +- src/vs/base/browser/ui/grid/grid.ts | 12 ++++++++---- src/vs/base/test/browser/ui/grid/util.ts | 5 +++-- src/vs/workbench/browser/part.ts | 5 ++--- src/vs/workbench/browser/parts/editor/editorPart.ts | 5 ++--- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index b40384018f1..299b6534873 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -6,7 +6,7 @@ import { SplitView, Orientation, ISplitViewStyles, IView as ISplitViewView } from 'vs/base/browser/ui/splitview/splitview'; import { $ } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; -import { IView, IViewSize } from 'vs/base/browser/ui/grid/gridview'; +import { IView, IViewSize } from 'vs/base/browser/ui/grid/grid'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 08090875642..7b38753725d 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -7,11 +7,11 @@ import 'vs/css!./gridview'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { Disposable } from 'vs/base/common/lifecycle'; import { tail2 as tail, equals } from 'vs/base/common/arrays'; -import { orthogonal, IView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize } from './gridview'; +import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize } from './gridview'; import { Event } from 'vs/base/common/event'; import { InvisibleSizing } from 'vs/base/browser/ui/splitview/splitview'; -export { Orientation, Sizing as GridViewSizing } from './gridview'; +export { Orientation, Sizing as GridViewSizing, IViewSize, orthogonal, LayoutPriority } from './gridview'; export const enum Direction { Up, @@ -29,6 +29,10 @@ function oppositeDirection(direction: Direction): Direction { } } +export interface IView extends IGridViewView { + readonly preferredSize?: number; +} + export interface GridLeafNode { readonly view: T; readonly box: Box; @@ -217,7 +221,7 @@ export class Grid extends Disposable { this.gridview = new GridView(options); this._register(this.gridview); - this._register(this.gridview.onDidSashReset(this.doResetViewSize, this)); + this._register(this.gridview.onDidSashReset(this.onDidSashReset, this)); const size: number | GridViewSizing = typeof options.firstViewVisibleCachedSize === 'number' ? GridViewSizing.Invisible(options.firstViewVisibleCachedSize) @@ -370,7 +374,7 @@ export class Grid extends Disposable { return getGridLocation(element); } - private doResetViewSize(location: number[]): void { + private onDidSashReset(location: number[]): void { const [parentLocation,] = tail(location); this.gridview.distributeViewSizes(parentLocation); } diff --git a/src/vs/base/test/browser/ui/grid/util.ts b/src/vs/base/test/browser/ui/grid/util.ts index 0efdc44851a..39a35736ddd 100644 --- a/src/vs/base/test/browser/ui/grid/util.ts +++ b/src/vs/base/test/browser/ui/grid/util.ts @@ -5,7 +5,8 @@ import * as assert from 'assert'; import { Emitter, Event } from 'vs/base/common/event'; -import { IView, GridNode, isGridBranchNode, } from 'vs/base/browser/ui/grid/gridview'; +import { GridNode, isGridBranchNode } from 'vs/base/browser/ui/grid/gridview'; +import { IView } from 'vs/base/browser/ui/grid/grid'; export class TestView implements IView { @@ -78,4 +79,4 @@ export function nodesToArrays(node: GridNode): any { } else { return node.view; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/part.ts b/src/vs/workbench/browser/part.ts index f80e4545957..f5281df2802 100644 --- a/src/vs/workbench/browser/part.ts +++ b/src/vs/workbench/browser/part.ts @@ -9,10 +9,9 @@ import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { Dimension, size } from 'vs/base/browser/dom'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IDimension } from 'vs/platform/layout/browser/layoutService'; -import { ISerializableView, Orientation } from 'vs/base/browser/ui/grid/grid'; +import { ISerializableView, Orientation, IViewSize } from 'vs/base/browser/ui/grid/grid'; import { Event, Emitter } from 'vs/base/common/event'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { IViewSize } from 'vs/base/browser/ui/grid/gridview'; export interface IPartOptions { hasTitle?: boolean; @@ -164,4 +163,4 @@ class PartLayout { return { titleSize, contentSize }; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 53611687ce5..84b899658bc 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -11,7 +11,7 @@ import { Event, Emitter, Relay } from 'vs/base/common/event'; import { contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, ICopyEditorOptions, GroupsOrder, GroupChangeKind, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid'; +import { IView, orthogonal, LayoutPriority, IViewSize, Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid'; import { GroupIdentifier, IWorkbenchEditorConfiguration, IEditorPartOptions } from 'vs/workbench/common/editor'; import { values } from 'vs/base/common/map'; import { EDITOR_GROUP_BORDER, EDITOR_PANE_BACKGROUND } from 'vs/workbench/common/theme'; @@ -27,7 +27,6 @@ import { EditorDropTarget } from 'vs/workbench/browser/parts/editor/editorDropTa import { localize } from 'vs/nls'; import { Color } from 'vs/base/common/color'; import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout'; -import { IView, orthogonal, LayoutPriority, IViewSize } from 'vs/base/browser/ui/grid/gridview'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -994,4 +993,4 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro } } -registerSingleton(IEditorGroupsService, EditorPart); \ No newline at end of file +registerSingleton(IEditorGroupsService, EditorPart); From 6cfe0672e7f981a066a0ecaf09eef690e52153af Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 26 Jul 2019 17:28:06 +0200 Subject: [PATCH 44/48] splitview: improve sash reset and distributeViewSizes behavior --- src/vs/base/browser/ui/splitview/splitview.ts | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 171a44837c4..6f031dac570 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -336,7 +336,24 @@ export class SplitView extends Disposable { const onChangeDisposable = onChange(this.onSashChange, this); const onEnd = Event.map(sash.onDidEnd, () => firstIndex(this.sashItems, item => item.sash === sash)); const onEndDisposable = onEnd(this.onSashEnd, this); - const onDidResetDisposable = sash.onDidReset(() => this._onDidSashReset.fire(firstIndex(this.sashItems, item => item.sash === sash))); + + const onDidResetDisposable = sash.onDidReset(() => { + const index = firstIndex(this.sashItems, item => item.sash === sash); + const upIndexes = range(index, -1); + const downIndexes = range(index + 1, this.viewItems.length); + const snapBeforeIndex = this.findFirstSnapIndex(upIndexes); + const snapAfterIndex = this.findFirstSnapIndex(downIndexes); + + if (typeof snapBeforeIndex === 'number' && !this.viewItems[snapBeforeIndex].visible) { + return; + } + + if (typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible) { + return; + } + + this._onDidSashReset.fire(index); + }); const disposable = combinedDisposable(onStartDisposable, onChangeDisposable, onEndDisposable, onDidResetDisposable, sash); const sashItem: ISashItem = { sash, disposable }; @@ -630,10 +647,19 @@ export class SplitView extends Disposable { } distributeViewSizes(): void { - const size = Math.floor(this.size / this.viewItems.length); + const flexibleViewItems: ViewItem[] = []; + let flexibleSize = 0; - for (let i = 0; i < this.viewItems.length; i++) { - const item = this.viewItems[i]; + for (const item of this.viewItems) { + if (item.maximumSize - item.minimumSize > 0) { + flexibleViewItems.push(item); + flexibleSize += item.size; + } + } + + const size = Math.floor(flexibleSize / flexibleViewItems.length); + + for (const item of flexibleViewItems) { item.size = clamp(size, item.minimumSize, item.maximumSize); } From 5d228fb849a1cc0eefc071079334050f79cef41e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 26 Jul 2019 17:28:40 +0200 Subject: [PATCH 45/48] grid: onDidSashReset with preferred width/height --- src/vs/base/browser/ui/grid/grid.ts | 36 +++++++++++++++++-- src/vs/base/browser/ui/grid/gridview.ts | 7 ++-- .../test/browser/ui/grid/gridview.test.ts | 22 ++++++------ 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 7b38753725d..386503317c4 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -30,7 +30,8 @@ function oppositeDirection(direction: Direction): Direction { } export interface IView extends IGridViewView { - readonly preferredSize?: number; + readonly preferredHeight?: number; + readonly preferredWidth?: number; } export interface GridLeafNode { @@ -339,7 +340,7 @@ export class Grid extends Disposable { } getViews(): GridBranchNode { - return this.gridview.getViews() as GridBranchNode; + return this.gridview.getView() as GridBranchNode; } getNeighborViews(view: T, direction: Direction, wrap: boolean = false): T[] { @@ -375,7 +376,36 @@ export class Grid extends Disposable { } private onDidSashReset(location: number[]): void { - const [parentLocation,] = tail(location); + const resizeToPreferredSize = (node: GridNode): boolean => { + if (isGridBranchNode(node)) { + return false; + } + + const direction = getLocationOrientation(this.orientation, location); + const size = direction === Orientation.HORIZONTAL ? node.view.preferredWidth : node.view.preferredHeight; + + if (typeof size !== 'number') { + return false; + } + + const viewSize = Orientation.HORIZONTAL ? { width: size } : { height: size }; + this.gridview.resizeView(location, viewSize); + return true; + }; + + let node = this.gridview.getView(location) as GridNode; + + if (resizeToPreferredSize(node)) { + return; + } + + const [parentLocation, index] = tail(location); + node = this.gridview.getView([...parentLocation, index + 1]) as GridNode; + + if (resizeToPreferredSize(node)) { + return; + } + this.gridview.distributeViewSizes(parentLocation); } } diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 3e05e4a9499..f6e65de3a14 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -903,8 +903,11 @@ export class GridView implements IDisposable { parent.setChildVisible(index, visible); } - getViews(): GridBranchNode { - return this._getViews(this.root, this.orientation, { top: 0, left: 0, width: this.width, height: this.height }) as GridBranchNode; + getView(): GridBranchNode; + getView(location?: number[]): GridNode; + getView(location?: number[]): GridNode { + const node = location ? this.getNode(location)[1] : this._root; + return this._getViews(node, this.orientation, { top: 0, left: 0, width: this.width, height: this.height }); } private _getViews(node: Node, orientation: Orientation, box: Box): GridNode { diff --git a/src/vs/base/test/browser/ui/grid/gridview.test.ts b/src/vs/base/test/browser/ui/grid/gridview.test.ts index 20b7626b184..78cc44cc76a 100644 --- a/src/vs/base/test/browser/ui/grid/gridview.test.ts +++ b/src/vs/base/test/browser/ui/grid/gridview.test.ts @@ -22,7 +22,7 @@ suite('Gridview', function () { }); test('empty gridview is empty', function () { - assert.deepEqual(nodesToArrays(gridview.getViews()), []); + assert.deepEqual(nodesToArrays(gridview.getView()), []); gridview.dispose(); }); @@ -43,7 +43,7 @@ suite('Gridview', function () { gridview.addView(views[1], 200, [1]); gridview.addView(views[2], 200, [2]); - assert.deepEqual(nodesToArrays(gridview.getViews()), views); + assert.deepEqual(nodesToArrays(gridview.getView()), views); gridview.dispose(); }); @@ -62,7 +62,7 @@ suite('Gridview', function () { gridview.addView((views[1] as TestView[])[0] as IView, 200, [1]); gridview.addView((views[1] as TestView[])[1] as IView, 200, [1, 1]); - assert.deepEqual(nodesToArrays(gridview.getViews()), views); + assert.deepEqual(nodesToArrays(gridview.getView()), views); gridview.dispose(); }); @@ -71,35 +71,35 @@ suite('Gridview', function () { const view1 = new TestView(20, 20, 20, 20); gridview.addView(view1 as IView, 200, [0]); - assert.deepEqual(nodesToArrays(gridview.getViews()), [view1]); + assert.deepEqual(nodesToArrays(gridview.getView()), [view1]); const view2 = new TestView(20, 20, 20, 20); gridview.addView(view2 as IView, 200, [1]); - assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, view2]); + assert.deepEqual(nodesToArrays(gridview.getView()), [view1, view2]); const view3 = new TestView(20, 20, 20, 20); gridview.addView(view3 as IView, 200, [1, 0]); - assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, [view3, view2]]); + assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view3, view2]]); const view4 = new TestView(20, 20, 20, 20); gridview.addView(view4 as IView, 200, [1, 0, 0]); - assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, [[view4, view3], view2]]); + assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [[view4, view3], view2]]); const view5 = new TestView(20, 20, 20, 20); gridview.addView(view5 as IView, 200, [1, 0]); - assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, [view5, [view4, view3], view2]]); + assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view5, [view4, view3], view2]]); const view6 = new TestView(20, 20, 20, 20); gridview.addView(view6 as IView, 200, [2]); - assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, [view5, [view4, view3], view2], view6]); + assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view5, [view4, view3], view2], view6]); const view7 = new TestView(20, 20, 20, 20); gridview.addView(view7 as IView, 200, [1, 1]); - assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, [view5, view7, [view4, view3], view2], view6]); + assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view5, view7, [view4, view3], view2], view6]); const view8 = new TestView(20, 20, 20, 20); gridview.addView(view8 as IView, 200, [1, 1, 0]); - assert.deepEqual(nodesToArrays(gridview.getViews()), [view1, [view5, [view8, view7], [view4, view3], view2], view6]); + assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view5, [view8, view7], [view4, view3], view2], view6]); gridview.dispose(); }); From b8bb655858966a417e5051f307435325b8e0a6b8 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 26 Jul 2019 17:38:17 +0200 Subject: [PATCH 46/48] grid: fix preferred size usage --- src/vs/base/browser/ui/grid/grid.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 386503317c4..d2cc4ee6ff5 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -376,7 +376,9 @@ export class Grid extends Disposable { } private onDidSashReset(location: number[]): void { - const resizeToPreferredSize = (node: GridNode): boolean => { + const resizeToPreferredSize = (location: number[]): boolean => { + const node = this.gridview.getView(location) as GridNode; + if (isGridBranchNode(node)) { return false; } @@ -388,21 +390,18 @@ export class Grid extends Disposable { return false; } - const viewSize = Orientation.HORIZONTAL ? { width: size } : { height: size }; + const viewSize = direction === Orientation.HORIZONTAL ? { width: Math.round(size) } : { height: Math.round(size) }; this.gridview.resizeView(location, viewSize); return true; }; - let node = this.gridview.getView(location) as GridNode; - - if (resizeToPreferredSize(node)) { + if (resizeToPreferredSize(location)) { return; } const [parentLocation, index] = tail(location); - node = this.gridview.getView([...parentLocation, index + 1]) as GridNode; - if (resizeToPreferredSize(node)) { + if (resizeToPreferredSize([...parentLocation, index + 1])) { return; } From 1fd0d4533f2fa19b0eaa83bb9414455da8f9cd33 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 26 Jul 2019 18:06:26 +0200 Subject: [PATCH 47/48] rename contentDimension --- src/vs/workbench/browser/parts/editor/editorPart.ts | 10 +++++----- .../workbench/contrib/watermark/browser/watermark.ts | 4 ++-- .../services/editor/common/editorGroupsService.ts | 2 +- src/vs/workbench/test/workbenchTestServices.ts | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 84b899658bc..14ea050a1b9 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -199,8 +199,8 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro //#region IEditorGroupsService - private _dimension: Dimension; - get dimension(): Dimension { return this._dimension; } + private _contentDimension: Dimension; + get contentDimension(): Dimension { return this._contentDimension; } get activeGroup(): IEditorGroupView { return this._activeGroup; @@ -409,7 +409,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro this.doCreateGridControlWithState(gridDescriptor, activeGroup.id, currentGroupViews); // Layout - this.doLayout(this._dimension); + this.doLayout(this._contentDimension); // Update container this.updateContainer(); @@ -933,10 +933,10 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro } private doLayout(dimension: Dimension): void { - this._dimension = dimension; + this._contentDimension = dimension; // Layout Grid - this.centeredLayoutWidget.layout(this._dimension.width, this._dimension.height); + this.centeredLayoutWidget.layout(this._contentDimension.width, this._contentDimension.height); // Event this._onDidLayout.fire(dimension); diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts index 7788f6e36a0..54c9b8620f5 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -173,7 +173,7 @@ export class WatermarkContribution extends Disposable implements IWorkbenchContr dom.prepend(container.firstElementChild as HTMLElement, this.watermark); this._register(this.keybindingService.onDidUpdateKeybindings(update)); this._register(this.editorGroupsService.onDidLayout(dimension => this.handleEditorPartSize(container, dimension))); - this.handleEditorPartSize(container, this.editorGroupsService.dimension); + this.handleEditorPartSize(container, this.editorGroupsService.contentDimension); } private handleEditorPartSize(container: HTMLElement, dimension: IDimension): void { @@ -214,4 +214,4 @@ Registry.as(ConfigurationExtensions.Configuration) 'description': nls.localize('tips.enabled', "When enabled, will show the watermark tips when no editor is open.") }, } - }); \ No newline at end of file + }); diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index c91173e5514..a9ab498cc98 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -179,7 +179,7 @@ export interface IEditorGroupsService { /** * The size of the editor groups area. */ - readonly dimension: IDimension; + readonly contentDimension: IDimension; /** * An active group is the default location for new editors to open. diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 9e9e0d739e8..0c9a9f933aa 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -667,7 +667,7 @@ export class TestEditorGroupsService implements IEditorGroupsService { whenRestored: Promise = Promise.resolve(undefined); willRestoreEditors = false; - dimension = { width: 800, height: 600 }; + contentDimension = { width: 800, height: 600 }; get activeGroup(): IEditorGroup { return this.groups[0]; @@ -1622,4 +1622,4 @@ export class RemoteFileSystemProvider implements IFileSystemProvider { write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise { return this.diskFileSystemProvider.write!(fd, pos, data, offset, length); } private toFileResource(resource: URI): URI { return resource.with({ scheme: Schemas.file, authority: '' }); } -} \ No newline at end of file +} From 45d944cfe399e7f6291969a6c8c3fd42f1014394 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 26 Jul 2019 18:13:39 +0200 Subject: [PATCH 48/48] workbench: implement preferred size for panel and sidebar fixes #67367 --- src/vs/workbench/browser/layout.ts | 6 ++++- src/vs/workbench/browser/legacyLayout.ts | 10 ++++---- src/vs/workbench/browser/part.ts | 11 +++++++-- .../workbench/browser/parts/compositePart.ts | 1 + .../browser/parts/panel/panelPart.ts | 24 +++++++++++++------ .../browser/parts/sidebar/sidebarPart.ts | 18 +++++++++++++- .../browser/parts/statusbar/statusbarPart.ts | 3 ++- .../services/layout/browser/layoutService.ts | 6 +++++ src/vs/workbench/test/browser/part.test.ts | 5 ++-- .../workbench/test/workbenchTestServices.ts | 4 ++++ 10 files changed, 68 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index c2e011c5370..7950b273c23 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -5,7 +5,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { EventType, addDisposableListener, addClass, removeClass, isAncestor, getClientArea, position, size, EventHelper } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, addClass, removeClass, isAncestor, getClientArea, position, size, EventHelper, Dimension } from 'vs/base/browser/dom'; import { onDidChangeFullscreen, isFullscreen, getZoomFactor } from 'vs/base/browser/browser'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -545,6 +545,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return true; // any other part cannot be hidden } + getDimension(part: Parts): Dimension { + return this.getPart(part).dimension; + } + getTitleBarOffset(): number { let offset = 0; if (this.isVisible(Parts.TITLEBAR_PART)) { diff --git a/src/vs/workbench/browser/legacyLayout.ts b/src/vs/workbench/browser/legacyLayout.ts index d936e13f1b2..8800afc09e8 100644 --- a/src/vs/workbench/browser/legacyLayout.ts +++ b/src/vs/workbench/browser/legacyLayout.ts @@ -626,11 +626,11 @@ export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLa } // Propagate to Part Layouts - this.parts.titlebar.layout(this.workbenchSize.width, this.titlebarHeight, -1); - this.parts.editor.layout(editorSize.width, editorSize.height, -1); - this.parts.sidebar.layout(sidebarSize.width, sidebarSize.height, -1); - this.parts.panel.layout(panelDimension.width, panelDimension.height, -1); - this.parts.activitybar.layout(activityBarSize.width, activityBarSize.height, -1); + this.parts.titlebar.layout(this.workbenchSize.width, this.titlebarHeight); + this.parts.editor.layout(editorSize.width, editorSize.height); + this.parts.sidebar.layout(sidebarSize.width, sidebarSize.height); + this.parts.panel.layout(panelDimension.width, panelDimension.height); + this.parts.activitybar.layout(activityBarSize.width, activityBarSize.height); // Propagate to Context View this.contextViewService.layout(); diff --git a/src/vs/workbench/browser/part.ts b/src/vs/workbench/browser/part.ts index f5281df2802..c78ad168cb7 100644 --- a/src/vs/workbench/browser/part.ts +++ b/src/vs/workbench/browser/part.ts @@ -9,7 +9,7 @@ import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { Dimension, size } from 'vs/base/browser/dom'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IDimension } from 'vs/platform/layout/browser/layoutService'; -import { ISerializableView, Orientation, IViewSize } from 'vs/base/browser/ui/grid/grid'; +import { ISerializableView, IViewSize } from 'vs/base/browser/ui/grid/grid'; import { Event, Emitter } from 'vs/base/common/event'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -28,6 +28,10 @@ export interface ILayoutContentResult { * arranges an optional title and mandatory content area to show content. */ export abstract class Part extends Component implements ISerializableView { + + private _dimension: Dimension; + get dimension(): Dimension { return this._dimension; } + private parent: HTMLElement; private titleArea: HTMLElement | null; private contentArea: HTMLElement | null; @@ -127,7 +131,10 @@ export abstract class Part extends Component implements ISerializableView { abstract minimumHeight: number; abstract maximumHeight: number; - abstract layout(width: number, height: number, orientation: Orientation): void; + layout(width: number, height: number): void { + this._dimension = new Dimension(width, height); + } + abstract toJSON(): object; //#endregion diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index d09cf93901b..e5d7cfbe7c8 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -466,6 +466,7 @@ export abstract class CompositePart extends Part { } layout(width: number, height: number): void { + super.layout(width, height); // Layout contents this.contentAreaSize = super.layoutContents(width, height).contentSize; diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 874d81baddb..1189df45c45 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -59,6 +59,16 @@ export class PanelPart extends CompositePart implements IPanelService { readonly snap = true; + get preferredHeight(): number | undefined { + const sidebarDimension = this.layoutService.getDimension(Parts.SIDEBAR_PART); + return sidebarDimension.height * 0.4; + } + + get preferredWidth(): number | undefined { + const statusbarPart = this.layoutService.getDimension(Parts.STATUSBAR_PART); + return statusbarPart.width * 0.4; + } + //#endregion get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean }> { return Event.map(this.onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus })); } @@ -74,7 +84,7 @@ export class PanelPart extends CompositePart implements IPanelService { private compositeActions: Map = new Map(); private blockOpeningPanel: boolean; - private dimension: Dimension; + private _contentDimension: Dimension; constructor( @INotificationService notificationService: INotificationService, @@ -293,21 +303,21 @@ export class PanelPart extends CompositePart implements IPanelService { } if (this.layoutService.getPanelPosition() === Position.RIGHT) { - this.dimension = new Dimension(width - 1, height!); // Take into account the 1px border when layouting + this._contentDimension = new Dimension(width - 1, height!); // Take into account the 1px border when layouting } else { - this.dimension = new Dimension(width, height!); + this._contentDimension = new Dimension(width, height!); } // Layout contents - super.layout(this.dimension.width, this.dimension.height); + super.layout(this._contentDimension.width, this._contentDimension.height); // Layout composite bar this.layoutCompositeBar(); } private layoutCompositeBar(): void { - if (this.dimension) { - let availableWidth = this.dimension.width - 40; // take padding into account + if (this._contentDimension) { + let availableWidth = this._contentDimension.width - 40; // take padding into account if (this.toolBar) { availableWidth = Math.max(PanelPart.MIN_COMPOSITE_BAR_WIDTH, availableWidth - this.getToolbarWidth()); // adjust height for global actions showing } @@ -522,4 +532,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { } }); -registerSingleton(IPanelService, PanelPart); \ No newline at end of file +registerSingleton(IPanelService, PanelPart); diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 88c40e827ae..8b50a07b662 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -47,6 +47,22 @@ export class SidebarPart extends CompositePart implements IViewletServi readonly snap = true; + get preferredWidth(): number | undefined { + const viewlet = this.getActiveViewlet(); + + if (!viewlet) { + return; + } + + const width = viewlet.getOptimalWidth(); + + if (typeof width !== 'number') { + return; + } + + return width; + } + //#endregion get onDidViewletRegister(): Event { return >this.viewletRegistry.onDidRegister; } @@ -303,4 +319,4 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(FocusSideBarAction, Fo primary: KeyMod.CtrlCmd | KeyCode.KEY_0 }), 'View: Focus into Side Bar', nls.localize('viewCategory', "View")); -registerSingleton(IViewletService, SidebarPart); \ No newline at end of file +registerSingleton(IViewletService, SidebarPart); diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 00fe6c45bc4..089cbd3003e 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -611,6 +611,7 @@ export class StatusbarPart extends Part implements IStatusbarService { } layout(width: number, height: number): void { + super.layout(width, height); super.layoutContents(width, height); } @@ -816,4 +817,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { } }); -registerSingleton(IStatusbarService, StatusbarPart); \ No newline at end of file +registerSingleton(IStatusbarService, StatusbarPart); diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index d0bf836984f..344c7657de5 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -8,6 +8,7 @@ import { Event } from 'vs/base/common/event'; import { MenuBarVisibility } from 'vs/platform/windows/common/windows'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { Part } from 'vs/workbench/browser/part'; +import { Dimension } from 'vs/base/browser/dom'; export const IWorkbenchLayoutService = createDecorator('layoutService'); @@ -81,6 +82,11 @@ export interface IWorkbenchLayoutService extends ILayoutService { */ isVisible(part: Parts): boolean; + /** + * Returns if the part is visible. + */ + getDimension(part: Parts): Dimension; + /** * Set activity bar hidden or not */ diff --git a/src/vs/workbench/test/browser/part.test.ts b/src/vs/workbench/test/browser/part.test.ts index 73878b4e603..ed151b3aced 100644 --- a/src/vs/workbench/test/browser/part.test.ts +++ b/src/vs/workbench/test/browser/part.test.ts @@ -10,7 +10,6 @@ import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService import { append, $, hide } from 'vs/base/browser/dom'; import { TestStorageService, TestLayoutService } from 'vs/workbench/test/workbenchTestServices'; import { StorageScope } from 'vs/platform/storage/common/storage'; -import { Orientation } from 'vs/base/browser/ui/grid/grid'; class SimplePart extends Part { @@ -19,7 +18,7 @@ class SimplePart extends Part { minimumHeight: number; maximumHeight: number; - layout(width: number, height: number, orientation: Orientation): void { + layout(width: number, height: number): void { throw new Error('Method not implemented.'); } @@ -172,4 +171,4 @@ suite('Workbench parts', () => { assert(!document.getElementById('myPart.title')); assert(document.getElementById('myPart.content')); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 0c9a9f933aa..153fc2bbae5 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -474,6 +474,10 @@ export class TestLayoutService implements IWorkbenchLayoutService { return true; } + getDimension(_part: Parts): Dimension { + return new Dimension(0, 0); + } + public getContainer(_part: Parts): HTMLElement { return null!; }