From 03e53fd77b0742af1ead4fa104df9542ab23d9e9 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 6 May 2022 15:55:35 -0700 Subject: [PATCH 001/942] add telemetry for shell integration --- .../common/xterm/shellIntegrationAddon.ts | 9 +++- .../terminal/browser/terminalInstance.ts | 11 +++- .../terminal/browser/xterm/xtermTerminal.ts | 54 +++++++++++++------ 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index ebe5ab92d1493..b89e83384110d 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -15,6 +15,7 @@ import { ILogService } from 'vs/platform/log/common/log'; // eslint-disable-next-line code-import-patterns import type { ITerminalAddon, Terminal } from 'xterm-headless'; import { ISerializedCommandDetectionCapability } from 'vs/platform/terminal/common/terminalProcess'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; /** * Shell integration is a feature that enhances the terminal's understanding of what's happening @@ -123,9 +124,11 @@ const enum VSCodeOscPt { export class ShellIntegrationAddon extends Disposable implements IShellIntegration, ITerminalAddon { private _terminal?: Terminal; readonly capabilities = new TerminalCapabilityStore(); + private _hasUpdatedTelemetry: boolean = false; constructor( - @ILogService private readonly _logService: ILogService + @ILogService private readonly _logService: ILogService, + @ITelemetryService private readonly _telemetryService?: ITelemetryService ) { super(); } @@ -244,6 +247,10 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati commandDetection = new CommandDetectionCapability(terminal, this._logService); this.capabilities.add(TerminalCapability.CommandDetection, commandDetection); } + if (!this._hasUpdatedTelemetry) { + this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivated'); + this._hasUpdatedTelemetry = true; + } return commandDetection; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 88b3e2d041d5b..390435476e999 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -80,6 +80,7 @@ import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/ import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import type { ITerminalAddon, Terminal as XTermTerminal } from 'xterm'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const enum Constants { /** @@ -361,7 +362,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @IEditorService private readonly _editorService: IEditorService, @IWorkspaceTrustRequestService private readonly _workspaceTrustRequestService: IWorkspaceTrustRequestService, - @IHistoryService private readonly _historyService: IHistoryService + @IHistoryService private readonly _historyService: IHistoryService, + @ITelemetryService private readonly _telemetryService: ITelemetryService ) { super(); @@ -1631,6 +1633,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } } + if (shellIntegrationAttempted) { + this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationProcessExit'); + } + // First onExit to consumers, this can happen after the terminal has already been disposed. this._onExit.fire(exitCodeOrError); @@ -2572,7 +2578,7 @@ export class TerminalLabelComputer extends Disposable { } } -export function parseExitResult( +export function parseExitResult(this: any, exitCodeOrError: ITerminalLaunchError | number | undefined, shellLaunchConfig: IShellLaunchConfig, processState: ProcessState, @@ -2602,6 +2608,7 @@ export function parseExitResult( if (shellIntegrationAttempted) { if (commandLine) { message = nls.localize('launchFailed.exitCodeAndCommandLineShellIntegration', "The terminal process \"{0}\" failed to launch (exit code: {1}). Disabling shell integration with `terminal.integrated.shellIntegration.enabled` might help.", commandLine, code); + this._logProcessExitShellIntegrationTelemetry(); } else { message = nls.localize('launchFailed.exitCodeOnlyShellIntegration', "The terminal process failed to launch (exit code: {0}). Disabling shell integration with `terminal.integrated.shellIntegration.enabled` might help.", code); } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index a1efe91c333d4..91236a0d7471e 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -32,8 +32,9 @@ import { Color } from 'vs/base/common/color'; import { ShellIntegrationAddon } from 'vs/platform/terminal/common/xterm/shellIntegrationAddon'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DecorationAddon } from 'vs/workbench/contrib/terminal/browser/xterm/decorationAddon'; -import { ITerminalCapabilityStore, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { ITerminalCapabilityStore, ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { Emitter } from 'vs/base/common/event'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -59,7 +60,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { // Always on addons private _commandNavigationAddon: CommandNavigationAddon; - private _shellIntegrationAddon: ShellIntegrationAddon; + private _shellIntegrationAddon!: ShellIntegrationAddon; private _decorationAddon: DecorationAddon | undefined; // Optional addons @@ -103,7 +104,8 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { @INotificationService private readonly _notificationService: INotificationService, @IStorageService private readonly _storageService: IStorageService, @IThemeService private readonly _themeService: IThemeService, - @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService + @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, + @ITelemetryService private readonly _telemetryService: ITelemetryService ) { super(); this.target = location; @@ -153,7 +155,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { } if (e.affectsConfiguration(TerminalSettingId.ShellIntegrationDecorationsEnabled) || e.affectsConfiguration(TerminalSettingId.ShellIntegrationEnabled)) { - this._updateDecorationAddon(); + this._updateShellIntegrationAddons(); } })); @@ -169,9 +171,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { this._updateUnicodeVersion(); this._commandNavigationAddon = this._instantiationService.createInstance(CommandNavigationAddon, _capabilities); this.raw.loadAddon(this._commandNavigationAddon); - this._shellIntegrationAddon = this._instantiationService.createInstance(ShellIntegrationAddon); - this.raw.loadAddon(this._shellIntegrationAddon); - this._updateDecorationAddon(); + this._updateShellIntegrationAddons(); } private _createDecorationAddon(): void { this._decorationAddon = this._instantiationService.createInstance(DecorationAddon, this._capabilities); @@ -595,16 +595,40 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { } } - private _updateDecorationAddon(): void { - if (this._configHelper.config.shellIntegration?.enabled && this._configHelper.config.shellIntegration.decorationsEnabled) { - if (!this._decorationAddon) { + private _updateShellIntegrationAddons(): void { + const shellIntegrationEnabled = this._configurationService.getValue(TerminalSettingId.ShellIntegrationEnabled); + const decorationsEnabled = this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationsEnabled); + if (shellIntegrationEnabled) { + if (!this._shellIntegrationAddon) { + this._createShellIntegrationAddon(); + } + if (decorationsEnabled && !this._decorationAddon) { this._createDecorationAddon(); + } else if (this._decorationAddon && !decorationsEnabled) { + this._decorationAddon.dispose(); + this._decorationAddon = undefined; + } + } else { + this._shellIntegrationAddon?.dispose(); + if (this._decorationAddon) { + this._decorationAddon.dispose(); + this._decorationAddon = undefined; + this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationDisabledByUser'); } - return; - } - if (this._decorationAddon) { - this._decorationAddon.dispose(); - this._decorationAddon = undefined; } } + + private _createShellIntegrationAddon(): void { + this._shellIntegrationAddon = this._instantiationService.createInstance(ShellIntegrationAddon); + this.raw.loadAddon(this._shellIntegrationAddon); + this._addTelemetryIfShellIntegrationFailed(); + } + + private async _addTelemetryIfShellIntegrationFailed(): Promise { + setTimeout(() => { + if (!this._capabilities.get(TerminalCapability.CommandDetection) && !this._capabilities.get(TerminalCapability.CwdDetection)) { + this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationFailedToActivate'); + } + }, 10000); + } } From 549c4077ecb6bd89299d8547441fd67a8e9390ff Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 6 May 2022 16:02:00 -0700 Subject: [PATCH 002/942] clean up --- src/vs/workbench/contrib/terminal/browser/terminal.ts | 4 ++-- .../workbench/contrib/terminal/browser/terminalInstance.ts | 5 ++--- .../contrib/terminal/browser/xterm/xtermTerminal.ts | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index c48979fa4d6b2..371b0f7e8f70a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -858,9 +858,9 @@ export interface IXtermTerminal { readonly commandTracker: ICommandTracker; /** - * Reports the status of shell integration and fires events relating to it. + * Reports the status of shell integration and fires events relating to it when enabled. */ - readonly shellIntegration: IShellIntegration; + readonly shellIntegration: IShellIntegration | undefined; /** * The position of the terminal. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 390435476e999..c04dcc4d37cd4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -725,7 +725,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._areLinksReady = true; this._onLinksReady.fire(this); }); - this._processManager.onRestoreCommands(e => this.xterm?.shellIntegration.deserialize(e)); + this._processManager.onRestoreCommands(e => this.xterm?.shellIntegration?.deserialize(e)); this._loadTypeAheadAddon(xterm); @@ -2578,7 +2578,7 @@ export class TerminalLabelComputer extends Disposable { } } -export function parseExitResult(this: any, +export function parseExitResult( exitCodeOrError: ITerminalLaunchError | number | undefined, shellLaunchConfig: IShellLaunchConfig, processState: ProcessState, @@ -2608,7 +2608,6 @@ export function parseExitResult(this: any, if (shellIntegrationAttempted) { if (commandLine) { message = nls.localize('launchFailed.exitCodeAndCommandLineShellIntegration', "The terminal process \"{0}\" failed to launch (exit code: {1}). Disabling shell integration with `terminal.integrated.shellIntegration.enabled` might help.", commandLine, code); - this._logProcessExitShellIntegrationTelemetry(); } else { message = nls.localize('launchFailed.exitCodeOnlyShellIntegration', "The terminal process failed to launch (exit code: {0}). Disabling shell integration with `terminal.integrated.shellIntegration.enabled` might help.", code); } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 91236a0d7471e..81f47c7bba94a 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -60,7 +60,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { // Always on addons private _commandNavigationAddon: CommandNavigationAddon; - private _shellIntegrationAddon!: ShellIntegrationAddon; + private _shellIntegrationAddon: ShellIntegrationAddon | undefined; private _decorationAddon: DecorationAddon | undefined; // Optional addons @@ -79,7 +79,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { readonly onDidChangeFindResults = this._onDidChangeFindResults.event; get commandTracker(): ICommandTracker { return this._commandNavigationAddon; } - get shellIntegration(): IShellIntegration { return this._shellIntegrationAddon; } + get shellIntegration(): IShellIntegration | undefined { return this._shellIntegrationAddon; } private _target: TerminalLocation | undefined; set target(location: TerminalLocation | undefined) { From 855c0f86571af731858774499bcff2ac69282b13 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 6 May 2022 16:12:29 -0700 Subject: [PATCH 003/942] =?UTF-8?q?=F0=9F=92=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contrib/terminal/browser/xterm/xtermTerminal.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 81f47c7bba94a..14c09ba34775a 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -621,13 +621,14 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { private _createShellIntegrationAddon(): void { this._shellIntegrationAddon = this._instantiationService.createInstance(ShellIntegrationAddon); this.raw.loadAddon(this._shellIntegrationAddon); - this._addTelemetryIfShellIntegrationFailed(); + this._ensureCapabilitiesOrAddFailureTelemetry(); } - private async _addTelemetryIfShellIntegrationFailed(): Promise { + private async _ensureCapabilitiesOrAddFailureTelemetry(): Promise { setTimeout(() => { if (!this._capabilities.get(TerminalCapability.CommandDetection) && !this._capabilities.get(TerminalCapability.CwdDetection)) { this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationFailedToActivate'); + this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); } }, 10000); } From 34d31f4b9e7be13cddbe9a3c085b81595d91fcb9 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 9 May 2022 14:09:00 +0200 Subject: [PATCH 004/942] merge editor kick off --- build/lib/i18n.resources.json | 4 + .../mergeEditor/browser/media/mergeEditor.css | 4 + .../browser/mergeEditor.contribution.ts | 61 ++++++++++ .../mergeEditor/browser/mergeEditor.ts | 110 ++++++++++++++++++ .../mergeEditor/browser/mergeEditorInput.ts | 93 +++++++++++++++ .../mergeEditor/browser/mergeEditorModel.ts | 20 ++++ .../browser/mergeEditorSerializer.ts | 31 +++++ src/vs/workbench/workbench.common.main.ts | 3 + 8 files changed, 326 insertions(+) create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/mergeEditorSerializer.ts diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 06bfbb2b506b9..246384ae15ad1 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -118,6 +118,10 @@ "name": "vs/workbench/contrib/markers", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/mergeEditor", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/localizations", "project": "vscode-workbench" diff --git a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css new file mode 100644 index 0000000000000..a4a092d83492f --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css @@ -0,0 +1,4 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts new file mode 100644 index 0000000000000..27d3d7c8c9b6b --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import { localize } from 'vs/nls'; +import { URI } from 'vs/base/common/uri'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; +import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor'; +import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditor'; +import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { MergeEditorSerializer } from './mergeEditorSerializer'; + + +//#region Editor Descriptior + +Registry.as(EditorExtensions.EditorPane).registerEditorPane( + EditorPaneDescriptor.create( + MergeEditor, + MergeEditor.ID, + localize('name', "Merge Editor") + ), + [ + new SyncDescriptor(MergeEditorInput) + ] +); + +Registry.as(EditorExtensions.EditorFactory).registerEditorSerializer( + MergeEditorInput.ID, + MergeEditorSerializer +); + +registerAction2(class Foo extends Action2 { + + constructor() { + super({ + id: 'testMergeEditor', + title: '3wm', + f1: true + }); + } + + run(accessor: ServicesAccessor, ...args: any[]): void { + const instaService = accessor.get(IInstantiationService); + const input = instaService.createInstance( + MergeEditorInput, + URI.file('/Users/jrieken/Code/_samples/abc/test.md'), + URI.file('/Users/jrieken/Code/_samples/abc/test.md'), + URI.file('/Users/jrieken/Code/_samples/abc/test.md'), + URI.file('/Users/jrieken/Code/_samples/abc/test.md'), + ); + accessor.get(IEditorService).openEditor(input); + } + +}); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts new file mode 100644 index 0000000000000..54f3b6dadb07d --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/mergeEditor'; +import { Dimension, reset } from 'vs/base/browser/dom'; +import { Emitter } from 'vs/base/common/event'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; +import { IEditorOpenContext } from 'vs/workbench/common/editor'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { BugIndicatingError } from 'vs/base/common/errors'; +import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Direction, Grid, IView, IViewSize, LayoutPriority } from 'vs/base/browser/ui/grid/grid'; +import { Sizing } from 'vs/base/browser/ui/splitview/splitview'; + + +class CodeEditorView implements IView { + + preferredWidth?: number | undefined; + preferredHeight?: number | undefined; + + element: HTMLElement = document.createElement('div'); + + minimumWidth: number = 10; + maximumWidth: number = Number.MAX_SAFE_INTEGER; + minimumHeight: number = 10; + maximumHeight: number = Number.MAX_SAFE_INTEGER; + priority?: LayoutPriority | undefined; + snap?: boolean | undefined; + + private readonly _onDidChange = new Emitter(); + readonly onDidChange = this._onDidChange.event; + + constructor(text: string) { + this.element.innerText = text; + } + + layout(width: number, height: number, top: number, left: number): void { + this.element.style.width = `${width}px`; + this.element.style.height = `${height}px`; + this.element.style.top = `${top}px`; + this.element.style.left = `${left}px`; + } + +} + +export class MergeEditor extends EditorPane { + + static readonly ID = 'mergeEditor'; + + private readonly _sessionDisposables = new DisposableStore(); + + private _grid!: Grid; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService storageService: IStorageService, + @IThemeService themeService: IThemeService, + ) { + super(MergeEditor.ID, telemetryService, themeService, storageService); + } + + override dispose(): void { + this._sessionDisposables.dispose(); + super.dispose(); + } + + protected createEditor(parent: HTMLElement): void { + + + const inputOneView = new CodeEditorView('one'); + const inputTwoView = new CodeEditorView('two'); + const inputResultView = new CodeEditorView('result'); + + this._grid = new Grid(inputResultView); + + this._grid.addView(inputOneView, Sizing.Distribute, inputResultView, Direction.Up); + this._grid.addView(inputTwoView, Sizing.Distribute, inputOneView, Direction.Right); + reset(parent, this._grid.element); + } + + layout(dimension: Dimension): void { + this._grid.layout(dimension.width, dimension.height); + } + + override async setInput(input: EditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { + if (!(input instanceof MergeEditorInput)) { + throw new BugIndicatingError('ONLY MergeEditorInput is supported'); + } + await super.setInput(input, options, context, token); + console.trace('mergeEditor@53'); + this._sessionDisposables.clear(); + // const model = await input.resolve(); + // if (token.isCancellationRequested) { + // return; + // } + } + + override clearInput(): void { + console.trace('mergeEditor@66'); + } + +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts new file mode 100644 index 0000000000000..9a3c69d7cc26a --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isEqual } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IUntypedEditorInput } from 'vs/workbench/common/editor'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; + +export interface MergeEditorInputJSON { + anchestor: URI; + inputOne: URI; + inputTwo: URI; + result: URI; +} + +export class MergeEditorInput extends EditorInput { + + static readonly ID = 'mergeEditor.Input'; + + private _model?: MergeEditorModel; + + constructor( + private readonly _anchestor: URI, + private readonly _inputOne: URI, + private readonly _inputTwo: URI, + private readonly _result: URI, + @IInstantiationService private readonly _instaService: IInstantiationService, + @ITextModelService private readonly _textModelService: ITextModelService, + ) { + super(); + } + + override dispose(): void { + super.dispose(); + } + + get typeId(): string { + return MergeEditorInput.ID; + } + + get resource(): URI | undefined { + return this._result; + } + + override async resolve(): Promise { + if (!this._model) { + + const anchestor = await this._textModelService.createModelReference(this._anchestor); + const inputOne = await this._textModelService.createModelReference(this._inputOne); + const inputTwo = await this._textModelService.createModelReference(this._inputTwo); + const result = await this._textModelService.createModelReference(this._result); + + this._model = this._instaService.createInstance( + MergeEditorModel, + anchestor.object.textEditorModel, + inputOne.object.textEditorModel, + inputTwo.object.textEditorModel, + result.object.textEditorModel + ); + + this._store.add(this._model); + this._store.add(anchestor); + this._store.add(inputOne); + this._store.add(inputTwo); + this._store.add(result); + } + return this._model; + } + + override matches(otherInput: EditorInput | IUntypedEditorInput): boolean { + if (!(otherInput instanceof MergeEditorInput)) { + return false; + } + return isEqual(this._anchestor, otherInput._anchestor) + && isEqual(this._inputOne, otherInput._inputOne) + && isEqual(this._inputTwo, otherInput._inputTwo) + && isEqual(this._result, otherInput._result); + } + + toJSON(): MergeEditorInputJSON { + return { + anchestor: this._anchestor, + inputOne: this._inputOne, + inputTwo: this._inputTwo, + result: this._result, + }; + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts new file mode 100644 index 0000000000000..172a5c8e1a02b --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ITextModel } from 'vs/editor/common/model'; +import { EditorModel } from 'vs/workbench/common/editor/editorModel'; + +export class MergeEditorModel extends EditorModel { + + constructor( + readonly anchestor: ITextModel, + readonly inputOne: ITextModel, + readonly inputTwo: ITextModel, + readonly result: ITextModel, + ) { + super(); + } + +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorSerializer.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorSerializer.ts new file mode 100644 index 0000000000000..2480f0950ba0f --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorSerializer.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { parse } from 'vs/base/common/marshalling'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IEditorSerializer } from 'vs/workbench/common/editor'; +import { MergeEditorInput, MergeEditorInputJSON } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; + +export class MergeEditorSerializer implements IEditorSerializer { + + canSerialize(): boolean { + return true; + } + + serialize(editor: MergeEditorInput): string { + return JSON.stringify(editor.toJSON()); + } + + deserialize(instantiationService: IInstantiationService, raw: string): MergeEditorInput | undefined { + try { + const data = parse(raw); + return instantiationService.createInstance(MergeEditorInput, data.anchestor, data.inputOne, data.inputTwo, data.result); + } catch (err) { + onUnexpectedError(err); + return undefined; + } + } +} diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index bf87db19d53f7..74ebaf956ecd1 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -210,6 +210,9 @@ import 'vs/workbench/contrib/debug/browser/debugViewlet'; // Markers import 'vs/workbench/contrib/markers/browser/markers.contribution'; +// Merge Editor +import 'vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution'; + // Comments import 'vs/workbench/contrib/comments/browser/comments.contribution'; From 6435afb931f5f1ac496e115c91fb0a1ed6ead82a Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 9 May 2022 15:26:23 +0200 Subject: [PATCH 005/942] remove trace, resolve input to model, log model --- .../workbench/contrib/mergeEditor/browser/mergeEditor.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 54f3b6dadb07d..4ce0cc0eb1295 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -74,7 +74,6 @@ export class MergeEditor extends EditorPane { protected createEditor(parent: HTMLElement): void { - const inputOneView = new CodeEditorView('one'); const inputTwoView = new CodeEditorView('two'); const inputResultView = new CodeEditorView('result'); @@ -95,16 +94,17 @@ export class MergeEditor extends EditorPane { throw new BugIndicatingError('ONLY MergeEditorInput is supported'); } await super.setInput(input, options, context, token); - console.trace('mergeEditor@53'); + this._sessionDisposables.clear(); - // const model = await input.resolve(); + const model = await input.resolve(); + console.log(model); // if (token.isCancellationRequested) { // return; // } } override clearInput(): void { - console.trace('mergeEditor@66'); + } } From 26a86d4a42da140bb268ee16f1ae1f04b1827b4b Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 9 May 2022 15:53:22 +0200 Subject: [PATCH 006/942] Loads paths from command argument. --- .../browser/mergeEditor.contribution.ts | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts index 27d3d7c8c9b6b..c1fc3b0fbf27e 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -45,17 +45,39 @@ registerAction2(class Foo extends Action2 { f1: true }); } - run(accessor: ServicesAccessor, ...args: any[]): void { + const validatedArgs = ITestMergeEditorArgs.validate(args[0]); + + function normalize(uri: URI | string): URI { + if (typeof uri === 'string') { + return URI.parse(uri); + } else { + return uri; + } + } + const instaService = accessor.get(IInstantiationService); const input = instaService.createInstance( MergeEditorInput, - URI.file('/Users/jrieken/Code/_samples/abc/test.md'), - URI.file('/Users/jrieken/Code/_samples/abc/test.md'), - URI.file('/Users/jrieken/Code/_samples/abc/test.md'), - URI.file('/Users/jrieken/Code/_samples/abc/test.md'), + normalize(validatedArgs.ancestor), + normalize(validatedArgs.input1), + normalize(validatedArgs.input2), + normalize(validatedArgs.output), ); accessor.get(IEditorService).openEditor(input); } }); + +namespace ITestMergeEditorArgs { + export function validate(args: any): ITestMergeEditorArgs { + return args as ITestMergeEditorArgs; + } +} + +interface ITestMergeEditorArgs { + ancestor: URI | string; + input1: URI | string; + input2: URI | string; + output: URI | string; +} From 6a051dc03a7763ed75fbc746b7bb16ed84aef9eb Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 9 May 2022 16:17:29 +0200 Subject: [PATCH 007/942] Creates editors & MVP of scroll synchronization. --- .../mergeEditor/browser/mergeEditor.ts | 109 +++++++++++------- 1 file changed, 70 insertions(+), 39 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 4ce0cc0eb1295..14950aef60ddd 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -19,37 +19,10 @@ import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/merge import { DisposableStore } from 'vs/base/common/lifecycle'; import { Direction, Grid, IView, IViewSize, LayoutPriority } from 'vs/base/browser/ui/grid/grid'; import { Sizing } from 'vs/base/browser/ui/splitview/splitview'; - - -class CodeEditorView implements IView { - - preferredWidth?: number | undefined; - preferredHeight?: number | undefined; - - element: HTMLElement = document.createElement('div'); - - minimumWidth: number = 10; - maximumWidth: number = Number.MAX_SAFE_INTEGER; - minimumHeight: number = 10; - maximumHeight: number = Number.MAX_SAFE_INTEGER; - priority?: LayoutPriority | undefined; - snap?: boolean | undefined; - - private readonly _onDidChange = new Emitter(); - readonly onDidChange = this._onDidChange.event; - - constructor(text: string) { - this.element.innerText = text; - } - - layout(width: number, height: number, top: number, left: number): void { - this.element.style.width = `${width}px`; - this.element.style.height = `${height}px`; - this.element.style.top = `${top}px`; - this.element.style.left = `${left}px`; - } - -} +import { ITextModel } from 'vs/editor/common/model'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ScrollType } from 'vs/editor/common/editorCommon'; export class MergeEditor extends EditorPane { @@ -59,12 +32,28 @@ export class MergeEditor extends EditorPane { private _grid!: Grid; + private readonly inputOneView = this.instantiation.createInstance(CodeEditorView); + private readonly inputTwoView = this.instantiation.createInstance(CodeEditorView); + private readonly inputResultView = this.instantiation.createInstance(CodeEditorView); + constructor( + @IInstantiationService private readonly instantiation: IInstantiationService, @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, @IThemeService themeService: IThemeService, ) { super(MergeEditor.ID, telemetryService, themeService, storageService); + + this._store.add(this.inputOneView.editor.onDidScrollChange(c => { + if (c.scrollTopChanged) { + this.inputTwoView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + } + })); + this._store.add(this.inputTwoView.editor.onDidScrollChange(c => { + if (c.scrollTopChanged) { + this.inputOneView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + } + })); } override dispose(): void { @@ -73,15 +62,10 @@ export class MergeEditor extends EditorPane { } protected createEditor(parent: HTMLElement): void { + this._grid = new Grid(this.inputResultView); - const inputOneView = new CodeEditorView('one'); - const inputTwoView = new CodeEditorView('two'); - const inputResultView = new CodeEditorView('result'); - - this._grid = new Grid(inputResultView); - - this._grid.addView(inputOneView, Sizing.Distribute, inputResultView, Direction.Up); - this._grid.addView(inputTwoView, Sizing.Distribute, inputOneView, Direction.Right); + this._grid.addView(this.inputOneView, Sizing.Distribute, this.inputResultView, Direction.Up); + this._grid.addView(this.inputTwoView, Sizing.Distribute, this.inputOneView, Direction.Right); reset(parent, this._grid.element); } @@ -97,6 +81,11 @@ export class MergeEditor extends EditorPane { this._sessionDisposables.clear(); const model = await input.resolve(); + + this.inputOneView.setModel(model.inputOne); + this.inputTwoView.setModel(model.inputTwo); + this.inputResultView.setModel(model.result); + console.log(model); // if (token.isCancellationRequested) { // return; @@ -108,3 +97,45 @@ export class MergeEditor extends EditorPane { } } + +class CodeEditorView implements IView { + preferredWidth?: number | undefined; + preferredHeight?: number | undefined; + + element: HTMLElement = document.createElement('div'); + + minimumWidth: number = 10; + maximumWidth: number = Number.MAX_SAFE_INTEGER; + minimumHeight: number = 10; + maximumHeight: number = Number.MAX_SAFE_INTEGER; + priority?: LayoutPriority | undefined; + snap?: boolean | undefined; + + private readonly _onDidChange = new Emitter(); + readonly onDidChange = this._onDidChange.event; + + public readonly editor = this.instantiationService.createInstance( + CodeEditorWidget, + this.element, + { minimap: { enabled: false } }, + {} + ); + + constructor( + @IInstantiationService + private readonly instantiationService: IInstantiationService + ) { + } + + public setModel(model: ITextModel | undefined): void { + this.editor.setModel(model); + } + + layout(width: number, height: number, top: number, left: number): void { + this.element.style.width = `${width}px`; + this.element.style.height = `${height}px`; + this.element.style.top = `${top}px`; + this.element.style.left = `${left}px`; + this.editor.layout({ width, height }); + } +} From 52a634dd87122c021f2ae6645d27fc043463442e Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 9 May 2022 16:25:11 +0200 Subject: [PATCH 008/942] Synchronize scrolling of all three editors. --- .../mergeEditor/browser/mergeEditor.ts | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 14950aef60ddd..c7c4d8e93589b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -44,14 +44,29 @@ export class MergeEditor extends EditorPane { ) { super(MergeEditor.ID, telemetryService, themeService, storageService); + const reentrancyBarrier = new ReentrancyBarrier(); this._store.add(this.inputOneView.editor.onDidScrollChange(c => { if (c.scrollTopChanged) { - this.inputTwoView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + reentrancyBarrier.runExclusively(() => { + this.inputTwoView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + this.inputResultView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + }); } })); this._store.add(this.inputTwoView.editor.onDidScrollChange(c => { if (c.scrollTopChanged) { - this.inputOneView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + reentrancyBarrier.runExclusively(() => { + this.inputOneView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + this.inputResultView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + }); + } + })); + this._store.add(this.inputResultView.editor.onDidScrollChange(c => { + if (c.scrollTopChanged) { + reentrancyBarrier.runExclusively(() => { + this.inputOneView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + this.inputTwoView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + }); } })); } @@ -139,3 +154,19 @@ class CodeEditorView implements IView { this.editor.layout({ width, height }); } } + +class ReentrancyBarrier { + private isActive = false; + + public runExclusively(fn: () => void): void { + if (this.isActive) { + return; + } + this.isActive = true; + try { + fn(); + } finally { + this.isActive = false; + } + } +} From c54e9d45e170d871be0bb485a103d80f9c06f5e7 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 9 May 2022 09:06:48 -0700 Subject: [PATCH 009/942] add doHandleVSCodeSequence and update telemetry based upon that --- .../common/xterm/shellIntegrationAddon.ts | 23 +++++++++++++++---- .../terminal/browser/xterm/xtermTerminal.ts | 10 -------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index b89e83384110d..648285d4a4539 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -137,9 +137,19 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati this._terminal = xterm; this.capabilities.add(TerminalCapability.PartialCommandDetection, new PartialCommandDetectionCapability(this._terminal)); this._register(xterm.parser.registerOscHandler(ShellIntegrationOscPs.VSCode, data => this._handleVSCodeSequence(data))); + this._ensureCapabilitiesOrAddFailureTelemetry(); } private _handleVSCodeSequence(data: string): boolean { + const didHandle = this._doHandleVSCodeSequence(data); + if (!this._hasUpdatedTelemetry && didHandle) { + this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivated'); + this._hasUpdatedTelemetry = true; + } + return didHandle; + } + + private _doHandleVSCodeSequence(data: string): boolean { if (!this._terminal) { return false; } @@ -214,6 +224,15 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati return false; } + private async _ensureCapabilitiesOrAddFailureTelemetry(): Promise { + setTimeout(() => { + if (!this.capabilities.get(TerminalCapability.CommandDetection) && !this.capabilities.get(TerminalCapability.CwdDetection)) { + this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationFailedToActivate'); + this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); + } + }, 10000); + } + serialize(): ISerializedCommandDetectionCapability { if (!this._terminal || !this.capabilities.has(TerminalCapability.CommandDetection)) { return { @@ -247,10 +266,6 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati commandDetection = new CommandDetectionCapability(terminal, this._logService); this.capabilities.add(TerminalCapability.CommandDetection, commandDetection); } - if (!this._hasUpdatedTelemetry) { - this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivated'); - this._hasUpdatedTelemetry = true; - } return commandDetection; } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 14c09ba34775a..fbb92329c8f24 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -621,15 +621,5 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { private _createShellIntegrationAddon(): void { this._shellIntegrationAddon = this._instantiationService.createInstance(ShellIntegrationAddon); this.raw.loadAddon(this._shellIntegrationAddon); - this._ensureCapabilitiesOrAddFailureTelemetry(); - } - - private async _ensureCapabilitiesOrAddFailureTelemetry(): Promise { - setTimeout(() => { - if (!this._capabilities.get(TerminalCapability.CommandDetection) && !this._capabilities.get(TerminalCapability.CwdDetection)) { - this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationFailedToActivate'); - this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); - } - }, 10000); } } From 740b2798ddc1954db5b69ad97939774cab31c317 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 9 May 2022 09:08:04 -0700 Subject: [PATCH 010/942] shellIntegrationAttempted -> failedShellIntegrationInjection --- .../contrib/terminal/browser/terminalInstance.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index c04dcc4d37cd4..9af95baf33351 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1581,7 +1581,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { * @param exitCode The exit code of the process, this is undefined when the terminal was exited * through user action. */ - private async _onProcessExit(exitCodeOrError?: number | ITerminalLaunchError, shellIntegrationAttempted?: boolean): Promise { + private async _onProcessExit(exitCodeOrError?: number | ITerminalLaunchError, failedShellIntegrationInjection?: boolean): Promise { // Prevent dispose functions being triggered multiple times if (this._isExiting) { return; @@ -1592,7 +1592,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { await this._flushXtermData(); this._logService.debug(`Terminal process exit (instanceId: ${this.instanceId}) with code ${this._exitCode}`); - const parsedExitResult = parseExitResult(exitCodeOrError, this.shellLaunchConfig, this._processManager.processState, this._initialCwd, shellIntegrationAttempted); + const parsedExitResult = parseExitResult(exitCodeOrError, this.shellLaunchConfig, this._processManager.processState, this._initialCwd, failedShellIntegrationInjection); this._exitCode = parsedExitResult?.code; const exitMessage = parsedExitResult?.message; @@ -1633,7 +1633,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } } - if (shellIntegrationAttempted) { + if (failedShellIntegrationInjection) { this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationProcessExit'); } @@ -2583,7 +2583,7 @@ export function parseExitResult( shellLaunchConfig: IShellLaunchConfig, processState: ProcessState, initialCwd: string | undefined, - shellIntegrationAttempted?: boolean + failedShellIntegrationInjection?: boolean ): { code: number | undefined; message: string | undefined } | undefined { // Only return a message if the exit code is non-zero if (exitCodeOrError === undefined || exitCodeOrError === 0) { @@ -2605,7 +2605,7 @@ export function parseExitResult( commandLine += shellLaunchConfig.args.map(a => ` '${a}'`).join(); } } - if (shellIntegrationAttempted) { + if (failedShellIntegrationInjection) { if (commandLine) { message = nls.localize('launchFailed.exitCodeAndCommandLineShellIntegration', "The terminal process \"{0}\" failed to launch (exit code: {1}). Disabling shell integration with `terminal.integrated.shellIntegration.enabled` might help.", commandLine, code); } else { From fef40536407c170d50743d7e6387b2322cbb4c7e Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 9 May 2022 09:58:23 -0700 Subject: [PATCH 011/942] remove unused --- .../workbench/contrib/terminal/browser/xterm/xtermTerminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index fbb92329c8f24..b1c05b75118b2 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -32,7 +32,7 @@ import { Color } from 'vs/base/common/color'; import { ShellIntegrationAddon } from 'vs/platform/terminal/common/xterm/shellIntegrationAddon'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DecorationAddon } from 'vs/workbench/contrib/terminal/browser/xterm/decorationAddon'; -import { ITerminalCapabilityStore, ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { ITerminalCapabilityStore, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; import { Emitter } from 'vs/base/common/event'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; From 6c796dde398f7925c485c8f1dea306c6d0c48a10 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 9 May 2022 11:40:42 -0700 Subject: [PATCH 012/942] rearrange properties --- .../platform/terminal/common/xterm/shellIntegrationAddon.ts | 4 ++-- .../workbench/contrib/terminal/browser/xterm/xtermTerminal.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 648285d4a4539..8994ae11b87c5 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -127,8 +127,8 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private _hasUpdatedTelemetry: boolean = false; constructor( - @ILogService private readonly _logService: ILogService, - @ITelemetryService private readonly _telemetryService?: ITelemetryService + private readonly _logService: ILogService, + private readonly _telemetryService?: ITelemetryService ) { super(); } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index b1c05b75118b2..485936bf0cb8c 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -619,7 +619,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { } private _createShellIntegrationAddon(): void { - this._shellIntegrationAddon = this._instantiationService.createInstance(ShellIntegrationAddon); + this._shellIntegrationAddon = new ShellIntegrationAddon(this._logService, this._telemetryService); this.raw.loadAddon(this._shellIntegrationAddon); } } From 0f51c46f30f8be6a4851b8c856eb1e29ac8a3275 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 9 May 2022 11:43:18 -0700 Subject: [PATCH 013/942] keep dependency injection --- src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 8994ae11b87c5..cfafc8907673d 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -127,7 +127,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private _hasUpdatedTelemetry: boolean = false; constructor( - private readonly _logService: ILogService, + @ILogService private readonly _logService: ILogService, private readonly _telemetryService?: ITelemetryService ) { super(); From 331fa9017a7d7d2370d7fc628f66bdfb61363da8 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 9 May 2022 16:11:07 -0700 Subject: [PATCH 014/942] add process property type --- src/vs/platform/terminal/common/terminal.ts | 4 +++- src/vs/platform/terminal/node/terminalProcess.ts | 5 ++++- .../contrib/terminal/browser/terminalProcessManager.ts | 7 ++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index c3d3657b6a9ce..91b3244bd3fe6 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -209,7 +209,8 @@ export const enum ProcessPropertyType { ShellType = 'shellType', HasChildProcesses = 'hasChildProcesses', ResolvedShellLaunchConfig = 'resolvedShellLaunchConfig', - OverrideDimensions = 'overrideDimensions' + OverrideDimensions = 'overrideDimensions', + FailedShellIntegrationActivation = 'failedShellIntegrationActivation' } export interface IProcessProperty { @@ -226,6 +227,7 @@ export interface IProcessPropertyMap { [ProcessPropertyType.HasChildProcesses]: boolean; [ProcessPropertyType.ResolvedShellLaunchConfig]: IShellLaunchConfig; [ProcessPropertyType.OverrideDimensions]: ITerminalDimensionsOverride | undefined; + [ProcessPropertyType.FailedShellIntegrationActivation]: boolean | undefined; } export interface IFixedTerminalDimensions { diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index a12bb5879ddeb..bc809abf0cde4 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -97,7 +97,8 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess shellType: undefined, hasChildProcesses: true, resolvedShellLaunchConfig: {}, - overrideDimensions: undefined + overrideDimensions: undefined, + failedShellIntegrationActivation: false }; private static _lastKillOrStart = 0; private _exitCode: number | undefined; @@ -213,6 +214,8 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess await fs.copyFile(f.source, f.dest); } } + } else { + this._onDidChangeProperty.fire({ type: ProcessPropertyType.FailedShellIntegrationActivation, value: true }); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 2a5080e8115f8..9c4280b385629 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -33,6 +33,7 @@ import { NaiveCwdDetectionCapability } from 'vs/platform/terminal/common/capabil import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { URI } from 'vs/base/common/uri'; import { ISerializedCommandDetectionCapability } from 'vs/platform/terminal/common/terminalProcess'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -130,7 +131,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce @IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService, @ITerminalProfileResolverService private readonly _terminalProfileResolverService: ITerminalProfileResolverService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService + @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, + @ITelemetryService private readonly _telemetryService: ITelemetryService ) { super(); @@ -332,6 +334,9 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce case ProcessPropertyType.HasChildProcesses: this._hasChildProcesses = value; break; + case ProcessPropertyType.FailedShellIntegrationActivation: + this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationFailedToActivateArgs'); + break; } this._onDidChangeProperty.fire({ type, value }); }) From 11ad58bfda475fe95767894aea65d3b97b735885 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 9 May 2022 16:31:41 -0700 Subject: [PATCH 015/942] add ShellIntegrationTelemetry --- src/vs/platform/terminal/common/terminal.ts | 9 +++++++++ .../terminal/common/xterm/shellIntegrationAddon.ts | 6 +++--- .../contrib/terminal/browser/terminalInstance.ts | 4 ++-- .../contrib/terminal/browser/terminalProcessManager.ts | 4 ++-- .../contrib/terminal/browser/xterm/xtermTerminal.ts | 3 ++- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 91b3244bd3fe6..95527630c529a 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -242,6 +242,15 @@ export interface IFixedTerminalDimensions { rows?: number; } + +export const enum ShellIntegrationTelemetry { + ActivationTimeout = 'terminal/shellIntegrationActivationTimeout', + FailureProcessExit = 'terminal/shellIntegrationFailureProcessExit', + FailureCustomArgs = 'terminal/shellIntegrationActivationFailureCustomArgs', + Success = 'terminal/shellIntegrationActivationSucceeded', + DisabledByUser = 'terminal/shellIntegrationDisabledByUser' +} + export interface IPtyHostController { readonly onPtyHostExit?: Event; readonly onPtyHostStart?: Event; diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index cfafc8907673d..aa9aca4d9d7fe 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IShellIntegration } from 'vs/platform/terminal/common/terminal'; +import { IShellIntegration, ShellIntegrationTelemetry } from 'vs/platform/terminal/common/terminal'; import { Disposable } from 'vs/base/common/lifecycle'; import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; import { CommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; @@ -143,7 +143,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private _handleVSCodeSequence(data: string): boolean { const didHandle = this._doHandleVSCodeSequence(data); if (!this._hasUpdatedTelemetry && didHandle) { - this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivated'); + this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.Success); this._hasUpdatedTelemetry = true; } return didHandle; @@ -227,7 +227,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private async _ensureCapabilitiesOrAddFailureTelemetry(): Promise { setTimeout(() => { if (!this.capabilities.get(TerminalCapability.CommandDetection) && !this.capabilities.get(TerminalCapability.CwdDetection)) { - this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationFailedToActivate'); + this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.ActivationTimeout); this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); } }, 10000); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 9af95baf33351..ce50ff14aad0e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -42,7 +42,7 @@ import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notif import { IProductService } from 'vs/platform/product/common/productService'; import { IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IProcessDataEvent, IProcessPropertyMap, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, ProcessPropertyType, TerminalIcon, TerminalLocation, TerminalSettingId, TerminalShellType, TitleEventSource, WindowsShellType } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IProcessPropertyMap, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, ProcessPropertyType, ShellIntegrationTelemetry, TerminalIcon, TerminalLocation, TerminalSettingId, TerminalShellType, TitleEventSource, WindowsShellType } from 'vs/platform/terminal/common/terminal'; import { escapeNonWindowsPath } from 'vs/platform/terminal/common/terminalEnvironment'; import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; @@ -1634,7 +1634,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } if (failedShellIntegrationInjection) { - this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationProcessExit'); + this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.FailureProcessExit); } // First onExit to consumers, this can happen after the terminal has already been disposed. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 9c4280b385629..8ad3c42cadd17 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -21,7 +21,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { EnvironmentVariableInfoChangesActive, EnvironmentVariableInfoStale } from 'vs/workbench/contrib/terminal/browser/environmentVariableInfo'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IEnvironmentVariableInfo, IEnvironmentVariableService, IMergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; -import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants, ITerminalDimensions, IProcessReadyEvent, IProcessProperty, ProcessPropertyType, IProcessPropertyMap, ITerminalProcessOptions, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants, ITerminalDimensions, IProcessReadyEvent, IProcessProperty, ProcessPropertyType, IProcessPropertyMap, ITerminalProcessOptions, TerminalSettingId, ShellIntegrationTelemetry } from 'vs/platform/terminal/common/terminal'; import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder'; import { localize } from 'vs/nls'; import { formatMessageForTerminal } from 'vs/workbench/contrib/terminal/common/terminalStrings'; @@ -335,7 +335,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce this._hasChildProcesses = value; break; case ProcessPropertyType.FailedShellIntegrationActivation: - this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationFailedToActivateArgs'); + this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.FailureCustomArgs); break; } this._onDidChangeProperty.fire({ type, value }); diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 485936bf0cb8c..5d9bed5f4f770 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -35,6 +35,7 @@ import { DecorationAddon } from 'vs/workbench/contrib/terminal/browser/xterm/dec import { ITerminalCapabilityStore, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; import { Emitter } from 'vs/base/common/event'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ShellIntegrationTelemetry } from 'vs/workbench/contrib/terminal/common/terminalStrings'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -613,7 +614,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { if (this._decorationAddon) { this._decorationAddon.dispose(); this._decorationAddon = undefined; - this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationDisabledByUser'); + this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.DisabledByUser); } } } From c252f1158d19a52e344e2262c7dc84ff158c094b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 9 May 2022 17:07:01 -0700 Subject: [PATCH 016/942] fix errors --- src/vs/workbench/contrib/terminal/browser/remotePty.ts | 3 ++- .../contrib/terminal/browser/xterm/xtermTerminal.ts | 6 +++--- .../workbench/contrib/terminal/electron-sandbox/localPty.ts | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/remotePty.ts b/src/vs/workbench/contrib/terminal/browser/remotePty.ts index cc7b5d1b6d52f..ab808bab234a3 100644 --- a/src/vs/workbench/contrib/terminal/browser/remotePty.ts +++ b/src/vs/workbench/contrib/terminal/browser/remotePty.ts @@ -37,7 +37,8 @@ export class RemotePty extends Disposable implements ITerminalChildProcess { shellType: undefined, hasChildProcesses: true, resolvedShellLaunchConfig: {}, - overrideDimensions: undefined + overrideDimensions: undefined, + failedShellIntegrationActivation: false }; get id(): number { return this._id; } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 5d9bed5f4f770..3e73fa309f0cb 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -13,7 +13,7 @@ import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configur import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { IShellIntegration, TerminalLocation, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { IShellIntegration, ShellIntegrationTelemetry, TerminalLocation, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { ITerminalFont, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { isSafari } from 'vs/base/browser/browser'; import { ICommandTracker, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -35,7 +35,6 @@ import { DecorationAddon } from 'vs/workbench/contrib/terminal/browser/xterm/dec import { ITerminalCapabilityStore, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; import { Emitter } from 'vs/base/common/event'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ShellIntegrationTelemetry } from 'vs/workbench/contrib/terminal/common/terminalStrings'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -614,7 +613,8 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { if (this._decorationAddon) { this._decorationAddon.dispose(); this._decorationAddon = undefined; - this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.DisabledByUser); + this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.DisabledByUser + ); } } } diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts index f840fbf52d16d..323dd56a2c157 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts @@ -24,7 +24,8 @@ export class LocalPty extends Disposable implements ITerminalChildProcess { shellType: undefined, hasChildProcesses: true, resolvedShellLaunchConfig: {}, - overrideDimensions: undefined + overrideDimensions: undefined, + failedShellIntegrationActivation: false }; private readonly _onProcessData = this._register(new Emitter()); readonly onProcessData = this._onProcessData.event; From beeb2e420210bb4673d239b85c7a59fc7b692dba Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 10 May 2022 09:37:43 +0200 Subject: [PATCH 017/942] save and dirty state for 3wm --- .../mergeEditor/browser/mergeEditorInput.ts | 63 +++++++++++++++++-- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts index 9a3c69d7cc26a..b50f8f925160c 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts @@ -3,13 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { DisposableStore } from 'vs/base/common/lifecycle'; import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { localize } from 'vs/nls'; +import { FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IUntypedEditorInput } from 'vs/workbench/common/editor'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IUntypedEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; export interface MergeEditorInputJSON { anchestor: URI; @@ -18,11 +25,12 @@ export interface MergeEditorInputJSON { result: URI; } -export class MergeEditorInput extends EditorInput { +export class MergeEditorInput extends AbstractTextResourceEditorInput { static readonly ID = 'mergeEditor.Input'; private _model?: MergeEditorModel; + private _outTextModel?: ITextFileEditorModel; constructor( private readonly _anchestor: URI, @@ -31,8 +39,32 @@ export class MergeEditorInput extends EditorInput { private readonly _result: URI, @IInstantiationService private readonly _instaService: IInstantiationService, @ITextModelService private readonly _textModelService: ITextModelService, + @IEditorService editorService: IEditorService, + @ITextFileService textFileService: ITextFileService, + @ILabelService labelService: ILabelService, + @IFileService fileService: IFileService ) { - super(); + super(_result, undefined, editorService, textFileService, labelService, fileService); + + const modelListener = new DisposableStore(); + const handleDidCreate = (model: ITextFileEditorModel) => { + // TODO@jrieken copied from fileEditorInput.ts + if (isEqual(_result, model.resource)) { + modelListener.clear(); + this._outTextModel = model; + modelListener.add(model.onDidChangeDirty(() => this._onDidChangeDirty.fire())); + modelListener.add(model.onDidSaveError(() => this._onDidChangeDirty.fire())); + + modelListener.add(model.onDidChangeReadonly(() => this._onDidChangeCapabilities.fire())); + + modelListener.add(model.onWillDispose(() => { + this._outTextModel = undefined; + modelListener.clear(); + })); + } + }; + textFileService.files.onDidCreate(handleDidCreate, this, modelListener); + textFileService.files.models.forEach(handleDidCreate); } override dispose(): void { @@ -43,11 +75,20 @@ export class MergeEditorInput extends EditorInput { return MergeEditorInput.ID; } - get resource(): URI | undefined { - return this._result; + override getName(): string { + return localize('name', "Merging: {0}", super.getName()); + } + + override get capabilities(): EditorInputCapabilities { + let result = EditorInputCapabilities.Singleton; + if (!this.fileService.hasProvider(this._result) || this.fileService.hasCapability(this.resource, FileSystemProviderCapabilities.Readonly)) { + result |= EditorInputCapabilities.Readonly; + } + return result; } override async resolve(): Promise { + if (!this._model) { const anchestor = await this._textModelService.createModelReference(this._anchestor); @@ -68,6 +109,8 @@ export class MergeEditorInput extends EditorInput { this._store.add(inputOne); this._store.add(inputTwo); this._store.add(result); + + // result.object. } return this._model; } @@ -90,4 +133,14 @@ export class MergeEditorInput extends EditorInput { result: this._result, }; } + + // ---- FileEditorInput + + override isDirty(): boolean { + return Boolean(this._outTextModel?.isDirty()); + } + + + // implement get/set languageId + // implement get/set encoding } From 8614c14318abbc33bd759bf979d1f0df954b2c58 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 10 May 2022 11:52:32 +0200 Subject: [PATCH 018/942] dispose model listener --- src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts index b50f8f925160c..cb5773e9f8694 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts @@ -65,6 +65,7 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { }; textFileService.files.onDidCreate(handleDidCreate, this, modelListener); textFileService.files.models.forEach(handleDidCreate); + this._store.add(modelListener); } override dispose(): void { From 1cd79ce2d97edbf56c464040f707dcd8b53a25bb Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 10 May 2022 11:55:51 +0200 Subject: [PATCH 019/942] add grid border color --- src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index c7c4d8e93589b..e8f02b90ea240 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -23,6 +23,8 @@ import { ITextModel } from 'vs/editor/common/model'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ScrollType } from 'vs/editor/common/editorCommon'; +import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; +import { Color } from 'vs/base/common/color'; export class MergeEditor extends EditorPane { @@ -77,7 +79,7 @@ export class MergeEditor extends EditorPane { } protected createEditor(parent: HTMLElement): void { - this._grid = new Grid(this.inputResultView); + this._grid = new Grid(this.inputResultView, { styles: { separatorBorder: this.theme.getColor(settingsSashBorder) ?? Color.transparent } }); this._grid.addView(this.inputOneView, Sizing.Distribute, this.inputResultView, Direction.Up); this._grid.addView(this.inputTwoView, Sizing.Distribute, this.inputOneView, Direction.Right); From 9bcc9a1d3e5798f4b494ce066cbe3a2e2a90f3a3 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 10 May 2022 12:17:54 +0200 Subject: [PATCH 020/942] add title area above code editor, add css styles, little :lipstick: --- .../mergeEditor/browser/media/mergeEditor.css | 11 +++++ .../mergeEditor/browser/mergeEditor.ts | 42 ++++++++++++------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css index a4a092d83492f..2a1dd749cbb69 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css +++ b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css @@ -2,3 +2,14 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .merge-editor .code-view>.title { + padding: 0 0 0 10px; + height: 30px; + display: flex; + align-content: center; +} + +.monaco-workbench .merge-editor .code-view>.title .monaco-icon-label { + margin: auto 0; +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index e8f02b90ea240..7029d266c3079 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -17,7 +17,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { BugIndicatingError } from 'vs/base/common/errors'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { Direction, Grid, IView, IViewSize, LayoutPriority } from 'vs/base/browser/ui/grid/grid'; +import { Direction, Grid, IView, IViewSize } from 'vs/base/browser/ui/grid/grid'; import { Sizing } from 'vs/base/browser/ui/splitview/splitview'; import { ITextModel } from 'vs/editor/common/model'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; @@ -25,6 +25,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ScrollType } from 'vs/editor/common/editorCommon'; import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { Color } from 'vs/base/common/color'; +import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { localize } from 'vs/nls'; +import { ILabelService } from 'vs/platform/label/common/label'; export class MergeEditor extends EditorPane { @@ -40,6 +43,7 @@ export class MergeEditor extends EditorPane { constructor( @IInstantiationService private readonly instantiation: IInstantiationService, + @ILabelService private readonly _labelService: ILabelService, @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, @IThemeService themeService: IThemeService, @@ -79,6 +83,7 @@ export class MergeEditor extends EditorPane { } protected createEditor(parent: HTMLElement): void { + parent.classList.add('merge-editor'); this._grid = new Grid(this.inputResultView, { styles: { separatorBorder: this.theme.getColor(settingsSashBorder) ?? Color.transparent } }); this._grid.addView(this.inputOneView, Sizing.Distribute, this.inputResultView, Direction.Up); @@ -99,14 +104,10 @@ export class MergeEditor extends EditorPane { this._sessionDisposables.clear(); const model = await input.resolve(); - this.inputOneView.setModel(model.inputOne); - this.inputTwoView.setModel(model.inputTwo); - this.inputResultView.setModel(model.result); + this.inputOneView.setModel(model.inputOne, localize('yours', 'Yours'), undefined); + this.inputTwoView.setModel(model.inputTwo, localize('theirs', 'Theirs',), undefined); + this.inputResultView.setModel(model.result, localize('result', 'Result',), this._labelService.getUriLabel(model.result.uri, { relative: true })); - console.log(model); - // if (token.isCancellationRequested) { - // return; - // } } override clearInput(): void { @@ -116,36 +117,47 @@ export class MergeEditor extends EditorPane { } class CodeEditorView implements IView { - preferredWidth?: number | undefined; - preferredHeight?: number | undefined; + + // preferredWidth?: number | undefined; + // preferredHeight?: number | undefined; element: HTMLElement = document.createElement('div'); + private _titleElement = document.createElement('div'); + private _editorElement = document.createElement('div'); minimumWidth: number = 10; maximumWidth: number = Number.MAX_SAFE_INTEGER; minimumHeight: number = 10; maximumHeight: number = Number.MAX_SAFE_INTEGER; - priority?: LayoutPriority | undefined; - snap?: boolean | undefined; + // priority?: LayoutPriority | undefined; + // snap?: boolean | undefined; private readonly _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; + private _title = new IconLabel(this._titleElement, { supportIcons: true }); + public readonly editor = this.instantiationService.createInstance( CodeEditorWidget, - this.element, + this._editorElement, { minimap: { enabled: false } }, {} ); + constructor( @IInstantiationService private readonly instantiationService: IInstantiationService ) { + this.element.classList.add('code-view'); + this._titleElement.classList.add('title'); + this.element.appendChild(this._titleElement); + this.element.appendChild(this._editorElement); } - public setModel(model: ITextModel | undefined): void { + public setModel(model: ITextModel, title: string, description: string | undefined): void { this.editor.setModel(model); + this._title.setLabel(title, description); } layout(width: number, height: number, top: number, left: number): void { @@ -153,7 +165,7 @@ class CodeEditorView implements IView { this.element.style.height = `${height}px`; this.element.style.top = `${top}px`; this.element.style.left = `${left}px`; - this.editor.layout({ width, height }); + this.editor.layout({ width, height: height - this._titleElement.clientHeight }); } } From 45abbdf08690a5011dae1eb87980eea005080595 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 10 May 2022 12:32:54 +0200 Subject: [PATCH 021/942] ensure default implementation of `clearInput` runs, make sure editor can be re-opened... --- .../workbench/contrib/mergeEditor/browser/mergeEditor.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 7029d266c3079..8afe3dbd7373b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -110,9 +110,13 @@ export class MergeEditor extends EditorPane { } - override clearInput(): void { + // override clearInput(): void { + // super.clearInput(); + // } - } + // protected override setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { + // console.log('VISISBLE', visible); + // } } From 44acc7887bc9869ffacf49d62a797ce3916bb86b Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 10 May 2022 12:37:52 +0200 Subject: [PATCH 022/942] add floating btn for merge (all fake tho...) --- .../workbench/contrib/mergeEditor/browser/mergeEditor.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 8afe3dbd7373b..ac0265355b6fd 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -28,6 +28,7 @@ import { Color } from 'vs/base/common/color'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { localize } from 'vs/nls'; import { ILabelService } from 'vs/platform/label/common/label'; +import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; export class MergeEditor extends EditorPane { @@ -75,6 +76,12 @@ export class MergeEditor extends EditorPane { }); } })); + + // TODO@jrieken make this proper: add menu id and allow extensions to contribute + const acceptBtn = this.instantiation.createInstance(FloatingClickWidget, this.inputResultView.editor, 'Accept Merge', ''); + acceptBtn.render(); + this._store.add(acceptBtn.onClick(() => { console.log('DO IT'); })); + this._store.add(acceptBtn); } override dispose(): void { From c7fa36480e452bc30b11061e56330d0b68e22f1a Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 10 May 2022 12:53:19 +0200 Subject: [PATCH 023/942] implement `getControl` and `scopedContextKeyService` for better interaction with "outside", add `ICodeEditorViewOptions` and make yours/theirs readonly --- .../mergeEditor/browser/mergeEditor.ts | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index ac0265355b6fd..3a1e6b6807915 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -12,7 +12,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; -import { IEditorOpenContext } from 'vs/workbench/common/editor'; +import { IEditorControl, IEditorOpenContext } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { BugIndicatingError } from 'vs/base/common/errors'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; @@ -29,6 +29,8 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { localize } from 'vs/nls'; import { ILabelService } from 'vs/platform/label/common/label'; import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; export class MergeEditor extends EditorPane { @@ -36,11 +38,11 @@ export class MergeEditor extends EditorPane { private readonly _sessionDisposables = new DisposableStore(); - private _grid!: Grid; + private _grid!: Grid; - private readonly inputOneView = this.instantiation.createInstance(CodeEditorView); - private readonly inputTwoView = this.instantiation.createInstance(CodeEditorView); - private readonly inputResultView = this.instantiation.createInstance(CodeEditorView); + private readonly inputOneView = this.instantiation.createInstance(CodeEditorView, { readonly: true }); + private readonly inputTwoView = this.instantiation.createInstance(CodeEditorView, { readonly: true }); + private readonly inputResultView = this.instantiation.createInstance(CodeEditorView, { readonly: false }); constructor( @IInstantiationService private readonly instantiation: IInstantiationService, @@ -125,6 +127,27 @@ export class MergeEditor extends EditorPane { // console.log('VISISBLE', visible); // } + // ---- interact with "outside world" via `getControl`, `scopedContextKeyService` + + override getControl(): IEditorControl | undefined { + for (const view of [this.inputOneView, this.inputTwoView, this.inputResultView]) { + if (view.editor.hasWidgetFocus()) { + return view.editor; + } + } + return undefined; + } + + override get scopedContextKeyService(): IContextKeyService | undefined { + const control = this.getControl(); + return isCodeEditor(control) + ? control.invokeWithinContext(accessor => accessor.get(IContextKeyService)) + : undefined; + } +} + +interface ICodeEditorViewOptions { + readonly: boolean; } class CodeEditorView implements IView { @@ -151,14 +174,14 @@ class CodeEditorView implements IView { public readonly editor = this.instantiationService.createInstance( CodeEditorWidget, this._editorElement, - { minimap: { enabled: false } }, + { minimap: { enabled: false }, readOnly: this._options.readonly }, {} ); constructor( - @IInstantiationService - private readonly instantiationService: IInstantiationService + private readonly _options: ICodeEditorViewOptions, + @IInstantiationService private readonly instantiationService: IInstantiationService ) { this.element.classList.add('code-view'); this._titleElement.classList.add('title'); From e4c04ea2e99dc6afc6f2c645ec4541ec8df9ed09 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 10 May 2022 13:49:31 +0200 Subject: [PATCH 024/942] - Introduce policy model with file policy - Introduce policy configuration using policy model - Use policy configuration while reading, inspecting and writing configuration - Adopt json settings editor --- .../sharedProcess/sharedProcessMain.ts | 2 +- src/vs/code/electron-main/main.ts | 2 +- src/vs/code/node/cliProcessMain.ts | 2 +- .../standalone/browser/standaloneServices.ts | 6 +- .../configuration/common/configuration.ts | 3 + .../common/configurationModels.ts | 82 +++++----- .../common/configurationService.ts | 36 +++-- .../configuration/common/configurations.ts | 143 ++++++++++++++++++ .../test/common/configurationModels.test.ts | 33 ++-- .../test/common/configurationService.test.ts | 26 ++-- src/vs/platform/environment/common/argv.ts | 1 + .../environment/common/environment.ts | 3 + .../environment/common/environmentService.ts | 3 + src/vs/platform/environment/node/argv.ts | 1 + src/vs/platform/policy/common/policy.ts | 112 ++++++++++++++ .../test/common/userDataSyncClient.ts | 2 +- .../node/remoteExtensionHostAgentCli.ts | 2 +- src/vs/server/node/serverServices.ts | 2 +- .../api/common/extHostConfiguration.ts | 2 +- .../test/browser/extHostConfiguration.test.ts | 5 + .../browser/preferencesRenderers.ts | 26 ++++ .../configuration/browser/configuration.ts | 32 ++-- .../browser/configurationService.ts | 32 +++- .../common/configurationEditingService.ts | 10 ++ .../common/configurationModels.ts | 3 +- .../test/common/configurationModels.test.ts | 8 +- 26 files changed, 458 insertions(+), 121 deletions(-) create mode 100644 src/vs/platform/configuration/common/configurations.ts create mode 100644 src/vs/platform/policy/common/policy.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 4dd6d42da9b96..aa4c7ad67e7b7 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -222,7 +222,7 @@ class SharedProcessMain extends Disposable { fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider); // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, environmentService, logService)); services.set(IConfigurationService, configurationService); // Storage (global access only) diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 762f288dabaa2..7b95aaad6182c 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -167,7 +167,7 @@ class CodeMain { services.set(ILoggerService, new LoggerService(logService, fileService)); // Configuration - const configurationService = new ConfigurationService(environmentMainService.settingsResource, fileService); + const configurationService = new ConfigurationService(environmentMainService.settingsResource, fileService, environmentMainService, logService); services.set(IConfigurationService, configurationService); // Lifecycle diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 9495713716e3a..6ae96fc2047a3 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -129,7 +129,7 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, diskFileSystemProvider); // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, environmentService, logService)); services.set(IConfigurationService, configurationService); // Init config diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index ef4d4407aa928..52803c25a6a50 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -29,7 +29,7 @@ import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService import { ITextResourceConfigurationService, ITextResourcePropertiesService, ITextResourceConfigurationChangeEvent } from 'vs/editor/common/services/textResourceConfiguration'; import { CommandsRegistry, ICommandEvent, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationModel, IConfigurationValue, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { Configuration, ConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels'; +import { Configuration, ConfigurationModel, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels'; import { IContextKeyService, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService, IInputResult, IShowResult } from 'vs/platform/dialogs/common/dialogs'; import { createDecorator, IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; @@ -88,6 +88,7 @@ import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/com import { staticObservableValue } from 'vs/base/common/observableValue'; import 'vs/editor/common/services/languageFeaturesService'; +import { DefaultConfigurationModel } from 'vs/platform/configuration/common/configurations'; class SimpleModel implements IResolvedTextEditorModel { @@ -521,7 +522,7 @@ export class StandaloneConfigurationService implements IConfigurationService { private readonly _configuration: Configuration; constructor() { - this._configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel()); + this._configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); } getValue(): T; @@ -582,6 +583,7 @@ export class StandaloneConfigurationService implements IConfigurationService { }; return { defaults: emptyModel, + policy: emptyModel, user: emptyModel, workspace: emptyModel, folders: [] diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index 6ed2b0c3ef8f7..4012d40585819 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -80,6 +80,7 @@ export interface IConfigurationValue { readonly workspaceValue?: T; readonly workspaceFolderValue?: T; readonly memoryValue?: T; + readonly policyValue?: T; readonly value?: T; readonly default?: { value?: T; override?: T }; @@ -89,6 +90,7 @@ export interface IConfigurationValue { readonly workspace?: { value?: T; override?: T }; readonly workspaceFolder?: { value?: T; override?: T }; readonly memory?: { value?: T; override?: T }; + readonly policy?: { value?: T }; readonly overrideIdentifiers?: string[]; } @@ -163,6 +165,7 @@ export interface IOverrides { export interface IConfigurationData { defaults: IConfigurationModel; + policy: IConfigurationModel; user: IConfigurationModel; workspace: IConfigurationModel; folders: [UriComponents, IConfigurationModel][]; diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 8afa3b7f0654f..03135dbdbb4bc 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -232,33 +232,6 @@ export class ConfigurationModel implements IConfigurationModel { } } -export class DefaultConfigurationModel extends ConfigurationModel { - - constructor(configurationDefaultsOverrides: IStringDictionary = {}) { - const properties = Registry.as(Extensions.Configuration).getConfigurationProperties(); - const keys = Object.keys(properties); - const contents: any = Object.create(null); - const overrides: IOverrides[] = []; - - for (const key in properties) { - const defaultOverrideValue = configurationDefaultsOverrides[key]; - const value = defaultOverrideValue !== undefined ? defaultOverrideValue : properties[key].default; - addToValueTree(contents, key, value, message => console.error(`Conflict in default settings: ${message}`)); - } - for (const key of Object.keys(contents)) { - if (OVERRIDE_PROPERTY_REGEX.test(key)) { - overrides.push({ - identifiers: overrideIdentifiersFromKey(key), - keys: Object.keys(contents[key]), - contents: toValuesTree(contents[key], message => console.error(`Conflict in default settings file: ${message}`)), - }); - } - } - - super(contents, keys, overrides); - } -} - export interface ConfigurationParseOptions { scopes: ConfigurationScope[] | undefined; skipRestricted?: boolean; @@ -473,6 +446,7 @@ export class Configuration { constructor( private _defaultConfiguration: ConfigurationModel, + private _policyConfiguration: ConfigurationModel, private _localUserConfiguration: ConfigurationModel, private _remoteUserConfiguration: ConfigurationModel = new ConfigurationModel(), private _workspaceConfiguration: ConfigurationModel = new ConfigurationModel(), @@ -483,7 +457,7 @@ export class Configuration { } getValue(section: string | undefined, overrides: IConfigurationOverrides, workspace: Workspace | undefined): any { - const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides, workspace); + const consolidateConfigurationModel = this.getConsolidatedConfigurationModel(section, overrides, workspace); return consolidateConfigurationModel.getValue(section); } @@ -511,11 +485,12 @@ export class Configuration { } inspect(key: string, overrides: IConfigurationOverrides, workspace: Workspace | undefined): IConfigurationValue { - const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides, workspace); + const consolidateConfigurationModel = this.getConsolidatedConfigurationModel(key, overrides, workspace); const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource, workspace); const memoryConfigurationModel = overrides.resource ? this._memoryConfigurationByResource.get(overrides.resource) || this._memoryConfiguration : this._memoryConfiguration; const defaultValue = overrides.overrideIdentifier ? this._defaultConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this._defaultConfiguration.freeze().getValue(key); + const policyValue = this._policyConfiguration.freeze().getValue(key); const userValue = overrides.overrideIdentifier ? this.userConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this.userConfiguration.freeze().getValue(key); const userLocalValue = overrides.overrideIdentifier ? this.localUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this.localUserConfiguration.freeze().getValue(key); const userRemoteValue = overrides.overrideIdentifier ? this.remoteUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this.remoteUserConfiguration.freeze().getValue(key); @@ -523,19 +498,21 @@ export class Configuration { const workspaceFolderValue = folderConfigurationModel ? overrides.overrideIdentifier ? folderConfigurationModel.freeze().override(overrides.overrideIdentifier).getValue(key) : folderConfigurationModel.freeze().getValue(key) : undefined; const memoryValue = overrides.overrideIdentifier ? memoryConfigurationModel.override(overrides.overrideIdentifier).getValue(key) : memoryConfigurationModel.getValue(key); const value = consolidateConfigurationModel.getValue(key); - const overrideIdentifiers: string[] = arrays.distinct(arrays.flatten(consolidateConfigurationModel.overrides.map(override => override.identifiers))).filter(overrideIdentifier => consolidateConfigurationModel.getOverrideValue(key, overrideIdentifier) !== undefined); + const overrideIdentifiers: string[] = arrays.distinct(consolidateConfigurationModel.overrides.map(override => override.identifiers).flat()).filter(overrideIdentifier => consolidateConfigurationModel.getOverrideValue(key, overrideIdentifier) !== undefined); return { - defaultValue: defaultValue, - userValue: userValue, - userLocalValue: userLocalValue, - userRemoteValue: userRemoteValue, - workspaceValue: workspaceValue, - workspaceFolderValue: workspaceFolderValue, - memoryValue: memoryValue, + defaultValue, + policyValue, + userValue, + userLocalValue, + userRemoteValue, + workspaceValue, + workspaceFolderValue, + memoryValue, value, default: defaultValue !== undefined ? { value: this._defaultConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this._defaultConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined, + policy: policyValue !== undefined ? { value: policyValue } : undefined, user: userValue !== undefined ? { value: this.userConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this.userConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined, userLocal: userLocalValue !== undefined ? { value: this.localUserConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this.localUserConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined, userRemote: userRemoteValue !== undefined ? { value: this.remoteUserConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this.remoteUserConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined, @@ -568,6 +545,10 @@ export class Configuration { this._foldersConsolidatedConfigurations.clear(); } + updatePolicyConfiguration(policyConfiguration: ConfigurationModel): void { + this._policyConfiguration = policyConfiguration; + } + updateLocalUserConfiguration(localUserConfiguration: ConfigurationModel): void { this._localUserConfiguration = localUserConfiguration; this._userConfiguration = null; @@ -620,6 +601,15 @@ export class Configuration { return { keys, overrides }; } + compareAndUpdatePolicyConfiguration(policyConfiguration: ConfigurationModel): IConfigurationChange { + const { added, updated, removed } = compare(this._policyConfiguration, policyConfiguration); + const keys = [...added, ...updated, ...removed]; + if (keys.length) { + this.updatePolicyConfiguration(policyConfiguration); + } + return { keys, overrides: [] }; + } + compareAndUpdateLocalUserConfiguration(user: ConfigurationModel): IConfigurationChange { const { added, updated, removed, overrides } = compare(this.localUserConfiguration, user); const keys = [...added, ...updated, ...removed]; @@ -698,9 +688,15 @@ export class Configuration { return this._folderConfigurations; } - private getConsolidateConfigurationModel(overrides: IConfigurationOverrides, workspace: Workspace | undefined): ConfigurationModel { + private getConsolidatedConfigurationModel(section: string | undefined, overrides: IConfigurationOverrides, workspace: Workspace | undefined): ConfigurationModel { let configurationModel = this.getConsolidatedConfigurationModelForResource(overrides, workspace); - return overrides.overrideIdentifier ? configurationModel.override(overrides.overrideIdentifier) : configurationModel; + if (overrides.overrideIdentifier) { + configurationModel = configurationModel.override(overrides.overrideIdentifier); + } + if (!this._policyConfiguration.isEmpty() && this._policyConfiguration.getValue(section) !== undefined) { + configurationModel = configurationModel.merge(this._policyConfiguration); + } + return configurationModel; } private getConsolidatedConfigurationModelForResource({ resource }: IConfigurationOverrides, workspace: Workspace | undefined): ConfigurationModel { @@ -765,6 +761,11 @@ export class Configuration { overrides: this._defaultConfiguration.overrides, keys: this._defaultConfiguration.keys }, + policy: { + contents: this._policyConfiguration.contents, + overrides: this._policyConfiguration.overrides, + keys: this._policyConfiguration.keys + }, user: { contents: this.userConfiguration.contents, overrides: this.userConfiguration.overrides, @@ -812,13 +813,14 @@ export class Configuration { static parse(data: IConfigurationData): Configuration { const defaultConfiguration = this.parseConfigurationModel(data.defaults); + const policyConfiguration = this.parseConfigurationModel(data.policy); const userConfiguration = this.parseConfigurationModel(data.user); const workspaceConfiguration = this.parseConfigurationModel(data.workspace); const folders: ResourceMap = data.folders.reduce((result, value) => { result.set(URI.revive(value[0]), this.parseConfigurationModel(value[1])); return result; }, new ResourceMap()); - return new Configuration(defaultConfiguration, userConfiguration, new ConfigurationModel(), workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap(), false); + return new Configuration(defaultConfiguration, policyConfiguration, userConfiguration, new ConfigurationModel(), workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap(), false); } private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel { diff --git a/src/vs/platform/configuration/common/configurationService.ts b/src/vs/platform/configuration/common/configurationService.ts index 7860c72ea8e95..b3ff50a6d0cd8 100644 --- a/src/vs/platform/configuration/common/configurationService.ts +++ b/src/vs/platform/configuration/common/configurationService.ts @@ -9,17 +9,20 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ConfigurationTarget, IConfigurationChange, IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; -import { Configuration, ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel, UserSettings } from 'vs/platform/configuration/common/configurationModels'; -import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { Configuration, ConfigurationChangeEvent, ConfigurationModel, UserSettings } from 'vs/platform/configuration/common/configurationModels'; +import { DefaultConfiguration, PolicyConfiguration } from 'vs/platform/configuration/common/configurations'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; -import { Registry } from 'vs/platform/registry/common/platform'; +import { ILogService } from 'vs/platform/log/common/log'; export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable { declare readonly _serviceBrand: undefined; private configuration: Configuration; - private userConfiguration: UserSettings; + private readonly defaultConfiguration: DefaultConfiguration; + private readonly policyConfiguration: PolicyConfiguration; + private readonly userConfiguration: UserSettings; private readonly reloadConfigurationScheduler: RunOnceScheduler; private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); @@ -27,20 +30,25 @@ export class ConfigurationService extends Disposable implements IConfigurationSe constructor( private readonly settingsResource: URI, - fileService: IFileService + fileService: IFileService, + environmentService: IEnvironmentService, + logService: ILogService, ) { super(); + this.defaultConfiguration = this._register(new DefaultConfiguration()); + this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, fileService, environmentService, logService)); this.userConfiguration = this._register(new UserSettings(this.settingsResource, undefined, extUriBiasedIgnorePathCase, fileService)); - this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel()); + this.configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, new ConfigurationModel()); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reloadConfiguration(), 50)); - this._register(Registry.as(Extensions.Configuration).onDidUpdateConfiguration(({ properties }) => this.onDidDefaultConfigurationChange(properties))); + this._register(this.defaultConfiguration.onDidChangeConfiguration(({ defaults, properties }) => this.onDidDefaultConfigurationChange(defaults, properties))); + this._register(this.policyConfiguration.onDidChangeConfiguration(model => this.onDidPolicyConfigurationChange(model))); this._register(this.userConfiguration.onDidChange(() => this.reloadConfigurationScheduler.schedule())); } async initialize(): Promise { - const userConfiguration = await this.userConfiguration.loadConfiguration(); - this.configuration = new Configuration(new DefaultConfigurationModel(), userConfiguration); + const [defaultModel, policyModel, userModel] = await Promise.all([this.defaultConfiguration.initialize(), this.policyConfiguration.initialize(), this.userConfiguration.loadConfiguration()]); + this.configuration = new Configuration(defaultModel, policyModel, userModel); } getConfigurationData(): IConfigurationData { @@ -89,9 +97,15 @@ export class ConfigurationService extends Disposable implements IConfigurationSe this.trigger(change, previous, ConfigurationTarget.USER); } - private onDidDefaultConfigurationChange(properties: string[]): void { + private onDidDefaultConfigurationChange(defaultConfigurationModel: ConfigurationModel, properties: string[]): void { const previous = this.configuration.toData(); - const change = this.configuration.compareAndUpdateDefaultConfiguration(new DefaultConfigurationModel(), properties); + const change = this.configuration.compareAndUpdateDefaultConfiguration(defaultConfigurationModel, properties); + this.trigger(change, previous, ConfigurationTarget.DEFAULT); + } + + private onDidPolicyConfigurationChange(policyConfiguration: ConfigurationModel): void { + const previous = this.configuration.toData(); + const change = this.configuration.compareAndUpdatePolicyConfiguration(policyConfiguration); this.trigger(change, previous, ConfigurationTarget.DEFAULT); } diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts new file mode 100644 index 0000000000000..a86a1f50e495f --- /dev/null +++ b/src/vs/platform/configuration/common/configurations.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IStringDictionary } from 'vs/base/common/collections'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { addToValueTree, IOverrides, toValuesTree } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; +import { Extensions, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +import { PolicyModel } from 'vs/platform/policy/common/policy'; +import { Registry } from 'vs/platform/registry/common/platform'; + +export class DefaultConfiguration extends Disposable { + + private readonly _onDidChangeConfiguration = this._register(new Emitter<{ defaults: ConfigurationModel; properties: string[] }>()); + readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event; + + private _configurationModel: ConfigurationModel | undefined; + get configurationModel(): ConfigurationModel { + if (!this._configurationModel) { + this._configurationModel = new DefaultConfigurationModel(this.getConfigurationDefaultOverrides()); + } + return this._configurationModel; + } + + async initialize(): Promise { + this._configurationModel = undefined; + this._register(Registry.as(Extensions.Configuration).onDidUpdateConfiguration(({ properties, defaultsOverrides }) => this.onDidUpdateConfiguration(properties, defaultsOverrides))); + return this.configurationModel; + } + + reload(): ConfigurationModel { + this._configurationModel = undefined; + return this.configurationModel; + } + + protected onDidUpdateConfiguration(properties: string[], defaultsOverrides?: boolean): void { + this._configurationModel = undefined; + this._onDidChangeConfiguration.fire({ defaults: this.configurationModel, properties }); + } + + protected getConfigurationDefaultOverrides(): IStringDictionary { + return {}; + } + +} + +export class DefaultConfigurationModel extends ConfigurationModel { + + constructor(configurationDefaultsOverrides: IStringDictionary = {}) { + const properties = Registry.as(Extensions.Configuration).getConfigurationProperties(); + const keys = Object.keys(properties); + const contents: any = Object.create(null); + const overrides: IOverrides[] = []; + + for (const key in properties) { + const defaultOverrideValue = configurationDefaultsOverrides[key]; + const value = defaultOverrideValue !== undefined ? defaultOverrideValue : properties[key].default; + addToValueTree(contents, key, value, message => console.error(`Conflict in default settings: ${message}`)); + } + for (const key of Object.keys(contents)) { + if (OVERRIDE_PROPERTY_REGEX.test(key)) { + overrides.push({ + identifiers: overrideIdentifiersFromKey(key), + keys: Object.keys(contents[key]), + contents: toValuesTree(contents[key], message => console.error(`Conflict in default settings file: ${message}`)), + }); + } + } + + super(contents, keys, overrides); + } +} + +export class PolicyConfiguration extends Disposable { + + private readonly _onDidChangeConfiguration = this._register(new Emitter()); + readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event; + + private readonly policies: PolicyModel; + + constructor( + private readonly defaultConfiguration: DefaultConfiguration, + fileService: IFileService, + environmentService: IEnvironmentService, + logService: ILogService + ) { + super(); + this.policies = new PolicyModel(fileService, environmentService, logService); + } + + private _configurationModel: ConfigurationModel | undefined; + get configurationModel(): ConfigurationModel { + if (!this._configurationModel) { + const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); + const keys: string[] = []; + const contents: any = Object.create(null); + for (const key of this.defaultConfiguration.configurationModel.keys) { + const policyName = configurationProperties[key].policy?.name; + if (!policyName) { + continue; + } + const value = this.policies.getPolicy(policyName); + if (value === undefined) { + continue; + } + keys.push(key); + addToValueTree(contents, key, value, message => console.error(`Conflict in policy settings: ${message}`)); + } + this._configurationModel = new ConfigurationModel(contents, keys, []); + } + return this._configurationModel; + } + + async initialize(): Promise { + await this.policies.initialize(); + this._register(this.policies.onDidChange(e => this.onDidChange())); + this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties }) => this.onDidDefaultConfigurationChange(properties))); + return this.reload(); + } + + reload(): ConfigurationModel { + this._configurationModel = undefined; + return this.configurationModel; + } + + private onDidDefaultConfigurationChange(properties: string[]): void { + const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); + if (properties.some(key => configurationProperties[key].policy?.name)) { + this.onDidChange(); + } + } + + private onDidChange(): void { + this._onDidChangeConfiguration.fire(this.reload()); + } + +} diff --git a/src/vs/platform/configuration/test/common/configurationModels.test.ts b/src/vs/platform/configuration/test/common/configurationModels.test.ts index ae1add24a7887..9887f80e18431 100644 --- a/src/vs/platform/configuration/test/common/configurationModels.test.ts +++ b/src/vs/platform/configuration/test/common/configurationModels.test.ts @@ -6,8 +6,9 @@ import * as assert from 'assert'; import { join } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { AllKeysConfigurationChangeEvent, Configuration, ConfigurationChangeEvent, ConfigurationModel, ConfigurationModelParser, DefaultConfigurationModel, mergeChanges } from 'vs/platform/configuration/common/configurationModels'; +import { AllKeysConfigurationChangeEvent, Configuration, ConfigurationChangeEvent, ConfigurationModel, ConfigurationModelParser, mergeChanges } from 'vs/platform/configuration/common/configurationModels'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { DefaultConfigurationModel } from 'vs/platform/configuration/common/configurations'; import { Registry } from 'vs/platform/registry/common/platform'; import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; @@ -496,7 +497,7 @@ suite('Configuration', () => { test('Test update value', () => { const parser = new ConfigurationModelParser('test'); parser.parse(JSON.stringify({ 'a': 1 })); - const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel()); + const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel(), new ConfigurationModel()); testObject.updateValue('a', 2); @@ -506,7 +507,7 @@ suite('Configuration', () => { test('Test update value after inspect', () => { const parser = new ConfigurationModelParser('test'); parser.parse(JSON.stringify({ 'a': 1 })); - const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel()); + const testObject: Configuration = new Configuration(parser.configurationModel, new ConfigurationModel(), new ConfigurationModel()); testObject.inspect('a', {}, undefined); testObject.updateValue('a', 2); @@ -515,7 +516,7 @@ suite('Configuration', () => { }); test('Test compare and update default configuration', () => { - const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); testObject.updateDefaultConfiguration(toConfigurationModel({ 'editor.lineNumbers': 'on', })); @@ -532,7 +533,7 @@ suite('Configuration', () => { }); test('Test compare and update user configuration', () => { - const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); testObject.updateLocalUserConfiguration(toConfigurationModel({ 'editor.lineNumbers': 'off', 'editor.fontSize': 12, @@ -555,7 +556,7 @@ suite('Configuration', () => { }); test('Test compare and update workspace configuration', () => { - const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); testObject.updateWorkspaceConfiguration(toConfigurationModel({ 'editor.lineNumbers': 'off', 'editor.fontSize': 12, @@ -578,7 +579,7 @@ suite('Configuration', () => { }); test('Test compare and update workspace folder configuration', () => { - const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); testObject.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({ 'editor.lineNumbers': 'off', 'editor.fontSize': 12, @@ -601,7 +602,7 @@ suite('Configuration', () => { }); test('Test compare and delete workspace folder configuration', () => { - const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); testObject.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({ 'editor.lineNumbers': 'off', 'editor.fontSize': 12, @@ -627,7 +628,7 @@ suite('Configuration', () => { suite('ConfigurationChangeEvent', () => { test('changeEvent affecting keys with new configuration', () => { - const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({ 'window.zoomLevel': 1, 'workbench.editor.enablePreview': false, @@ -653,7 +654,7 @@ suite('ConfigurationChangeEvent', () => { }); test('changeEvent affecting keys when configuration changed', () => { - const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); configuration.updateLocalUserConfiguration(toConfigurationModel({ 'window.zoomLevel': 2, 'workbench.editor.enablePreview': true, @@ -682,7 +683,7 @@ suite('ConfigurationChangeEvent', () => { }); test('changeEvent affecting overrides with new configuration', () => { - const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({ 'files.autoSave': 'off', '[markdown]': { @@ -724,7 +725,7 @@ suite('ConfigurationChangeEvent', () => { }); test('changeEvent affecting overrides when configuration changed', () => { - const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); configuration.updateLocalUserConfiguration(toConfigurationModel({ 'workbench.editor.enablePreview': true, '[markdown]': { @@ -791,7 +792,7 @@ suite('ConfigurationChangeEvent', () => { }); test('changeEvent affecting workspace folders', () => { - const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); configuration.updateWorkspaceConfiguration(toConfigurationModel({ 'window.title': 'custom' })); configuration.updateFolderConfiguration(URI.file('folder1'), toConfigurationModel({ 'window.zoomLevel': 2, 'window.restoreFullscreen': true })); configuration.updateFolderConfiguration(URI.file('folder2'), toConfigurationModel({ 'workbench.editor.enablePreview': true, 'window.restoreWindows': true })); @@ -881,7 +882,7 @@ suite('ConfigurationChangeEvent', () => { }); test('changeEvent - all', () => { - const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); configuration.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({ 'window.zoomLevel': 2, 'window.restoreFullscreen': true })); const data = configuration.toData(); const change = mergeChanges( @@ -976,7 +977,7 @@ suite('ConfigurationChangeEvent', () => { }); test('changeEvent affecting tasks and launches', () => { - const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({ 'launch': { 'configuraiton': {} @@ -999,7 +1000,7 @@ suite('ConfigurationChangeEvent', () => { suite('AllKeysConfigurationChangeEvent', () => { test('changeEvent', () => { - const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel()); + const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel()); configuration.updateDefaultConfiguration(toConfigurationModel({ 'editor.lineNumbers': 'off', '[markdown]': { diff --git a/src/vs/platform/configuration/test/common/configurationService.test.ts b/src/vs/platform/configuration/test/common/configurationService.test.ts index 559803b0588c8..04635ed013c16 100644 --- a/src/vs/platform/configuration/test/common/configurationService.test.ts +++ b/src/vs/platform/configuration/test/common/configurationService.test.ts @@ -12,9 +12,11 @@ import { URI } from 'vs/base/common/uri'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { NullLogService } from 'vs/platform/log/common/log'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -22,6 +24,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; suite('ConfigurationService', () => { let fileService: IFileService; + let environmentService: IEnvironmentService; let settingsResource: URI; const disposables: DisposableStore = new DisposableStore(); @@ -30,13 +33,14 @@ suite('ConfigurationService', () => { const diskFileSystemProvider = disposables.add(new InMemoryFileSystemProvider()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); settingsResource = URI.file('settings.json'); + environmentService = new TestInstantiationService().mock(IEnvironmentService) as IEnvironmentService; }); teardown(() => disposables.clear()); test('simple', async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); await testObject.initialize(); const config = testObject.getValue<{ foo: string; @@ -49,7 +53,7 @@ suite('ConfigurationService', () => { test('config gets flattened', async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); await testObject.initialize(); const config = testObject.getValue<{ testworkbench: { @@ -68,7 +72,7 @@ suite('ConfigurationService', () => { test('error case does not explode', async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString(',,,,')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); await testObject.initialize(); const config = testObject.getValue<{ foo: string; @@ -78,7 +82,7 @@ suite('ConfigurationService', () => { }); test('missing file does not explode', async () => { - const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService)); + const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, environmentService, new NullLogService())); await testObject.initialize(); const config = testObject.getValue<{ foo: string }>(); @@ -87,7 +91,7 @@ suite('ConfigurationService', () => { }); test('trigger configuration change event when file does not exist', async () => { - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); await testObject.initialize(); return new Promise((c, e) => { disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(() => { @@ -100,7 +104,7 @@ suite('ConfigurationService', () => { }); test('trigger configuration change event when file exists', async () => { - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); await testObject.initialize(); @@ -116,7 +120,7 @@ suite('ConfigurationService', () => { test('reloadConfiguration', async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); await testObject.initialize(); let config = testObject.getValue<{ foo: string; @@ -155,7 +159,7 @@ suite('ConfigurationService', () => { } }); - let testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService)); + let testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, environmentService, new NullLogService())); await testObject.initialize(); let setting = testObject.getValue(); @@ -163,7 +167,7 @@ suite('ConfigurationService', () => { assert.strictEqual(setting.configuration.service.testSetting, 'isSet'); await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); - testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); setting = testObject.getValue(); @@ -191,7 +195,7 @@ suite('ConfigurationService', () => { } }); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); testObject.initialize(); let res = testObject.inspect('something.missing'); @@ -226,7 +230,7 @@ suite('ConfigurationService', () => { } }); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); testObject.initialize(); let res = testObject.inspect('lookup.service.testNullSetting'); diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index b2b4500a61468..3834247b45b18 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -88,6 +88,7 @@ export interface NativeParsedArgs { 'sync'?: 'on' | 'off'; '__sandbox'?: boolean; 'logsPath'?: string; + 'policy-file'?: string; // chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches 'no-proxy-server'?: boolean; diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 56531a3a7845f..e0feccde448aa 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -87,6 +87,9 @@ export interface IEnvironmentService { telemetryLogResource: URI; serviceMachineIdResource: URI; + // --- Policy + policyFile?: URI; + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // NOTE: KEEP THIS INTERFACE AS SMALL AS POSSIBLE. diff --git a/src/vs/platform/environment/common/environmentService.ts b/src/vs/platform/environment/common/environmentService.ts index fcea3f58023aa..d3214f7036f21 100644 --- a/src/vs/platform/environment/common/environmentService.ts +++ b/src/vs/platform/environment/common/environmentService.ts @@ -237,6 +237,9 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron @memoize get disableWorkspaceTrust(): boolean { return !!this.args['disable-workspace-trust']; } + @memoize + get policyFile(): URI | undefined { return this.args['policy-file'] ? URI.file(this.args['policy-file']) : undefined; } + get args(): NativeParsedArgs { return this._args; } constructor( diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 062ce48a5808c..0a69e9cd2c461 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -73,6 +73,7 @@ export const OPTIONS: OptionDescriptions> = { 'disable-extensions': { type: 'boolean', deprecates: ['disableExtensions'], cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") }, 'disable-extension': { type: 'string[]', cat: 't', args: 'ext-id', description: localize('disableExtension', "Disable an extension.") }, 'sync': { type: 'string', cat: 't', description: localize('turn sync', "Turn sync on or off."), args: ['on | off'] }, + 'policy-file': { type: 'string', cat: 't', description: localize('policyFile', "Path of the file defining policies") }, 'inspect-extensions': { type: 'string', deprecates: ['debugPluginHost'], args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") }, 'inspect-brk-extensions': { type: 'string', deprecates: ['debugBrkPluginHost'], args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") }, diff --git a/src/vs/platform/policy/common/policy.ts b/src/vs/platform/policy/common/policy.ts new file mode 100644 index 0000000000000..5f9b1cfafe994 --- /dev/null +++ b/src/vs/platform/policy/common/policy.ts @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ThrottledDelayer } from 'vs/base/common/async'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; + +export type PolicyValue = string | boolean | number; +export type Policies = IStringDictionary; + +export class PolicyModel extends Disposable { + readonly _serviceBrand: undefined; + + private readonly _onDidChange = new Emitter(); + readonly onDidChange = this._onDidChange.event; + + private policies: Policies = {}; + + constructor( + @IFileService private readonly fileService: IFileService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @ILogService private readonly logService: ILogService, + ) { + super(); + } + + async initialize(): Promise { + const policy = this.environmentService.policyFile ? new FilePolicy(this.environmentService.policyFile, this.fileService, this.logService) : new NullPolicy(); + this.policies = await policy.read(); + this._register(policy.onDidChange(({ changed, policies }) => { + this.policies = policies; + this._onDidChange.fire(changed); + })); + } + + getPolicy(name: string): PolicyValue | undefined { + return this.policies[name]; + } +} + +export interface IPolicy { + readonly onDidChange: Event<{ changed: string[]; policies: Policies }>; + read(): Promise; +} + +class FilePolicy extends Disposable implements IPolicy { + + private policies: Policies = {}; + + private readonly _onDidChange = new Emitter<{ changed: string[]; policies: Policies }>(); + readonly onDidChange = this._onDidChange.event; + + private readonly throttledDelayer = this._register(new ThrottledDelayer(500)); + + constructor( + private readonly file: URI, + @IFileService private readonly fileService: IFileService, + @ILogService private readonly logService: ILogService + ) { + super(); + this._register(fileService.watch(file)); + this._register(Event.filter(fileService.onDidFilesChange, e => e.affects(file))(e => this.throttledDelayer.trigger(() => this.onDidFileChange()))); + } + + async read(): Promise { + try { + const content = await this.fileService.readFile(this.file); + this.policies = JSON.parse(content.value.toString()); + } catch (error) { + this.logService.error(error); + this.policies = {}; + } + return this.policies; + } + + private async onDidFileChange(): Promise { + const old = this.policies; + await this.read(); + const changed = this.compare(old, this.policies); + if (changed.length > 0) { + this._onDidChange.fire({ changed, policies: this.policies }); + } + } + + private compare(old: Policies, current: Policies): string[] { + const changed: string[] = []; + for (const key of Object.keys(old)) { + if (old[key] !== current[key]) { + changed.push(key); + } + } + for (const key of Object.keys(current)) { + if (old[key] === undefined) { + changed.push(key); + } + } + return changed; + } + +} + +class NullPolicy extends Disposable implements IPolicy { + readonly onDidChange = Event.None; + async read(): Promise { return {}; } +} diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 293324b83c576..5d3ba1f21bdcd 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -86,7 +86,7 @@ export class UserDataSyncClient extends Disposable { this.instantiationService.stub(IStorageService, this._register(new InMemoryStorageService())); - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, environmentService, logService)); await configurationService.initialize(); this.instantiationService.stub(IConfigurationService, configurationService); this.instantiationService.stub(IUriIdentityService, this.instantiationService.createInstance(UriIdentityService)); diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index f1e8986f5c508..170fbafa06a45 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -91,7 +91,7 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, this._register(new DiskFileSystemProvider(logService))); // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, environmentService, logService)); await configurationService.initialize(); services.set(IConfigurationService, configurationService); diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index a072fa8bb0e11..a443d3bc84c31 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -107,7 +107,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken services.set(IFileService, fileService); fileService.registerProvider(Schemas.file, disposables.add(new DiskFileSystemProvider(logService))); - const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService); + const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService, environmentService, logService); services.set(IConfigurationService, configurationService); const extensionHostStatusService = new ExtensionHostStatusService(); diff --git a/src/vs/workbench/api/common/extHostConfiguration.ts b/src/vs/workbench/api/common/extHostConfiguration.ts index 3a45d62f28ff9..0bd5f8e9752f0 100644 --- a/src/vs/workbench/api/common/extHostConfiguration.ts +++ b/src/vs/workbench/api/common/extHostConfiguration.ts @@ -256,7 +256,7 @@ export class ExtHostConfigProvider { return { key, - defaultValue: config.default?.value, + defaultValue: config.policy?.value ?? config.default?.value, globalValue: config.user?.value, workspaceValue: config.workspace?.value, workspaceFolderValue: config.workspaceFolder?.value, diff --git a/src/vs/workbench/api/test/browser/extHostConfiguration.test.ts b/src/vs/workbench/api/test/browser/extHostConfiguration.test.ts index 764c1c294b1a9..950c278a1ff2a 100644 --- a/src/vs/workbench/api/test/browser/extHostConfiguration.test.ts +++ b/src/vs/workbench/api/test/browser/extHostConfiguration.test.ts @@ -43,6 +43,7 @@ suite('ExtHostConfiguration', function () { function createConfigurationData(contents: any): IConfigurationInitData { return { defaults: new ConfigurationModel(contents), + policy: new ConfigurationModel(), user: new ConfigurationModel(contents), workspace: new ConfigurationModel(), folders: [], @@ -279,6 +280,7 @@ suite('ExtHostConfiguration', function () { 'wordWrap': 'off' } }, ['editor.wordWrap']), + policy: new ConfigurationModel(), user: new ConfigurationModel({ 'editor': { 'wordWrap': 'on' @@ -328,6 +330,7 @@ suite('ExtHostConfiguration', function () { 'wordWrap': 'off' } }, ['editor.wordWrap']), + policy: new ConfigurationModel(), user: new ConfigurationModel({ 'editor': { 'wordWrap': 'on' @@ -405,6 +408,7 @@ suite('ExtHostConfiguration', function () { 'lineNumbers': 'on' } }, ['editor.wordWrap']), + policy: new ConfigurationModel(), user: new ConfigurationModel({ 'editor': { 'wordWrap': 'on' @@ -508,6 +512,7 @@ suite('ExtHostConfiguration', function () { 'editor.wordWrap': 'bounded', } }), + policy: new ConfigurationModel(), user: toConfigurationModel({ 'editor.wordWrap': 'bounded', '[typescript]': { diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index 606a925588443..4d2b985ec7426 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -175,6 +175,7 @@ class EditSettingRenderer extends Disposable { constructor(private editor: ICodeEditor, private primarySettingsModel: ISettingsEditorModel, private settingHighlighter: SettingHighlighter, + @IConfigurationService private readonly configurationService: IConfigurationService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextMenuService private readonly contextMenuService: IContextMenuService ) { @@ -283,6 +284,9 @@ class EditSettingRenderer extends Disposable { return this.getSettingsAtLineNumber(lineNumber).filter(setting => { const configurationNode = configurationMap[setting.key]; if (configurationNode) { + if (configurationNode.policy && this.configurationService.inspect(setting.key).policyValue !== undefined) { + return false; + } if (this.isDefaultSettings()) { if (setting.key === 'launch') { // Do not show because of https://github.com/microsoft/vscode/issues/32593 @@ -522,6 +526,9 @@ class UnsupportedSettingsRenderer extends Disposable implements languages.CodeAc for (const setting of section.settings) { const configuration = configurationRegistry[setting.key]; if (configuration) { + if (this.handlePolicyConfiguration(setting, configuration, markerData)) { + continue; + } switch (this.settingsEditorModel.configurationTarget) { case ConfigurationTarget.USER_LOCAL: this.handleLocalUserConfiguration(setting, configuration, markerData); @@ -550,6 +557,25 @@ class UnsupportedSettingsRenderer extends Disposable implements languages.CodeAc return markerData; } + private handlePolicyConfiguration(setting: ISetting, configuration: IConfigurationPropertySchema, markerData: IMarkerData[]): boolean { + if (!configuration.policy) { + return false; + } + if (this.configurationService.inspect(setting.key).policyValue === undefined) { + return false; + } + if (this.settingsEditorModel.configurationTarget === ConfigurationTarget.DEFAULT) { + return false; + } + markerData.push({ + severity: MarkerSeverity.Hint, + tags: [MarkerTag.Unnecessary], + ...setting.range, + message: nls.localize('unsupportedPolicySetting', "This setting cannot be applied because it is configured in the system policy.") + }); + return true; + } + private handleLocalUserConfiguration(setting: ISetting, configuration: IConfigurationPropertySchema, markerData: IMarkerData[]): void { if (this.environmentService.remoteAuthority && (configuration.scope === ConfigurationScope.MACHINE || configuration.scope === ConfigurationScope.MACHINE_OVERRIDABLE)) { markerData.push({ diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 2bd9b092c9a9b..39ed9b5e64cda 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -9,7 +9,7 @@ import * as errors from 'vs/base/common/errors'; import { Disposable, IDisposable, dispose, toDisposable, MutableDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; import { FileChangeType, FileChangesEvent, IFileService, whenProviderRegistered, FileOperationError, FileOperationResult, FileOperation, FileOperationEvent } from 'vs/platform/files/common/files'; -import { ConfigurationModel, ConfigurationModelParser, ConfigurationParseOptions, DefaultConfigurationModel, UserSettings } from 'vs/platform/configuration/common/configurationModels'; +import { ConfigurationModel, ConfigurationModelParser, ConfigurationParseOptions, UserSettings } from 'vs/platform/configuration/common/configurationModels'; import { WorkspaceConfigurationModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels'; import { TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, REMOTE_MACHINE_SCOPES, FOLDER_SCOPES, WORKSPACE_SCOPES } from 'vs/workbench/services/configuration/common/configuration'; import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; @@ -26,8 +26,9 @@ import { joinPath } from 'vs/base/common/resources'; import { Registry } from 'vs/platform/registry/common/platform'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { isObject } from 'vs/base/common/types'; +import { DefaultConfiguration as BaseDefaultConfiguration } from 'vs/platform/configuration/common/configurations'; -export class DefaultConfiguration extends Disposable { +export class DefaultConfiguration extends BaseDefaultConfiguration { static readonly DEFAULT_OVERRIDES_CACHE_EXISTS_KEY = 'DefaultOverridesCacheExists'; @@ -35,9 +36,6 @@ export class DefaultConfiguration extends Disposable { private cachedConfigurationDefaultsOverrides: IStringDictionary = {}; private readonly cacheKey: ConfigurationKey = { type: 'defaults', key: 'configurationDefaultsOverrides' }; - private readonly _onDidChangeConfiguration = this._register(new Emitter<{ defaults: ConfigurationModel; properties: string[] }>()); - readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event; - private updateCache: boolean = false; constructor( @@ -50,27 +48,20 @@ export class DefaultConfiguration extends Disposable { } } - private _configurationModel: ConfigurationModel | undefined; - get configurationModel(): ConfigurationModel { - if (!this._configurationModel) { - this._configurationModel = new DefaultConfigurationModel(this.cachedConfigurationDefaultsOverrides); - } - return this._configurationModel; + protected override getConfigurationDefaultOverrides(): IStringDictionary { + return this.cachedConfigurationDefaultsOverrides; } - async initialize(): Promise { + override async initialize(): Promise { await this.initializeCachedConfigurationDefaultsOverrides(); - this._configurationModel = undefined; - this._register(this.configurationRegistry.onDidUpdateConfiguration(({ properties, defaultsOverrides }) => this.onDidUpdateConfiguration(properties, defaultsOverrides))); - return this.configurationModel; + return super.initialize(); } - reload(): ConfigurationModel { + override reload(): ConfigurationModel { this.updateCache = true; this.cachedConfigurationDefaultsOverrides = {}; - this._configurationModel = undefined; this.updateCachedConfigurationDefaultsOverrides(); - return this.configurationModel; + return super.reload(); } private initiaizeCachedConfigurationDefaultsOverridesPromise: Promise | undefined; @@ -92,9 +83,8 @@ export class DefaultConfiguration extends Disposable { return this.initiaizeCachedConfigurationDefaultsOverridesPromise; } - private onDidUpdateConfiguration(properties: string[], defaultsOverrides?: boolean): void { - this._configurationModel = undefined; - this._onDidChangeConfiguration.fire({ defaults: this.configurationModel, properties }); + protected override onDidUpdateConfiguration(properties: string[], defaultsOverrides?: boolean): void { + super.onDidUpdateConfiguration(properties, defaultsOverrides); if (defaultsOverrides) { this.updateCachedConfigurationDefaultsOverrides(); } diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 45af17cc1752e..a4c1112129062 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -13,6 +13,7 @@ import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/plat import { IWorkspaceContextService, Workspace as BaseWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkspaceFolder, toWorkspaceFolder, isWorkspaceFolder, IWorkspaceFoldersWillChangeEvent, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier, IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { ConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent, mergeChanges } from 'vs/platform/configuration/common/configurationModels'; import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange, ConfigurationTargetToString, IConfigurationUpdateOverrides, isConfigurationUpdateOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { PolicyConfiguration } from 'vs/platform/configuration/common/configurations'; import { Configuration } from 'vs/workbench/services/configuration/common/configurationModels'; import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService, RestrictedSettings } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -54,7 +55,8 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat private readonly configurationCache: IConfigurationCache; private _configuration: Configuration; private initialized: boolean = false; - private defaultConfiguration: DefaultConfiguration; + private readonly defaultConfiguration: DefaultConfiguration; + private readonly policyConfiguration: PolicyConfiguration; private localUserConfiguration: UserConfiguration; private remoteUserConfiguration: RemoteUserConfiguration | null = null; private workspaceConfiguration: WorkspaceConfiguration; @@ -109,12 +111,13 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat this.initRemoteUserConfigurationBarrier = new Barrier(); this.completeWorkspaceBarrier = new Barrier(); - this.defaultConfiguration = new DefaultConfiguration(configurationCache, environmentService); + this.defaultConfiguration = this._register(new DefaultConfiguration(configurationCache, environmentService)); + this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, fileService, environmentService, logService)); this.configurationCache = configurationCache; this.fileService = fileService; this.uriIdentityService = uriIdentityService; this.logService = logService; - this._configuration = new Configuration(this.defaultConfiguration.configurationModel, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); + this._configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); this.cachedFolderConfigs = new ResourceMap(); this.localUserConfiguration = this._register(new UserConfiguration(environmentService.settingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService)); this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); @@ -138,6 +141,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat })); this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties, defaults }) => this.onDefaultConfigurationChanged(defaults, properties))); + this._register(this.policyConfiguration.onDidChangeConfiguration(configurationModel => this.onPolicyConfigurationChanged(configurationModel))); this.workspaceEditingQueue = new Queue(); } @@ -572,12 +576,18 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat private async initializeConfiguration(): Promise { await this.defaultConfiguration.initialize(); - mark('code/willInitUserConfiguration'); - const { local, remote } = await this.initializeUserConfiguration(); - mark('code/didInitUserConfiguration'); + const [, user] = await Promise.all([ + this.policyConfiguration.initialize(), + (async () => { + mark('code/willInitUserConfiguration'); + const result = await this.initializeUserConfiguration(); + mark('code/didInitUserConfiguration'); + return result; + })() + ]); mark('code/willInitWorkspaceConfiguration'); - await this.loadConfiguration(local, remote); + await this.loadConfiguration(user.local, user.remote); mark('code/didInitWorkspaceConfiguration'); } @@ -640,7 +650,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration)); const currentConfiguration = this._configuration; - this._configuration = new Configuration(this.defaultConfiguration.configurationModel, userConfigurationModel, remoteUserConfigurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap(), this.workspace); + this._configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, userConfigurationModel, remoteUserConfigurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap(), this.workspace); if (this.initialized) { const change = this._configuration.compare(currentConfiguration); @@ -692,6 +702,12 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat } } + private onPolicyConfigurationChanged(policyConfiguration: ConfigurationModel): void { + const previous = { data: this._configuration.toData(), workspace: this.workspace }; + const change = this._configuration.compareAndUpdatePolicyConfiguration(policyConfiguration); + this.triggerConfigurationChange(change, previous, ConfigurationTarget.DEFAULT); + } + private onLocalUserConfigurationChanged(userConfiguration: ConfigurationModel): void { const previous = { data: this._configuration.toData(), workspace: this.workspace }; const change = this._configuration.compareAndUpdateLocalUserConfiguration(userConfiguration); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index 3e4e805c23132..bf0d5f3adde47 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -92,6 +92,11 @@ export const enum ConfigurationEditingErrorCode { */ ERROR_INVALID_CONFIGURATION, + /** + * Error when trying to write a policy configuration + */ + ERROR_POLICY_CONFIGURATION, + /** * Internal Error. */ @@ -359,6 +364,7 @@ export class ConfigurationEditingService { switch (error) { // API constraints + case ConfigurationEditingErrorCode.ERROR_POLICY_CONFIGURATION: return nls.localize('errorPolicyConfiguration', "Unable to write {0} because it is configured in system policy.", operation.key); case ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY: return nls.localize('errorUnknownKey', "Unable to write to {0} because {1} is not a registered configuration.", this.stringifyTarget(target), operation.key); case ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_CONFIGURATION_APPLICATION: return nls.localize('errorInvalidWorkspaceConfigurationApplication', "Unable to write {0} to Workspace Settings. This setting can be written only into User settings.", operation.key); case ConfigurationEditingErrorCode.ERROR_INVALID_WORKSPACE_CONFIGURATION_MACHINE: return nls.localize('errorInvalidWorkspaceConfigurationMachine', "Unable to write {0} to Workspace Settings. This setting can be written only into User settings.", operation.key); @@ -492,6 +498,10 @@ export class ConfigurationEditingService { private async validate(target: EditableConfigurationTarget, operation: IConfigurationEditOperation, checkDirty: boolean, overrides: IConfigurationUpdateOverrides): Promise { + if (this.configurationService.inspect(operation.key).policyValue !== undefined) { + throw this.toConfigurationEditingError(ConfigurationEditingErrorCode.ERROR_POLICY_CONFIGURATION, target, operation); + } + const configurationProperties = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); const configurationScope = configurationProperties[operation.key]?.scope; diff --git a/src/vs/workbench/services/configuration/common/configurationModels.ts b/src/vs/workbench/services/configuration/common/configurationModels.ts index 95a1d40ad7704..807e96399dfab 100644 --- a/src/vs/workbench/services/configuration/common/configurationModels.ts +++ b/src/vs/workbench/services/configuration/common/configurationModels.ts @@ -98,6 +98,7 @@ export class Configuration extends BaseConfiguration { constructor( defaults: ConfigurationModel, + policy: ConfigurationModel, localUser: ConfigurationModel, remoteUser: ConfigurationModel, workspaceConfiguration: ConfigurationModel, @@ -105,7 +106,7 @@ export class Configuration extends BaseConfiguration { memoryConfiguration: ConfigurationModel, memoryConfigurationByResource: ResourceMap, private readonly _workspace?: Workspace) { - super(defaults, localUser, remoteUser, workspaceConfiguration, folders, memoryConfiguration, memoryConfigurationByResource); + super(defaults, policy, localUser, remoteUser, workspaceConfiguration, folders, memoryConfiguration, memoryConfigurationByResource); } override getValue(key: string | undefined, overrides: IConfigurationOverrides = {}): any { diff --git a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts index 228cc13113a73..7f391a11ad36e 100644 --- a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts +++ b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts @@ -155,14 +155,14 @@ suite('Workspace Configuration', () => { test('Test compare same configurations', () => { const workspace = new Workspace('a', [new WorkspaceFolder({ index: 0, name: 'a', uri: URI.file('folder1') }), new WorkspaceFolder({ index: 1, name: 'b', uri: URI.file('folder2') }), new WorkspaceFolder({ index: 2, name: 'c', uri: URI.file('folder3') })]); - const configuration1 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), workspace); + const configuration1 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), workspace); configuration1.updateDefaultConfiguration(defaultConfigurationModel); configuration1.updateLocalUserConfiguration(toConfigurationModel({ 'window.title': 'native', '[typescript]': { 'editor.insertSpaces': false } })); configuration1.updateWorkspaceConfiguration(toConfigurationModel({ 'editor.lineNumbers': 'on' })); configuration1.updateFolderConfiguration(URI.file('folder1'), toConfigurationModel({ 'editor.fontSize': 14 })); configuration1.updateFolderConfiguration(URI.file('folder2'), toConfigurationModel({ 'editor.wordWrap': 'on' })); - const configuration2 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), workspace); + const configuration2 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), workspace); configuration2.updateDefaultConfiguration(defaultConfigurationModel); configuration2.updateLocalUserConfiguration(toConfigurationModel({ 'window.title': 'native', '[typescript]': { 'editor.insertSpaces': false } })); configuration2.updateWorkspaceConfiguration(toConfigurationModel({ 'editor.lineNumbers': 'on' })); @@ -176,14 +176,14 @@ suite('Workspace Configuration', () => { test('Test compare different configurations', () => { const workspace = new Workspace('a', [new WorkspaceFolder({ index: 0, name: 'a', uri: URI.file('folder1') }), new WorkspaceFolder({ index: 1, name: 'b', uri: URI.file('folder2') }), new WorkspaceFolder({ index: 2, name: 'c', uri: URI.file('folder3') })]); - const configuration1 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), workspace); + const configuration1 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), workspace); configuration1.updateDefaultConfiguration(defaultConfigurationModel); configuration1.updateLocalUserConfiguration(toConfigurationModel({ 'window.title': 'native', '[typescript]': { 'editor.insertSpaces': false } })); configuration1.updateWorkspaceConfiguration(toConfigurationModel({ 'editor.lineNumbers': 'on' })); configuration1.updateFolderConfiguration(URI.file('folder1'), toConfigurationModel({ 'editor.fontSize': 14 })); configuration1.updateFolderConfiguration(URI.file('folder2'), toConfigurationModel({ 'editor.wordWrap': 'on' })); - const configuration2 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), workspace); + const configuration2 = new Configuration(new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), workspace); configuration2.updateDefaultConfiguration(defaultConfigurationModel); configuration2.updateLocalUserConfiguration(toConfigurationModel({ 'workbench.enableTabs': true, '[typescript]': { 'editor.insertSpaces': true } })); configuration2.updateWorkspaceConfiguration(toConfigurationModel({ 'editor.fontSize': 11 })); From 94da754e273c33fdaa93f84c28240200cd1bc5d0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 10 May 2022 14:04:00 +0200 Subject: [PATCH 025/942] make sure policy value is undefined if empty --- src/vs/platform/configuration/common/configurationModels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 03135dbdbb4bc..de218cadea1d2 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -490,7 +490,7 @@ export class Configuration { const memoryConfigurationModel = overrides.resource ? this._memoryConfigurationByResource.get(overrides.resource) || this._memoryConfiguration : this._memoryConfiguration; const defaultValue = overrides.overrideIdentifier ? this._defaultConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this._defaultConfiguration.freeze().getValue(key); - const policyValue = this._policyConfiguration.freeze().getValue(key); + const policyValue = this._policyConfiguration.isEmpty() ? undefined : this._policyConfiguration.freeze().getValue(key); const userValue = overrides.overrideIdentifier ? this.userConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this.userConfiguration.freeze().getValue(key); const userLocalValue = overrides.overrideIdentifier ? this.localUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this.localUserConfiguration.freeze().getValue(key); const userRemoteValue = overrides.overrideIdentifier ? this.remoteUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this.remoteUserConfiguration.freeze().getValue(key); From 6086f78bfdb1a243d290137db5e731429c28dcd5 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 10 May 2022 14:04:15 +0200 Subject: [PATCH 026/942] fix tests --- .../configuration/test/common/configurationModels.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/configuration/test/common/configurationModels.test.ts b/src/vs/platform/configuration/test/common/configurationModels.test.ts index 9887f80e18431..859cd0c28a14b 100644 --- a/src/vs/platform/configuration/test/common/configurationModels.test.ts +++ b/src/vs/platform/configuration/test/common/configurationModels.test.ts @@ -487,7 +487,7 @@ suite('Configuration', () => { const defaultConfigurationModel = parseConfigurationModel({ '[l1]': { 'a': 1 }, '[l2]': { 'b': 1 } }); const userConfigurationModel = parseConfigurationModel({ '[l3]': { 'a': 2 } }); const workspaceConfigurationModel = parseConfigurationModel({ '[l1]': { 'a': 3 }, '[l4]': { 'a': 3 } }); - const testObject: Configuration = new Configuration(defaultConfigurationModel, userConfigurationModel, new ConfigurationModel(), workspaceConfigurationModel); + const testObject: Configuration = new Configuration(defaultConfigurationModel, new ConfigurationModel(), userConfigurationModel, workspaceConfigurationModel); const { overrideIdentifiers } = testObject.inspect('a', {}, undefined); From 364240765b6cc5265c6341b87818a48c62ffa325 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 10 May 2022 14:48:02 +0200 Subject: [PATCH 027/942] refactor policy service --- .../configuration/common/configurations.ts | 13 +- .../policy/common/filePolicyService.ts | 91 ++++++++++++++ src/vs/platform/policy/common/policy.ts | 113 ++++-------------- 3 files changed, 122 insertions(+), 95 deletions(-) create mode 100644 src/vs/platform/policy/common/filePolicyService.ts diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index a86a1f50e495f..ed855abf2ab9c 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -12,7 +12,8 @@ import { Extensions, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRID import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; -import { PolicyModel } from 'vs/platform/policy/common/policy'; +import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; +import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; import { Registry } from 'vs/platform/registry/common/platform'; export class DefaultConfiguration extends Disposable { @@ -82,7 +83,7 @@ export class PolicyConfiguration extends Disposable { private readonly _onDidChangeConfiguration = this._register(new Emitter()); readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event; - private readonly policies: PolicyModel; + private readonly policyService: IPolicyService; constructor( private readonly defaultConfiguration: DefaultConfiguration, @@ -91,7 +92,7 @@ export class PolicyConfiguration extends Disposable { logService: ILogService ) { super(); - this.policies = new PolicyModel(fileService, environmentService, logService); + this.policyService = environmentService.policyFile ? new FilePolicyService(environmentService.policyFile, fileService, logService) : new NullPolicyService(); } private _configurationModel: ConfigurationModel | undefined; @@ -105,7 +106,7 @@ export class PolicyConfiguration extends Disposable { if (!policyName) { continue; } - const value = this.policies.getPolicy(policyName); + const value = this.policyService.getPolicyValue(policyName); if (value === undefined) { continue; } @@ -118,8 +119,8 @@ export class PolicyConfiguration extends Disposable { } async initialize(): Promise { - await this.policies.initialize(); - this._register(this.policies.onDidChange(e => this.onDidChange())); + await this.policyService.initialize(); + this._register(this.policyService.onDidChange(e => this.onDidChange())); this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties }) => this.onDidDefaultConfigurationChange(properties))); return this.reload(); } diff --git a/src/vs/platform/policy/common/filePolicyService.ts b/src/vs/platform/policy/common/filePolicyService.ts new file mode 100644 index 0000000000000..0cfa9e1588c17 --- /dev/null +++ b/src/vs/platform/policy/common/filePolicyService.ts @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ThrottledDelayer } from 'vs/base/common/async'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Iterable } from 'vs/base/common/iterator'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { isObject } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IPolicyService, Policies, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; + +function keysDiff(a: Map, b: Map): string[] { + const result: string[] = []; + + for (const key of Iterable.concat(a.keys(), b.keys())) { + if (a.get(key) !== b.get(key)) { + result.push(key); + } + } + + return result; +} + +export class FilePolicyService extends Disposable implements IPolicyService { + + private policies: Policies = new Map(); + + private readonly _onDidChange = new Emitter(); + readonly onDidChange = this._onDidChange.event; + + private readonly throttledDelayer = this._register(new ThrottledDelayer(500)); + + constructor( + private readonly file: URI, + @IFileService private readonly fileService: IFileService, + @ILogService private readonly logService: ILogService + ) { + super(); + + const onDidChangePolicyFile = Event.filter(fileService.onDidFilesChange, e => e.affects(file)); + this._register(fileService.watch(file)); + this._register(onDidChangePolicyFile(this.refresh, this)); + } + + async initialize(): Promise { + await this.doRefresh(); + } + + private refresh(): void { + this.throttledDelayer.trigger(() => this.doRefresh()); + } + + private async read(): Promise { + const policies: Policies = new Map(); + + try { + const content = await this.fileService.readFile(this.file); + const raw = JSON.parse(content.value.toString()); + + if (!isObject(raw)) { + throw new Error('Policy file isn\'t a JSON object'); + } + + for (const key of Object.keys(raw)) { + policies.set(key, raw[key]); + } + } catch (error) { + this.logService.error(`[FilePolicyService] Failed to read policies`, error); + } + + return policies; + } + + private async doRefresh(): Promise { + const policies = await this.read(); + const diff = keysDiff(this.policies, policies); + this.policies = policies; + + if (diff.length > 0) { + this._onDidChange.fire(diff); + } + } + + getPolicyValue(name: PolicyName): PolicyValue | undefined { + return this.policies.get(name); + } +} diff --git a/src/vs/platform/policy/common/policy.ts b/src/vs/platform/policy/common/policy.ts index 5f9b1cfafe994..4febe66904fe3 100644 --- a/src/vs/platform/policy/common/policy.ts +++ b/src/vs/platform/policy/common/policy.ts @@ -3,110 +3,45 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ThrottledDelayer } from 'vs/base/common/async'; -import { IStringDictionary } from 'vs/base/common/collections'; -import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IFileService } from 'vs/platform/files/common/files'; -import { ILogService } from 'vs/platform/log/common/log'; +import { Event } from 'vs/base/common/event'; +export type PolicyName = string; export type PolicyValue = string | boolean | number; -export type Policies = IStringDictionary; +export type Policies = Map; -export class PolicyModel extends Disposable { - readonly _serviceBrand: undefined; - - private readonly _onDidChange = new Emitter(); - readonly onDidChange = this._onDidChange.event; - - private policies: Policies = {}; - - constructor( - @IFileService private readonly fileService: IFileService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ILogService private readonly logService: ILogService, - ) { - super(); - } - - async initialize(): Promise { - const policy = this.environmentService.policyFile ? new FilePolicy(this.environmentService.policyFile, this.fileService, this.logService) : new NullPolicy(); - this.policies = await policy.read(); - this._register(policy.onDidChange(({ changed, policies }) => { - this.policies = policies; - this._onDidChange.fire(changed); - })); - } - - getPolicy(name: string): PolicyValue | undefined { - return this.policies[name]; - } +export interface IPolicyService { + readonly onDidChange: Event; + initialize(): Promise; + getPolicyValue(name: PolicyName): PolicyValue | undefined; } -export interface IPolicy { - readonly onDidChange: Event<{ changed: string[]; policies: Policies }>; - read(): Promise; +export class NullPolicyService implements IPolicyService { + readonly onDidChange = Event.None; + async initialize() { } + getPolicyValue() { return undefined; } } -class FilePolicy extends Disposable implements IPolicy { - - private policies: Policies = {}; - - private readonly _onDidChange = new Emitter<{ changed: string[]; policies: Policies }>(); - readonly onDidChange = this._onDidChange.event; +export class MultiPolicyService implements IPolicyService { - private readonly throttledDelayer = this._register(new ThrottledDelayer(500)); + readonly onDidChange: Event; - constructor( - private readonly file: URI, - @IFileService private readonly fileService: IFileService, - @ILogService private readonly logService: ILogService - ) { - super(); - this._register(fileService.watch(file)); - this._register(Event.filter(fileService.onDidFilesChange, e => e.affects(file))(e => this.throttledDelayer.trigger(() => this.onDidFileChange()))); + constructor(private policyServices: readonly IPolicyService[]) { + this.onDidChange = Event.any(...policyServices.map(p => p.onDidChange)); } - async read(): Promise { - try { - const content = await this.fileService.readFile(this.file); - this.policies = JSON.parse(content.value.toString()); - } catch (error) { - this.logService.error(error); - this.policies = {}; - } - return this.policies; + async initialize() { + await Promise.all(this.policyServices.map(p => p.initialize())); } - private async onDidFileChange(): Promise { - const old = this.policies; - await this.read(); - const changed = this.compare(old, this.policies); - if (changed.length > 0) { - this._onDidChange.fire({ changed, policies: this.policies }); - } - } + getPolicyValue(name: PolicyName) { + for (const policyService of this.policyServices) { + const result = policyService.getPolicyValue(name); - private compare(old: Policies, current: Policies): string[] { - const changed: string[] = []; - for (const key of Object.keys(old)) { - if (old[key] !== current[key]) { - changed.push(key); - } - } - for (const key of Object.keys(current)) { - if (old[key] === undefined) { - changed.push(key); + if (typeof result !== 'undefined') { + return result; } } - return changed; - } - -} -class NullPolicy extends Disposable implements IPolicy { - readonly onDidChange = Event.None; - async read(): Promise { return {}; } + return undefined; + } } From 638ba65eccc030511fab770d731a348cc77360a4 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 10 May 2022 15:04:22 +0200 Subject: [PATCH 028/942] rename `testMergeEditor` to `_open.mergeEditor` --- .../mergeEditor/browser/mergeEditor.contribution.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts index c1fc3b0fbf27e..a9446f8a1833b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -40,19 +40,18 @@ registerAction2(class Foo extends Action2 { constructor() { super({ - id: 'testMergeEditor', - title: '3wm', - f1: true + id: '_open.mergeEditor', + title: localize('title', "Open Merge Editor"), }); } run(accessor: ServicesAccessor, ...args: any[]): void { const validatedArgs = ITestMergeEditorArgs.validate(args[0]); - function normalize(uri: URI | string): URI { + function normalize(uri: URI | UriComponents | string): URI { if (typeof uri === 'string') { return URI.parse(uri); } else { - return uri; + return URI.revive(uri); } } From 5971d3188148969dfd101dfccad134b313919368 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 10 May 2022 15:09:35 +0200 Subject: [PATCH 029/942] (prototype) use `_open.mergeEditor` from git extension for files under conflict --- extensions/git/package.json | 3 +- extensions/git/src/main.ts | 4 ++- extensions/git/src/mergeInfoProvider.ts | 47 +++++++++++++++++++++++++ extensions/git/src/repository.ts | 15 +++++++- 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 extensions/git/src/mergeInfoProvider.ts diff --git a/extensions/git/package.json b/extensions/git/package.json index 3517a2803d91f..2d3da8735b382 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -22,7 +22,8 @@ ], "activationEvents": [ "*", - "onFileSystem:git" + "onFileSystem:git", + "onFileSystem:git-show" ], "extensionDependencies": [ "vscode.git-base" diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 24768c1dba417..4977cd4058cbc 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -25,6 +25,7 @@ import { GitTimelineProvider } from './timelineProvider'; import { registerAPICommands } from './api/api1'; import { TerminalEnvironmentManager } from './terminal'; import { OutputChannelLogger } from './log'; +import { GitMergeShowContentProvider } from './mergeInfoProvider'; const deactivateTasks: { (): Promise }[] = []; @@ -102,7 +103,8 @@ async function createModel(context: ExtensionContext, outputChannelLogger: Outpu new GitFileSystemProvider(model), new GitDecorations(model), new GitProtocolHandler(), - new GitTimelineProvider(model, cc) + new GitTimelineProvider(model, cc), + new GitMergeShowContentProvider(model) ); checkGitVersion(info); diff --git a/extensions/git/src/mergeInfoProvider.ts b/extensions/git/src/mergeInfoProvider.ts new file mode 100644 index 0000000000000..83853a18f47e1 --- /dev/null +++ b/extensions/git/src/mergeInfoProvider.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TextDocumentContentProvider, Uri, workspace } from 'vscode'; +import { Model } from './model'; + +export class GitMergeShowContentProvider implements TextDocumentContentProvider { + + static readonly Scheme = 'git-show'; + + readonly dispose: () => void; + + constructor(private model: Model) { + const reg = workspace.registerTextDocumentContentProvider(GitMergeShowContentProvider.Scheme, this); + this.dispose = reg.dispose.bind(reg); + } + + async provideTextDocumentContent(uri: Uri): Promise { + await this.model.isInitialized; + + const repository = this.model.getRepository(uri); + if (!repository) { + return undefined; + } + + if (!/^:[123]$/.test(uri.query)) { + return undefined; + } + + try { + return await repository.show(uri.query, uri.fsPath); + } catch (error) { + console.error(error); + return undefined; + } + } +} + +export function toMergeUris(uri: Uri): { base: Uri; ours: Uri; theirs: Uri } { + return { + base: uri.with({ scheme: GitMergeShowContentProvider.Scheme, query: ':1' }), + ours: uri.with({ scheme: GitMergeShowContentProvider.Scheme, query: ':2' }), + theirs: uri.with({ scheme: GitMergeShowContentProvider.Scheme, query: ':3' }), + }; +} diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index c7962fb1a62ff..6d603936b80f2 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -21,6 +21,7 @@ import { IPushErrorHandlerRegistry } from './pushError'; import { ApiRepository } from './api/api1'; import { IRemoteSourcePublisherRegistry } from './remotePublisher'; import { ActionButtonCommand } from './actionButton'; +import { toMergeUris } from './mergeInfoProvider'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -602,7 +603,19 @@ class ResourceCommandResolver { resolveChangeCommand(resource: Resource): Command { const title = this.getTitle(resource); - if (!resource.leftUri) { + if (!resource.leftUri && resource.rightUri && resource.type === Status.BOTH_MODIFIED) { + const mergeUris = toMergeUris(resource.rightUri); + return { + command: '_open.mergeEditor', + title: localize('open.merge', "Open Merge"), + arguments: [{ + ancestor: mergeUris.base, + input1: mergeUris.ours, + input2: mergeUris.theirs, + output: resource.rightUri + }] + }; + } else if (!resource.leftUri) { return { command: 'vscode.open', title: localize('open', "Open"), From 983fc7a6b6c9da3272219ab00a59ce5146a8afbf Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 10 May 2022 16:14:28 +0200 Subject: [PATCH 030/942] connect floating button to a menu and contribute to that from git --- extensions/git/package.json | 12 +++++++ extensions/git/package.nls.json | 1 + extensions/git/src/commands.ts | 13 +++++++ src/vs/platform/actions/common/actions.ts | 1 + .../browser/mergeEditor.contribution.ts | 3 -- .../mergeEditor/browser/mergeEditor.ts | 35 +++++++++++++++---- .../actions/common/menusExtensionPoint.ts | 6 ++++ .../common/extensionsApiProposals.ts | 1 + ...de.proposed.contribMergeEditorToolbar.d.ts | 6 ++++ 9 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.contribMergeEditorToolbar.d.ts diff --git a/extensions/git/package.json b/extensions/git/package.json index 2d3da8735b382..ced39d466d184 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -11,6 +11,7 @@ "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "enabledApiProposals": [ "diffCommand", + "contribMergeEditorToolbar", "contribViewsWelcome", "scmActionButton", "scmSelectedProvider", @@ -550,6 +551,11 @@ "command": "git.api.getRemoteSources", "title": "%command.api.getRemoteSources%", "category": "Git API" + }, + { + "command": "git.acceptMerge", + "title": "%command.git.acceptMerge%", + "category": "Git" } ], "keybindings": [ @@ -1419,6 +1425,12 @@ "when": "isInDiffRightEditor && !isInEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" } ], + "merge/toolbar": [ + { + "command": "git.acceptMerge", + "when": "isMergeEditor" + } + ], "scm/change/title": [ { "command": "git.stageChange", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 523b994592866..2a1ec856252cd 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -97,6 +97,7 @@ "command.api.getRepositories": "Get Repositories", "command.api.getRepositoryState": "Get Repository State", "command.api.getRemoteSources": "Get Remote Sources", + "command.git.acceptMerge": "Accept Merge", "config.enabled": "Whether git is enabled.", "config.path": "Path and filename of the git executable, e.g. `C:\\Program Files\\Git\\bin\\git.exe` (Windows). This can also be an array of string values containing multiple paths to look up.", "config.autoRepositoryDetection": "Configures when repositories should be automatically detected.", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index a3479a0b3b5e1..93cf4df895a1e 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1033,6 +1033,19 @@ export class CommandCenter { await this._stageChanges(textEditor, selectedChanges); } + @command('git.acceptMerge') + async acceptMerge(uri: Uri | unknown): Promise { + // TODO@jrieken make this proper, needs help from SCM folks + if (!(uri instanceof Uri)) { + return; + } + const repository = this.model.getRepository(uri); + if (!repository) { + return; + } + await repository.add([uri]); + } + private async _stageChanges(textEditor: TextEditor, changes: LineChange[]): Promise { const modifiedDocument = textEditor.document; const modifiedUri = modifiedDocument.uri; diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index e97c241b95c38..aef37651a7d5c 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -158,6 +158,7 @@ export class MenuId { static readonly WebviewContext = new MenuId('WebviewContext'); static readonly InlineCompletionsActions = new MenuId('InlineCompletionsActions'); static readonly NewFile = new MenuId('NewFile'); + static readonly MergeToolbar = new MenuId('MergeToolbar'); readonly id: number; readonly _debugName: string; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts index a9446f8a1833b..88fc90c9d2a87 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -17,9 +17,6 @@ import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/merge import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { MergeEditorSerializer } from './mergeEditorSerializer'; - -//#region Editor Descriptior - Registry.as(EditorExtensions.EditorPane).registerEditorPane( EditorPaneDescriptor.create( MergeEditor, diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 3a1e6b6807915..d3a8f704b3610 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -29,8 +29,13 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { localize } from 'vs/nls'; import { ILabelService } from 'vs/platform/label/common/label'; import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IAction } from 'vs/base/common/actions'; + +export const ctxIsMergeEditor = new RawContextKey('isMergeEditor', false); export class MergeEditor extends EditorPane { @@ -47,12 +52,16 @@ export class MergeEditor extends EditorPane { constructor( @IInstantiationService private readonly instantiation: IInstantiationService, @ILabelService private readonly _labelService: ILabelService, + @IMenuService private readonly _menuService: IMenuService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, @IThemeService themeService: IThemeService, ) { super(MergeEditor.ID, telemetryService, themeService, storageService); + ctxIsMergeEditor.bindTo(_contextKeyService).set(true); + const reentrancyBarrier = new ReentrancyBarrier(); this._store.add(this.inputOneView.editor.onDidScrollChange(c => { if (c.scrollTopChanged) { @@ -80,10 +89,25 @@ export class MergeEditor extends EditorPane { })); // TODO@jrieken make this proper: add menu id and allow extensions to contribute - const acceptBtn = this.instantiation.createInstance(FloatingClickWidget, this.inputResultView.editor, 'Accept Merge', ''); - acceptBtn.render(); - this._store.add(acceptBtn.onClick(() => { console.log('DO IT'); })); - this._store.add(acceptBtn); + const toolbarMenu = this._menuService.createMenu(MenuId.MergeToolbar, this._contextKeyService); + const toolbarMenuDisposables = new DisposableStore(); + const toolbarMenuRender = () => { + toolbarMenuDisposables.clear(); + + const actions: IAction[] = []; + createAndFillInActionBarActions(toolbarMenu, { renderShortTitle: true, shouldForwardArgs: true }, actions); + if (actions.length > 0) { + const [first] = actions; + const acceptBtn = this.instantiation.createInstance(FloatingClickWidget, this.inputResultView.editor, first.label, first.id); + toolbarMenuDisposables.add(acceptBtn.onClick(() => first.run(this.inputResultView.editor.getModel()?.uri))); + toolbarMenuDisposables.add(acceptBtn); + acceptBtn.render(); + } + }; + this._store.add(toolbarMenu); + this._store.add(toolbarMenuDisposables); + this._store.add(toolbarMenu.onDidChange(toolbarMenuRender)); + toolbarMenuRender(); } override dispose(): void { @@ -116,7 +140,6 @@ export class MergeEditor extends EditorPane { this.inputOneView.setModel(model.inputOne, localize('yours', 'Yours'), undefined); this.inputTwoView.setModel(model.inputTwo, localize('theirs', 'Theirs',), undefined); this.inputResultView.setModel(model.result, localize('result', 'Result',), this._labelService.getUriLabel(model.result.uri, { relative: true })); - } // override clearInput(): void { diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index 7e06c120f9562..b43e964676d23 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -252,6 +252,12 @@ const apiMenus: IAPIMenu[] = [ supportsSubmenus: false, proposed: 'inlineCompletions' }, + { + key: 'merge/toolbar', + id: MenuId.MergeToolbar, + description: localize('merge.toolbar', "The prominent botton in the merge editor"), + proposed: 'contribMergeEditorToolbar' + } ]; namespace schema { diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 4b51252484a14..bf9cf9d7024f9 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -11,6 +11,7 @@ export const allApiProposals = Object.freeze({ commentsResolvedState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentsResolvedState.d.ts', contribLabelFormatterWorkspaceTooltip: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribLabelFormatterWorkspaceTooltip.d.ts', contribMenuBarHome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribMenuBarHome.d.ts', + contribMergeEditorToolbar: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribMergeEditorToolbar.d.ts', contribRemoteHelp: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribRemoteHelp.d.ts', contribViewsRemote: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts', contribViewsWelcome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts', diff --git a/src/vscode-dts/vscode.proposed.contribMergeEditorToolbar.d.ts b/src/vscode-dts/vscode.proposed.contribMergeEditorToolbar.d.ts new file mode 100644 index 0000000000000..323ff90cecb7b --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribMergeEditorToolbar.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `mergeEditor/toolbar` menu From d4a3656ab354e17bc047de4590d0b9d58c3c73e5 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 10 May 2022 20:07:24 +0200 Subject: [PATCH 031/942] smart/scoped update when policy values change --- .../common/configurationModels.ts | 14 +++- .../configuration/common/configurations.ts | 84 +++++++++++-------- 2 files changed, 59 insertions(+), 39 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index de218cadea1d2..ad704762f681e 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -21,7 +21,7 @@ import { Workspace } from 'vs/platform/workspace/common/workspace'; export class ConfigurationModel implements IConfigurationModel { - private isFrozen: boolean = false; + private frozen: boolean = false; private readonly overrideConfigurations = new Map(); constructor( @@ -47,6 +47,10 @@ export class ConfigurationModel implements IConfigurationModel { return this._keys.length === 0 && Object.keys(this._contents).length === 0 && this._overrides.length === 0; } + isFrozen(): boolean { + return this.frozen; + } + getValue(section: string | undefined): V { return section ? getConfigurationValue(this.contents, section) : this.contents; } @@ -113,10 +117,14 @@ export class ConfigurationModel implements IConfigurationModel { } freeze(): ConfigurationModel { - this.isFrozen = true; + this.frozen = true; return this; } + clone(): ConfigurationModel { + return new ConfigurationModel(objects.deepClone(this.contents), [...this.keys], objects.deepClone(this.overrides)); + } + private createOverrideConfigurationModel(identifier: string): ConfigurationModel { const overrideContents = this.getContentsForOverrideIdentifer(identifier); @@ -161,7 +169,7 @@ export class ConfigurationModel implements IConfigurationModel { } private checkAndFreeze(data: T): T { - if (this.isFrozen && !Object.isFrozen(data)) { + if (this.frozen && !Object.isFrozen(data)) { return objects.deepFreeze(data); } return data; diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index ed855abf2ab9c..9e9603e9c615f 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -3,9 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { coalesce } from 'vs/base/common/arrays'; import { IStringDictionary } from 'vs/base/common/collections'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { equals } from 'vs/base/common/objects'; import { addToValueTree, IOverrides, toValuesTree } from 'vs/platform/configuration/common/configuration'; import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { Extensions, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; @@ -13,7 +15,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; -import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; +import { IPolicyService, NullPolicyService, PolicyName } from 'vs/platform/policy/common/policy'; import { Registry } from 'vs/platform/registry/common/platform'; export class DefaultConfiguration extends Disposable { @@ -84,6 +86,10 @@ export class PolicyConfiguration extends Disposable { readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event; private readonly policyService: IPolicyService; + private readonly policyNamesToKeys = new Map(); + + private _configurationModel = new ConfigurationModel(); + get configurationModel() { return this._configurationModel; } constructor( private readonly defaultConfiguration: DefaultConfiguration, @@ -95,50 +101,56 @@ export class PolicyConfiguration extends Disposable { this.policyService = environmentService.policyFile ? new FilePolicyService(environmentService.policyFile, fileService, logService) : new NullPolicyService(); } - private _configurationModel: ConfigurationModel | undefined; - get configurationModel(): ConfigurationModel { - if (!this._configurationModel) { - const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); - const keys: string[] = []; - const contents: any = Object.create(null); - for (const key of this.defaultConfiguration.configurationModel.keys) { - const policyName = configurationProperties[key].policy?.name; - if (!policyName) { - continue; - } - const value = this.policyService.getPolicyValue(policyName); - if (value === undefined) { - continue; - } - keys.push(key); - addToValueTree(contents, key, value, message => console.error(`Conflict in policy settings: ${message}`)); - } - this._configurationModel = new ConfigurationModel(contents, keys, []); - } + async initialize(): Promise { + await this.policyService.initialize(); + this.updateKeys(this.defaultConfiguration.configurationModel.keys, false); + this._register(this.policyService.onDidChange(policyNames => this.onDidChangePolicies(policyNames))); + this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties }) => this.updateKeys(properties, true))); return this._configurationModel; } - async initialize(): Promise { - await this.policyService.initialize(); - this._register(this.policyService.onDidChange(e => this.onDidChange())); - this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties }) => this.onDidDefaultConfigurationChange(properties))); - return this.reload(); + private updateKeys(keys: string[], trigger: boolean): void { + const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); + const keyPolicyNamePairs: [string, PolicyName][] = coalesce(keys.map(key => { + const policyName = configurationProperties[key].policy?.name; + return policyName ? [key, policyName] : undefined; + })); + this.update(keyPolicyNamePairs, trigger); } - reload(): ConfigurationModel { - this._configurationModel = undefined; - return this.configurationModel; + private onDidChangePolicies(policyNames: readonly PolicyName[]): void { + const keyPolicyNamePairs: [string, PolicyName][] = coalesce(policyNames.map(policyName => { + const key = this.policyNamesToKeys.get(policyName); + return key ? [key, policyName] : undefined; + })); + this.update(keyPolicyNamePairs, true); } - private onDidDefaultConfigurationChange(properties: string[]): void { - const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); - if (properties.some(key => configurationProperties[key].policy?.name)) { - this.onDidChange(); + private update(keyPolicyNamePairs: [string, PolicyName][], trigger: boolean): void { + if (!keyPolicyNamePairs.length) { + return; } - } - private onDidChange(): void { - this._onDidChangeConfiguration.fire(this.reload()); + const updated: string[] = []; + this._configurationModel = this._configurationModel.isFrozen() ? this._configurationModel.clone() : this._configurationModel; + const isEmpty = this._configurationModel.isEmpty(); + for (const [key, policyName] of keyPolicyNamePairs) { + this.policyNamesToKeys.set(policyName, key); + const value = this.policyService.getPolicyValue(policyName); + if (!isEmpty && equals(this._configurationModel.getValue(key), value)) { + continue; + } + if (value === undefined) { + this._configurationModel.removeValue(key); + } else { + this._configurationModel.setValue(key, value); + } + updated.push(key); + } + + if (updated.length && trigger) { + this._onDidChangeConfiguration.fire(this._configurationModel); + } } } From 02f42326ab9c83b822e1860e528457c08fa4f40a Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 11 May 2022 12:03:41 +0200 Subject: [PATCH 032/942] use descriptive view sizing for grid --- .../mergeEditor/browser/mergeEditor.ts | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index d3a8f704b3610..c300d43dd5459 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -17,8 +17,8 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { BugIndicatingError } from 'vs/base/common/errors'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { Direction, Grid, IView, IViewSize } from 'vs/base/browser/ui/grid/grid'; -import { Sizing } from 'vs/base/browser/ui/splitview/splitview'; +import { Grid, IView, IViewSize, SerializableGrid } from 'vs/base/browser/ui/grid/grid'; +import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; import { ITextModel } from 'vs/editor/common/model'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -117,10 +117,29 @@ export class MergeEditor extends EditorPane { protected createEditor(parent: HTMLElement): void { parent.classList.add('merge-editor'); - this._grid = new Grid(this.inputResultView, { styles: { separatorBorder: this.theme.getColor(settingsSashBorder) ?? Color.transparent } }); - this._grid.addView(this.inputOneView, Sizing.Distribute, this.inputResultView, Direction.Up); - this._grid.addView(this.inputTwoView, Sizing.Distribute, this.inputOneView, Direction.Right); + this._grid = SerializableGrid.from({ + orientation: Orientation.VERTICAL, + size: 100, + groups: [ + { + size: 38, + groups: [{ + data: this.inputOneView + }, { + data: this.inputTwoView + }] + }, + { + size: 62, + data: this.inputResultView + }, + ] + }, { + styles: { separatorBorder: this.theme.getColor(settingsSashBorder) ?? Color.transparent }, + proportionalLayout: true + }); + reset(parent, this._grid.element); } From abd3e66bac0970c2a87c627903053f2f4f28da8d Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 11 May 2022 12:04:04 +0200 Subject: [PATCH 033/942] use git-fs to read base, ours, and theirs --- extensions/git/src/main.ts | 4 +-- extensions/git/src/mergeInfoProvider.ts | 47 ------------------------- extensions/git/src/repository.ts | 3 +- extensions/git/src/uri.ts | 11 ++++++ 4 files changed, 13 insertions(+), 52 deletions(-) delete mode 100644 extensions/git/src/mergeInfoProvider.ts diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 4977cd4058cbc..24768c1dba417 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -25,7 +25,6 @@ import { GitTimelineProvider } from './timelineProvider'; import { registerAPICommands } from './api/api1'; import { TerminalEnvironmentManager } from './terminal'; import { OutputChannelLogger } from './log'; -import { GitMergeShowContentProvider } from './mergeInfoProvider'; const deactivateTasks: { (): Promise }[] = []; @@ -103,8 +102,7 @@ async function createModel(context: ExtensionContext, outputChannelLogger: Outpu new GitFileSystemProvider(model), new GitDecorations(model), new GitProtocolHandler(), - new GitTimelineProvider(model, cc), - new GitMergeShowContentProvider(model) + new GitTimelineProvider(model, cc) ); checkGitVersion(info); diff --git a/extensions/git/src/mergeInfoProvider.ts b/extensions/git/src/mergeInfoProvider.ts deleted file mode 100644 index 83853a18f47e1..0000000000000 --- a/extensions/git/src/mergeInfoProvider.ts +++ /dev/null @@ -1,47 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { TextDocumentContentProvider, Uri, workspace } from 'vscode'; -import { Model } from './model'; - -export class GitMergeShowContentProvider implements TextDocumentContentProvider { - - static readonly Scheme = 'git-show'; - - readonly dispose: () => void; - - constructor(private model: Model) { - const reg = workspace.registerTextDocumentContentProvider(GitMergeShowContentProvider.Scheme, this); - this.dispose = reg.dispose.bind(reg); - } - - async provideTextDocumentContent(uri: Uri): Promise { - await this.model.isInitialized; - - const repository = this.model.getRepository(uri); - if (!repository) { - return undefined; - } - - if (!/^:[123]$/.test(uri.query)) { - return undefined; - } - - try { - return await repository.show(uri.query, uri.fsPath); - } catch (error) { - console.error(error); - return undefined; - } - } -} - -export function toMergeUris(uri: Uri): { base: Uri; ours: Uri; theirs: Uri } { - return { - base: uri.with({ scheme: GitMergeShowContentProvider.Scheme, query: ':1' }), - ours: uri.with({ scheme: GitMergeShowContentProvider.Scheme, query: ':2' }), - theirs: uri.with({ scheme: GitMergeShowContentProvider.Scheme, query: ':3' }), - }; -} diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 6d603936b80f2..4a81c8fac64b1 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -13,7 +13,7 @@ import { AutoFetcher } from './autofetch'; import { debounce, memoize, throttle } from './decorators'; import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions } from './git'; import { StatusBarCommands } from './statusbar'; -import { toGitUri } from './uri'; +import { toGitUri, toMergeUris } from './uri'; import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util'; import { IFileWatcher, watch } from './watch'; import { LogLevel, OutputChannelLogger } from './log'; @@ -21,7 +21,6 @@ import { IPushErrorHandlerRegistry } from './pushError'; import { ApiRepository } from './api/api1'; import { IRemoteSourcePublisherRegistry } from './remotePublisher'; import { ActionButtonCommand } from './actionButton'; -import { toMergeUris } from './mergeInfoProvider'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); diff --git a/extensions/git/src/uri.ts b/extensions/git/src/uri.ts index 94e6b5e38aeeb..5694c920d6b15 100644 --- a/extensions/git/src/uri.ts +++ b/extensions/git/src/uri.ts @@ -51,3 +51,14 @@ export function toGitUri(uri: Uri, ref: string, options: GitUriOptions = {}): Ur query: JSON.stringify(params) }); } + +/** + * Assuming `uri` is being merged it creates uris for `base`, `ours`, and `theirs` + */ +export function toMergeUris(uri: Uri): { base: Uri; ours: Uri; theirs: Uri } { + return { + base: toGitUri(uri, ':1'), + ours: toGitUri(uri, ':2'), + theirs: toGitUri(uri, ':3'), + }; +} From 27fc8f70758943221c8a9c0a248b4a4f7b18b198 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 11 May 2022 14:13:30 +0200 Subject: [PATCH 034/942] First draft of the model implementation. --- src/vs/base/common/arrays.ts | 16 + src/vs/base/common/errors.ts | 4 +- .../mergeEditor/browser/mergeEditorInput.ts | 6 +- .../mergeEditor/browser/mergeEditorModel.ts | 207 ++++++++++- .../contrib/mergeEditor/browser/model.ts | 341 ++++++++++++++++++ 5 files changed, 568 insertions(+), 6 deletions(-) create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model.ts diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 8f6ab7661ae3d..e876d9d4304ae 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -748,15 +748,31 @@ export class ArrayQueue { } peek(): T | undefined { + if (this.length === 0) { + return undefined; + } return this.items[this.firstIdx]; } + peekLast(): T | undefined { + if (this.length === 0) { + return undefined; + } + return this.items[this.lastIdx]; + } + dequeue(): T | undefined { const result = this.items[this.firstIdx]; this.firstIdx++; return result; } + removeLast(): T | undefined { + const result = this.items[this.lastIdx]; + this.lastIdx--; + return result; + } + takeCount(count: number): T[] { const result = this.items.slice(this.firstIdx, this.firstIdx + count); this.firstIdx += count; diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 4f397866ae464..38ca3e4e102c1 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -259,8 +259,8 @@ export class ErrorNoTelemetry extends Error { * Only catch this error to recover gracefully from bugs. */ export class BugIndicatingError extends Error { - constructor(message: string) { - super(message); + constructor(message?: string) { + super(message || 'An unexpected bug occurred.'); Object.setPrototypeOf(this, BugIndicatingError.prototype); // Because we know for sure only buggy code throws this, diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts index cb5773e9f8694..8943def874155 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts @@ -14,7 +14,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IUntypedEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; -import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; +import { MergeEditorModel, MergeEditorModelFactory } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -31,6 +31,7 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { private _model?: MergeEditorModel; private _outTextModel?: ITextFileEditorModel; + private readonly mergeEditorModelFactory = this._instaService.createInstance(MergeEditorModelFactory); constructor( private readonly _anchestor: URI, @@ -97,8 +98,7 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { const inputTwo = await this._textModelService.createModelReference(this._inputTwo); const result = await this._textModelService.createModelReference(this._result); - this._model = this._instaService.createInstance( - MergeEditorModel, + this._model = await this.mergeEditorModelFactory.create( anchestor.object.textEditorModel, inputOne.object.textEditorModel, inputTwo.object.textEditorModel, diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts index 172a5c8e1a02b..ce2eb6b3afbd5 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -3,18 +3,223 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { compareBy, numberComparator } from 'vs/base/common/arrays'; +import { BugIndicatingError } from 'vs/base/common/errors'; import { ITextModel } from 'vs/editor/common/model'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; +import { MergeableDiff, LineEdit, LineEdits, LineDiff, MergeState, LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; + +export class MergeEditorModelFactory { + constructor( + @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, + //@IInstantiationService private readonly instantiationService: IInstantiationService, + ) { + } + + public async create( + ancestor: ITextModel, + inputOne: ITextModel, + inputTwo: ITextModel, + result: ITextModel, + ): Promise { + + const ancestorToInputOneDiffPromise = this._editorWorkerService.computeDiff( + ancestor.uri, + inputOne.uri, + false, + 1000 + ); + const ancestorToInputTwoDiffPromise = this._editorWorkerService.computeDiff( + ancestor.uri, + inputTwo.uri, + false, + 1000 + ); + + const [ancestorToInputOneDiff, ancestorToInputTwoDiff] = await Promise.all([ + ancestorToInputOneDiffPromise, + ancestorToInputTwoDiffPromise, + ]); + + const changesInput1 = + ancestorToInputOneDiff?.changes.map((c) => + LineDiff.fromLineChange(c, ancestor, inputOne) + ) || []; + const changesInput2 = + ancestorToInputTwoDiff?.changes.map((c) => + LineDiff.fromLineChange(c, ancestor, inputTwo) + ) || []; + + return new MergeEditorModel( + InternalSymbol, + ancestor, + inputOne, + inputTwo, + result, + changesInput1, + changesInput2, + ); + } +} + +const InternalSymbol = Symbol(); export class MergeEditorModel extends EditorModel { + private resultEdits = new ResultEdits([], this.ancestor, this.result); constructor( - readonly anchestor: ITextModel, + symbol: typeof InternalSymbol, + readonly ancestor: ITextModel, readonly inputOne: ITextModel, readonly inputTwo: ITextModel, readonly result: ITextModel, + private readonly inputOneLinesDiffs: readonly LineDiff[], + private readonly inputTwoLinesDiffs: readonly LineDiff[], ) { super(); + + result.setValue(ancestor.getValue()); + + /* + // Apply all non-conflicts diffs + const lineEditsArr: LineEdit[] = []; + for (const diff of this.mergeableDiffs) { + if (!diff.isConflicting) { + for (const d of diff.inputOneDiffs) { + lineEditsArr.push(d.getLineEdit()); + } + for (const d of diff.inputTwoDiffs) { + lineEditsArr.push(d.getLineEdit()); + } + } + } + new LineEdits(lineEditsArr).apply(result); + */ + + console.log(this, 'hello'); + (globalThis as any)['mergeEditorModel'] = this; + } + + public get resultDiffs(): readonly LineDiff[] { + return this.resultEdits.diffs; + } + + public readonly mergeableDiffs = MergeableDiff.fromDiffs(this.inputOneLinesDiffs, this.inputTwoLinesDiffs); + + public getState(conflict: MergeableDiff): MergeState | undefined { + return undefined; + } + + // Undo all edits of result that conflict with the conflict!! + public setConflictResolutionStatus(conflict: MergeableDiff, status: MergeState): void { + const existingDiff = this.resultEdits.findConflictingDiffs(conflict.originalRange); + if (existingDiff) { + this.resultEdits.removeDiff(existingDiff); + } + + const edit = status.input1 ? conflict.getInput1LineEdit() : conflict.getInput2LineEdit(); + if (edit) { + this.resultEdits.applyEditRelativeToOriginal(edit); + } + } +} + +class ResultEdits { + constructor( + private _diffs: LineDiff[], + private readonly originalTextModel: ITextModel, + private readonly textModel: ITextModel, + ) { + this._diffs.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator)); + } + + public get diffs(): readonly LineDiff[] { + return this._diffs; + } + + public removeDiff(diffToRemove: LineDiff): void { + const len = this._diffs.length; + this._diffs = this._diffs.filter((d) => d !== diffToRemove); + if (len === this._diffs.length) { + throw new BugIndicatingError(); + } + + const edits = new LineEdits([diffToRemove.getReverseLineEdit()]); + edits.apply(this.textModel); + + this._diffs = this._diffs.map((d) => + d.modifiedRange.isAfter(diffToRemove.modifiedRange) + ? new LineDiff( + d.originalTextModel, + d.originalRange, + d.modifiedTextModel, + d.modifiedRange.delta( + diffToRemove.originalRange.lineCount - diffToRemove.modifiedRange.lineCount + ) + ) + : d + ); } + /** + * Edit must be conflict free. + */ + public applyEditRelativeToOriginal(edit: LineEdit): void { + let firstAfter = false; + let delta = 0; + const newDiffs = new Array(); + for (let i = 0; i < this._diffs.length; i++) { + const diff = this._diffs[i]; + + if (diff.originalRange.intersects(edit.range)) { + throw new BugIndicatingError(); + } else if (diff.originalRange.isAfter(edit.range)) { + if (!firstAfter) { + firstAfter = true; + + newDiffs.push(new LineDiff( + this.originalTextModel, + edit.range, + this.textModel, + new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) + )); + } + + newDiffs.push(new LineDiff( + diff.originalTextModel, + diff.originalRange, + diff.modifiedTextModel, + diff.modifiedRange.delta(edit.newLines.length - edit.range.lineCount) + )); + } else { + newDiffs.push(diff); + } + + if (!firstAfter) { + delta += diff.modifiedRange.lineCount - diff.originalRange.lineCount; + } + } + + if (!firstAfter) { + firstAfter = true; + + newDiffs.push(new LineDiff( + this.originalTextModel, + edit.range, + this.textModel, + new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) + )); + } + this._diffs = newDiffs; + + const edits = new LineEdits([new LineEdit(edit.range.delta(delta), edit.newLines, edit.data)]); + edits.apply(this.textModel); + } + + // TODO return many! + public findConflictingDiffs(rangeInOriginalTextModel: LineRange): LineDiff | undefined { + // TODO binary search + return this.diffs.find(d => d.originalRange.intersects(rangeInOriginalTextModel)); + } } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model.ts b/src/vs/workbench/contrib/mergeEditor/browser/model.ts new file mode 100644 index 0000000000000..c26686a046698 --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/model.ts @@ -0,0 +1,341 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ArrayQueue, compareBy, numberComparator } from 'vs/base/common/arrays'; +import { BugIndicatingError } from 'vs/base/common/errors'; +import { Range } from 'vs/editor/common/core/range'; +import { ILineChange } from 'vs/editor/common/diff/diffComputer'; +import { ITextModel } from 'vs/editor/common/model'; + +export class LineEdits { + constructor(public readonly edits: readonly LineEdit[]) { + + } + + public apply(model: ITextModel): void { + model.pushEditOperations( + null, + this.edits.map((e) => ({ + range: new Range(e.range.startLineNumber, 1, e.range.endLineNumberExclusive, 1), + text: e.newLines.map(l => l + '\n').join(), + })), + () => null + ); + } +} + +export class LineEdit { + constructor( + public readonly range: LineRange, + public readonly newLines: string[], + public readonly data: T + ) { } +} + +export class MergeableDiff { + public static fromDiffs( + diffs1: readonly LineDiff[], + diffs2: readonly LineDiff[] + ): MergeableDiff[] { + const compareByStartLineNumber = compareBy( + (d) => d.originalRange.startLineNumber, + numberComparator + ); + + const queueDiffs1 = new ArrayQueue( + diffs1.slice().sort(compareByStartLineNumber) + ); + const queueDiffs2 = new ArrayQueue( + diffs2.slice().sort(compareByStartLineNumber) + ); + + const result = new Array(); + + while (true) { + const lastDiff1 = queueDiffs1.peekLast(); + const lastDiff2 = queueDiffs2.peekLast(); + + if ( + lastDiff1 && + (!lastDiff2 || + lastDiff1.originalRange.startLineNumber >= + lastDiff2.originalRange.startLineNumber) + ) { + queueDiffs1.removeLast(); + + const otherConflictingWith = + queueDiffs2.takeFromEndWhile((d) => d.conflicts(lastDiff1)) || []; + + const singleLinesDiff = LineDiff.hull(otherConflictingWith); + + const moreConflictingWith = + (singleLinesDiff && + queueDiffs1.takeFromEndWhile((d) => + d.conflicts(singleLinesDiff) + )) || + []; + moreConflictingWith.push(lastDiff1); + + result.push( + new MergeableDiff(moreConflictingWith, otherConflictingWith) + ); + } else if (lastDiff2) { + queueDiffs2.removeLast(); + + const otherConflictingWith = + queueDiffs1.takeFromEndWhile((d) => d.conflicts(lastDiff2)) || []; + + const singleLinesDiff = LineDiff.hull(otherConflictingWith); + + const moreConflictingWith = + (singleLinesDiff && + queueDiffs2.takeFromEndWhile((d) => + d.conflicts(singleLinesDiff) + )) || + []; + moreConflictingWith.push(lastDiff2); + + result.push( + new MergeableDiff(otherConflictingWith, moreConflictingWith) + ); + } else { + break; + } + } + + result.reverse(); + + return result; + } + + public readonly originalRange = LineRange.hull( + this.input1Diffs + .map((d) => d.originalRange) + .concat(this.input2Diffs.map((d) => d.originalRange)) + )!; + + constructor( + public readonly input1Diffs: readonly LineDiff[], + public readonly input2Diffs: readonly LineDiff[] + ) { } + + public get isConflicting(): boolean { + return this.input1Diffs.length > 0 && this.input2Diffs.length > 0; + } + + public getInput1LineEdit(): LineEdit | undefined { + if (this.input1Diffs.length === 0) { + return undefined; + } + if (this.input1Diffs.length === 1) { + return this.input1Diffs[0].getLineEdit(); + } else { + throw new Error('Method not implemented.'); + } + } + + public getInput2LineEdit(): LineEdit | undefined { + if (this.input2Diffs.length === 0) { + return undefined; + } + if (this.input2Diffs.length === 1) { + return this.input2Diffs[0].getLineEdit(); + } else { + throw new Error('Method not implemented.'); + } + } +} + +export class LineRange { + public static hull(ranges: LineRange[]): LineRange | undefined { + if (ranges.length === 0) { + return undefined; + } + + let startLineNumber = Number.MAX_SAFE_INTEGER; + let endLineNumber = 0; + for (const range of ranges) { + startLineNumber = Math.min(startLineNumber, range.startLineNumber); + endLineNumber = Math.max(endLineNumber, range.startLineNumber + range.lineCount); + } + return new LineRange(startLineNumber, endLineNumber - startLineNumber); + } + + constructor( + public readonly startLineNumber: number, + public readonly lineCount: number + ) { + if (lineCount < 0) { + throw new BugIndicatingError(); + } + } + + public get endLineNumberExclusive(): number { + return this.startLineNumber + this.lineCount; + } + + public get isEmpty(): boolean { + return this.lineCount === 0; + } + + public intersects(other: LineRange): boolean { + return ( + this.endLineNumberExclusive >= other.startLineNumber && + other.endLineNumberExclusive >= this.startLineNumber + ); + } + + public isAfter(modifiedRange: LineRange): boolean { + return this.startLineNumber >= modifiedRange.endLineNumberExclusive; + } + + public delta(lineDelta: number): LineRange { + return new LineRange(this.startLineNumber + lineDelta, this.lineCount); + } + + public toString() { + return `[${this.startLineNumber},${this.endLineNumberExclusive})`; + } +} + +export class LineDiff { + public static fromLineChange(lineChange: ILineChange, originalTextModel: ITextModel, modifiedTextModel: ITextModel): LineDiff { + let originalRange: LineRange; + if (lineChange.originalEndLineNumber === 0) { + // Insertion + originalRange = new LineRange(lineChange.originalStartLineNumber + 1, 0); + } else { + originalRange = new LineRange(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1); + } + + let modifiedRange: LineRange; + if (lineChange.modifiedEndLineNumber === 0) { + // Insertion + modifiedRange = new LineRange(lineChange.modifiedStartLineNumber + 1, 0); + } else { + modifiedRange = new LineRange(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1); + } + + return new LineDiff( + originalTextModel, + originalRange, + modifiedTextModel, + modifiedRange, + ); + } + + public static hull(lineDiffs: LineDiff[]): LineDiff | undefined { + if (lineDiffs.length === 0) { + return undefined; + } + + return new LineDiff( + lineDiffs[0].originalTextModel, + LineRange.hull(lineDiffs.map((d) => d.originalRange))!, + lineDiffs[0].modifiedTextModel, + LineRange.hull(lineDiffs.map((d) => d.modifiedRange))!, + ); + } + + constructor( + public readonly originalTextModel: ITextModel, + public readonly originalRange: LineRange, + public readonly modifiedTextModel: ITextModel, + public readonly modifiedRange: LineRange, + ) { + } + + private ensureSameOriginalModel(other: LineDiff): void { + if (this.originalTextModel !== other.originalTextModel) { + // Both changes must refer to the same original model + throw new BugIndicatingError(); + } + } + + public conflicts(other: LineDiff): boolean { + this.ensureSameOriginalModel(other); + return this.originalRange.intersects(other.originalRange); + } + + public isStrictBefore(other: LineDiff): boolean { + this.ensureSameOriginalModel(other); + return this.originalRange.endLineNumberExclusive <= other.originalRange.startLineNumber; + } + + public getLineEdit(): LineEdit { + return new LineEdit( + this.originalRange, + this.getModifiedLines(), + undefined + ); + } + + public getReverseLineEdit(): LineEdit { + return new LineEdit( + this.modifiedRange, + this.getOriginalLines(), + undefined + ); + } + + private getModifiedLines(): string[] { + const result = new Array(this.modifiedRange.lineCount); + for (let i = 0; i < this.modifiedRange.lineCount; i++) { + result[i] = this.modifiedTextModel.getLineContent(this.modifiedRange.startLineNumber + i); + } + return result; + } + + private getOriginalLines(): string[] { + const result = new Array(this.originalRange.lineCount); + for (let i = 0; i < this.originalRange.lineCount; i++) { + result[i] = this.originalTextModel.getLineContent(this.originalRange.startLineNumber + i); + } + return result; + } +} + + +export class MergeState { + constructor( + public readonly input1: boolean, + public readonly input2: boolean, + public readonly input2First: boolean + ) { } + + public withInput1(value: boolean): MergeState { + return new MergeState( + value, + this.input2, + value && this.isEmpty ? false : this.input2First + ); + } + + public withInput2(value: boolean): MergeState { + return new MergeState( + this.input1, + value, + value && this.isEmpty ? true : this.input2First + ); + } + + public get isEmpty(): boolean { + return !this.input1 && !this.input2; + } + + public toString(): string { + const arr: ('1' | '2')[] = []; + if (this.input1) { + arr.push('1'); + } + if (this.input2) { + arr.push('2'); + } + if (this.input2First) { + arr.reverse(); + } + return arr.join(','); + } +} From a38e09d1380f81c84ca19abfdd2e8d04e107452c Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 11 May 2022 15:37:12 +0200 Subject: [PATCH 035/942] add toggle layout action - column vs 2 by 1 --- .../browser/mergeEditor.contribution.ts | 35 ++++++++++++++-- .../mergeEditor/browser/mergeEditor.ts | 40 ++++++++++++++----- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts index 88fc90c9d2a87..3f72bfca52422 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -6,16 +6,17 @@ import { localize } from 'vs/nls'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor'; -import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditor'; +import { ctxIsMergeEditor, ctxUsesColumnLayout, MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditor'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { MergeEditorSerializer } from './mergeEditorSerializer'; +import { Codicon } from 'vs/base/common/codicons'; Registry.as(EditorExtensions.EditorPane).registerEditorPane( EditorPaneDescriptor.create( @@ -33,7 +34,35 @@ Registry.as(EditorExtensions.EditorFactory).registerEdit MergeEditorSerializer ); -registerAction2(class Foo extends Action2 { +registerAction2(class ToggleLayout extends Action2 { + + constructor() { + super({ + id: 'merge.toggleLayout', + title: localize('toggle.title', "Switch to column view"), + icon: Codicon.layoutCentered, + toggled: { + condition: ctxUsesColumnLayout, + icon: Codicon.layoutPanel, + title: localize('toggle.title2', "Switch to 2 by 1 view"), + }, + menu: [{ + id: MenuId.EditorTitle, + when: ctxIsMergeEditor, + group: 'navigation' + }] + }); + } + + run(accessor: ServicesAccessor): void { + const { activeEditorPane } = accessor.get(IEditorService); + if (activeEditorPane instanceof MergeEditor) { + activeEditorPane.toggleLayout(); + } + } +}); + +registerAction2(class Open extends Action2 { constructor() { super({ diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index c300d43dd5459..93918c9ae43cd 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -17,8 +17,8 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { BugIndicatingError } from 'vs/base/common/errors'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { Grid, IView, IViewSize, SerializableGrid } from 'vs/base/browser/ui/grid/grid'; -import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; +import { Direction, Grid, IView, IViewSize, SerializableGrid } from 'vs/base/browser/ui/grid/grid'; +import { Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview'; import { ITextModel } from 'vs/editor/common/model'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -29,13 +29,14 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { localize } from 'vs/nls'; import { ILabelService } from 'vs/platform/label/common/label'; import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; -import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IAction } from 'vs/base/common/actions'; export const ctxIsMergeEditor = new RawContextKey('isMergeEditor', false); +export const ctxUsesColumnLayout = new RawContextKey('mergeEditorUsesColumnLayout', false); export class MergeEditor extends EditorPane { @@ -49,6 +50,9 @@ export class MergeEditor extends EditorPane { private readonly inputTwoView = this.instantiation.createInstance(CodeEditorView, { readonly: true }); private readonly inputResultView = this.instantiation.createInstance(CodeEditorView, { readonly: false }); + private readonly _ctxIsMergeEditor: IContextKey; + private readonly _ctxUsesColumnLayout: IContextKey; + constructor( @IInstantiationService private readonly instantiation: IInstantiationService, @ILabelService private readonly _labelService: ILabelService, @@ -60,7 +64,8 @@ export class MergeEditor extends EditorPane { ) { super(MergeEditor.ID, telemetryService, themeService, storageService); - ctxIsMergeEditor.bindTo(_contextKeyService).set(true); + this._ctxIsMergeEditor = ctxIsMergeEditor.bindTo(_contextKeyService); + this._ctxUsesColumnLayout = ctxUsesColumnLayout.bindTo(_contextKeyService); const reentrancyBarrier = new ReentrancyBarrier(); this._store.add(this.inputOneView.editor.onDidScrollChange(c => { @@ -112,6 +117,7 @@ export class MergeEditor extends EditorPane { override dispose(): void { this._sessionDisposables.dispose(); + this._ctxIsMergeEditor.reset(); super.dispose(); } @@ -141,6 +147,7 @@ export class MergeEditor extends EditorPane { }); reset(parent, this._grid.element); + this._ctxUsesColumnLayout.set(false); } layout(dimension: Dimension): void { @@ -161,13 +168,9 @@ export class MergeEditor extends EditorPane { this.inputResultView.setModel(model.result, localize('result', 'Result',), this._labelService.getUriLabel(model.result.uri, { relative: true })); } - // override clearInput(): void { - // super.clearInput(); - // } - - // protected override setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { - // console.log('VISISBLE', visible); - // } + protected override setEditorVisible(visible: boolean): void { + this._ctxIsMergeEditor.set(visible); + } // ---- interact with "outside world" via `getControl`, `scopedContextKeyService` @@ -186,6 +189,21 @@ export class MergeEditor extends EditorPane { ? control.invokeWithinContext(accessor => accessor.get(IContextKeyService)) : undefined; } + + // --- layout + + private _usesColumnLayout = false; + + toggleLayout(): void { + if (!this._usesColumnLayout) { + this._grid.moveView(this.inputResultView, Sizing.Distribute, this.inputOneView, Direction.Right); + } else { + this._grid.moveView(this.inputResultView, this._grid.height * .62, this.inputOneView, Direction.Down); + this._grid.moveView(this.inputTwoView, Sizing.Distribute, this.inputOneView, Direction.Right); + } + this._usesColumnLayout = !this._usesColumnLayout; + this._ctxUsesColumnLayout.set(this._usesColumnLayout); + } } interface ICodeEditorViewOptions { From ce60d548e2cced5d3cf868e4a929ed0f8f9d8823 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 11 May 2022 16:16:11 +0200 Subject: [PATCH 036/942] - add tests - fix scenarios causing tests failures - check for duplicate policy names --- .../common/configurationRegistry.ts | 34 +++- .../configuration/common/configurations.ts | 73 +++---- .../test/common/configurationRegistry.test.ts | 26 +++ .../test/common/policyConfiguration.test.ts | 179 ++++++++++++++++++ .../policy/common/filePolicyService.ts | 4 +- src/vs/platform/policy/common/policy.ts | 6 + .../api/common/configurationExtensionPoint.ts | 4 +- .../browser/configurationService.ts | 11 +- .../configurationEditingService.test.ts | 37 +++- .../test/browser/configurationService.test.ts | 30 ++- 10 files changed, 352 insertions(+), 52 deletions(-) create mode 100644 src/vs/platform/configuration/test/common/policyConfiguration.test.ts diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 1f424d52cb76c..11331ac829bf6 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -11,6 +11,7 @@ import * as types from 'vs/base/common/types'; import * as nls from 'vs/nls'; import { getLanguageTagSettingPlainKey } from 'vs/platform/configuration/common/configuration'; import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { PolicyName } from 'vs/platform/policy/common/policy'; import { Registry } from 'vs/platform/registry/common/platform'; export enum EditPresentationTypes { @@ -89,6 +90,11 @@ export interface IConfigurationRegistry { */ getConfigurationProperties(): IStringDictionary; + /** + * Return all configurations by policy name + */ + getPolicyConfigurations(): Map; + /** * Returns all excluded configurations settings of all configuration nodes contributed to this registry. */ @@ -127,12 +133,12 @@ export const enum ConfigurationScope { MACHINE_OVERRIDABLE, } -export interface PolicyConfiguration { +export interface IPolicy { /** * The policy name. */ - readonly name: string; + readonly name: PolicyName; /** * The Code version in which this policy was introduced. @@ -193,7 +199,7 @@ export interface IConfigurationPropertySchema extends IJSONSchema { * When specified, this setting's value can always be overwritten by * a system-wide policy. */ - policy?: PolicyConfiguration; + policy?: IPolicy; } export interface IExtensionInfo { @@ -245,6 +251,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { private readonly defaultLanguageConfigurationOverridesNode: IConfigurationNode; private readonly configurationContributors: IConfigurationNode[]; private readonly configurationProperties: IStringDictionary; + private readonly policyConfigurations: Map; private readonly excludedConfigurationProperties: IStringDictionary; private readonly resourceLanguageSettingsSchema: IJSONSchema; private readonly overrideIdentifiers = new Set(); @@ -265,6 +272,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { this.configurationContributors = [this.defaultLanguageConfigurationOverridesNode]; this.resourceLanguageSettingsSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown editor configuration setting', allowTrailingCommas: true, allowComments: true }; this.configurationProperties = {}; + this.policyConfigurations = new Map(); this.excludedConfigurationProperties = {}; contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema); @@ -397,6 +405,10 @@ class ConfigurationRegistry implements IConfigurationRegistry { if (configuration.properties) { for (const key in configuration.properties) { properties.push(key); + const property = this.configurationProperties[key]; + if (property?.policy?.name) { + this.policyConfigurations.delete(property.policy.name); + } delete this.configurationProperties[key]; this.removeFromSchema(key, configuration.properties[key]); } @@ -421,12 +433,12 @@ class ConfigurationRegistry implements IConfigurationRegistry { let properties = configuration.properties; if (properties) { for (let key in properties) { - if (validate && validateProperty(key)) { + const property: IRegisteredConfigurationPropertySchema = properties[key]; + if (validate && validateProperty(key, property)) { delete properties[key]; continue; } - const property: IRegisteredConfigurationPropertySchema = properties[key]; property.source = extensionInfo; // update default value @@ -449,6 +461,9 @@ class ConfigurationRegistry implements IConfigurationRegistry { continue; } else { this.configurationProperties[key] = properties[key]; + if (properties[key].policy?.name) { + this.policyConfigurations.set(properties[key].policy!.name, key); + } } if (!properties[key].deprecationMessage && properties[key].markdownDeprecationMessage) { @@ -477,6 +492,10 @@ class ConfigurationRegistry implements IConfigurationRegistry { return this.configurationProperties; } + getPolicyConfigurations(): Map { + return this.policyConfigurations; + } + getExcludedConfigurationProperties(): IStringDictionary { return this.excludedConfigurationProperties; } @@ -647,7 +666,7 @@ export function getDefaultValue(type: string | string[] | undefined): any { const configurationRegistry = new ConfigurationRegistry(); Registry.add(Extensions.Configuration, configurationRegistry); -export function validateProperty(property: string): string | null { +export function validateProperty(property: string, schema: IRegisteredConfigurationPropertySchema): string | null { if (!property.trim()) { return nls.localize('config.property.empty', "Cannot register an empty property"); } @@ -657,6 +676,9 @@ export function validateProperty(property: string): string | null { if (configurationRegistry.getConfigurationProperties()[property] !== undefined) { return nls.localize('config.property.duplicate', "Cannot register '{0}'. This property is already registered.", property); } + if (schema.policy?.name && configurationRegistry.getPolicyConfigurations().get(schema.policy?.name) !== undefined) { + return nls.localize('config.policy.duplicate', "Cannot register '{0}'. The associated policy {1} is already registered with {2}.", property, schema.policy?.name, configurationRegistry.getPolicyConfigurations().get(schema.policy?.name)); + } return null; } diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index 9e9603e9c615f..a9cf1f256f9fb 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -15,7 +15,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; -import { IPolicyService, NullPolicyService, PolicyName } from 'vs/platform/policy/common/policy'; +import { IPolicyService, NullPolicyService, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; import { Registry } from 'vs/platform/registry/common/platform'; export class DefaultConfiguration extends Disposable { @@ -86,7 +86,6 @@ export class PolicyConfiguration extends Disposable { readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event; private readonly policyService: IPolicyService; - private readonly policyNamesToKeys = new Map(); private _configurationModel = new ConfigurationModel(); get configurationModel() { return this._configurationModel; } @@ -103,53 +102,55 @@ export class PolicyConfiguration extends Disposable { async initialize(): Promise { await this.policyService.initialize(); - this.updateKeys(this.defaultConfiguration.configurationModel.keys, false); + this.update(this.defaultConfiguration.configurationModel.keys, false); this._register(this.policyService.onDidChange(policyNames => this.onDidChangePolicies(policyNames))); - this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties }) => this.updateKeys(properties, true))); + this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties }) => this.update(properties, true))); return this._configurationModel; } - private updateKeys(keys: string[], trigger: boolean): void { - const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); - const keyPolicyNamePairs: [string, PolicyName][] = coalesce(keys.map(key => { - const policyName = configurationProperties[key].policy?.name; - return policyName ? [key, policyName] : undefined; - })); - this.update(keyPolicyNamePairs, trigger); + async reload(): Promise { + await this.policyService.refresh(); + this.update(this.defaultConfiguration.configurationModel.keys, false); + return this._configurationModel; } private onDidChangePolicies(policyNames: readonly PolicyName[]): void { - const keyPolicyNamePairs: [string, PolicyName][] = coalesce(policyNames.map(policyName => { - const key = this.policyNamesToKeys.get(policyName); - return key ? [key, policyName] : undefined; - })); - this.update(keyPolicyNamePairs, true); + const policyConfigurations = Registry.as(Extensions.Configuration).getPolicyConfigurations(); + const keys = coalesce(policyNames.map(policyName => policyConfigurations.get(policyName))); + this.update(keys, true); } - private update(keyPolicyNamePairs: [string, PolicyName][], trigger: boolean): void { - if (!keyPolicyNamePairs.length) { - return; - } - - const updated: string[] = []; - this._configurationModel = this._configurationModel.isFrozen() ? this._configurationModel.clone() : this._configurationModel; - const isEmpty = this._configurationModel.isEmpty(); - for (const [key, policyName] of keyPolicyNamePairs) { - this.policyNamesToKeys.set(policyName, key); - const value = this.policyService.getPolicyValue(policyName); - if (!isEmpty && equals(this._configurationModel.getValue(key), value)) { - continue; - } - if (value === undefined) { - this._configurationModel.removeValue(key); + private update(keys: string[], trigger: boolean): void { + const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); + const changed: [string, PolicyValue | undefined][] = []; + const wasEmpty = this._configurationModel.isEmpty(); + + for (const key of keys) { + const policyName = configurationProperties[key]?.policy?.name; + if (policyName) { + const policyValue = this.policyService.getPolicyValue(policyName); + if (wasEmpty ? policyValue !== undefined : !equals(this._configurationModel.getValue(key), policyValue)) { + changed.push([key, policyValue]); + } } else { - this._configurationModel.setValue(key, value); + if (this._configurationModel.getValue(key) !== undefined) { + changed.push([key, undefined]); + } } - updated.push(key); } - if (updated.length && trigger) { - this._onDidChangeConfiguration.fire(this._configurationModel); + if (changed.length) { + this._configurationModel = this._configurationModel.isFrozen() ? this._configurationModel.clone() : this._configurationModel; + for (const [key, policyValue] of changed) { + if (policyValue === undefined) { + this._configurationModel.removeValue(key); + } else { + this._configurationModel.setValue(key, policyValue); + } + } + if (trigger) { + this._onDidChangeConfiguration.fire(this._configurationModel); + } } } diff --git a/src/vs/platform/configuration/test/common/configurationRegistry.test.ts b/src/vs/platform/configuration/test/common/configurationRegistry.test.ts index 83314c83ad361..8e836674dae84 100644 --- a/src/vs/platform/configuration/test/common/configurationRegistry.test.ts +++ b/src/vs/platform/configuration/test/common/configurationRegistry.test.ts @@ -50,4 +50,30 @@ suite('ConfigurationRegistry', () => { assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 2, c: 3 }); }); + + test('registering multiple settings with same policy', async () => { + configurationRegistry.registerConfiguration({ + 'id': '_test_default', + 'type': 'object', + 'properties': { + 'policy1': { + 'type': 'object', + policy: { + name: 'policy', + minimumVersion: '1.0.0' + } + }, + 'policy2': { + 'type': 'object', + policy: { + name: 'policy', + minimumVersion: '1.0.0' + } + } + } + }); + const actual = configurationRegistry.getConfigurationProperties(); + assert.ok(actual['policy1'] !== undefined); + assert.ok(actual['policy2'] === undefined); + }); }); diff --git a/src/vs/platform/configuration/test/common/policyConfiguration.test.ts b/src/vs/platform/configuration/test/common/policyConfiguration.test.ts new file mode 100644 index 0000000000000..3f6e15bdd0c6a --- /dev/null +++ b/src/vs/platform/configuration/test/common/policyConfiguration.test.ts @@ -0,0 +1,179 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Event } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; +import { DefaultConfiguration, PolicyConfiguration } from 'vs/platform/configuration/common/configurations'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { Extensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { deepClone } from 'vs/base/common/objects'; + +suite('PolicyConfiguration', () => { + + let testObject: PolicyConfiguration; + let fileService: IFileService; + const policyFile = URI.file('policyFile').with({ scheme: 'vscode-tests' }); + const disposables = new DisposableStore(); + const policyConfigurationNode: IConfigurationNode = { + 'id': 'policyConfiguration', + 'order': 1, + 'title': 'a', + 'type': 'object', + 'properties': { + 'policy.settingA': { + 'type': 'boolean', + 'default': true, + policy: { + name: 'PolicySettingA', + minimumVersion: '1.0.0', + } + }, + 'nonPolicy.setting': { + 'type': 'boolean', + 'default': true + } + } + }; + + suiteSetup(() => Registry.as(Extensions.Configuration).registerConfiguration(policyConfigurationNode)); + suiteTeardown(() => Registry.as(Extensions.Configuration).deregisterConfigurations([policyConfigurationNode])); + + setup(async () => { + const defaultConfiguration = disposables.add(new DefaultConfiguration()); + await defaultConfiguration.initialize(); + fileService = disposables.add(new FileService(new NullLogService())); + const diskFileSystemProvider = disposables.add(new InMemoryFileSystemProvider()); + fileService.registerProvider(policyFile.scheme, diskFileSystemProvider); + testObject = disposables.add(new PolicyConfiguration(defaultConfiguration, fileService, { policyFile } as IEnvironmentService, new NullLogService())); + }); + + teardown(() => disposables.clear()); + + test('initialize: with policies', async () => { + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false }))); + + await testObject.initialize(); + const acutal = testObject.configurationModel; + + assert.strictEqual(acutal.getValue('policy.settingA'), false); + assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); + assert.deepStrictEqual(acutal.keys, ['policy.settingA']); + assert.deepStrictEqual(acutal.overrides, []); + }); + + test('initialize: no policies', async () => { + await testObject.initialize(); + const acutal = testObject.configurationModel; + + assert.deepStrictEqual(acutal.keys, []); + assert.deepStrictEqual(acutal.overrides, []); + assert.strictEqual(acutal.getValue('policy.settingA'), undefined); + assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); + }); + + test('initialize: with policies but not registered', async () => { + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false, 'PolicySettingB': false }))); + + await testObject.initialize(); + const acutal = testObject.configurationModel; + + assert.strictEqual(acutal.getValue('policy.settingA'), false); + assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); + assert.deepStrictEqual(acutal.keys, ['policy.settingA']); + assert.deepStrictEqual(acutal.overrides, []); + }); + + test('change: when policy is added', async () => { + await testObject.initialize(); + + const promise = Event.toPromise(testObject.onDidChangeConfiguration); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false }))); + await promise; + + const acutal = testObject.configurationModel; + assert.strictEqual(acutal.getValue('policy.settingA'), false); + assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); + assert.deepStrictEqual(acutal.keys, ['policy.settingA']); + assert.deepStrictEqual(acutal.overrides, []); + }); + + test('change: when policy is updated', async () => { + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false }))); + await testObject.initialize(); + + const promise = Event.toPromise(testObject.onDidChangeConfiguration); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': true }))); + await promise; + + const acutal = testObject.configurationModel; + assert.strictEqual(acutal.getValue('policy.settingA'), true); + assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); + assert.deepStrictEqual(acutal.keys, ['policy.settingA']); + assert.deepStrictEqual(acutal.overrides, []); + }); + + test('change: when policy is removed', async () => { + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false }))); + await testObject.initialize(); + + const promise = Event.toPromise(testObject.onDidChangeConfiguration); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({}))); + await promise; + + const acutal = testObject.configurationModel; + assert.strictEqual(acutal.getValue('policy.settingA'), undefined); + assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); + assert.deepStrictEqual(acutal.keys, []); + assert.deepStrictEqual(acutal.overrides, []); + }); + + test('change: when policy setting is registered', async () => { + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingB': false }))); + await testObject.initialize(); + + const promise = Event.toPromise(testObject.onDidChangeConfiguration); + policyConfigurationNode.properties!['policy.settingB'] = { + 'type': 'boolean', + 'default': true, + policy: { + name: 'PolicySettingB', + minimumVersion: '1.0.0', + } + }; + Registry.as(Extensions.Configuration).registerConfiguration(deepClone(policyConfigurationNode)); + await promise; + + const acutal = testObject.configurationModel; + assert.strictEqual(acutal.getValue('policy.settingB'), false); + assert.strictEqual(acutal.getValue('policy.settingA'), undefined); + assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); + assert.deepStrictEqual(acutal.keys, ['policy.settingB']); + assert.deepStrictEqual(acutal.overrides, []); + }); + + test('change: when policy setting is deregistered', async () => { + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false }))); + await testObject.initialize(); + + const promise = Event.toPromise(testObject.onDidChangeConfiguration); + Registry.as(Extensions.Configuration).deregisterConfigurations([policyConfigurationNode]); + await promise; + + const acutal = testObject.configurationModel; + assert.strictEqual(acutal.getValue('policy.settingA'), undefined); + assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); + assert.deepStrictEqual(acutal.keys, []); + assert.deepStrictEqual(acutal.overrides, []); + }); + +}); diff --git a/src/vs/platform/policy/common/filePolicyService.ts b/src/vs/platform/policy/common/filePolicyService.ts index 0cfa9e1588c17..4779fe77c0ad0 100644 --- a/src/vs/platform/policy/common/filePolicyService.ts +++ b/src/vs/platform/policy/common/filePolicyService.ts @@ -50,8 +50,8 @@ export class FilePolicyService extends Disposable implements IPolicyService { await this.doRefresh(); } - private refresh(): void { - this.throttledDelayer.trigger(() => this.doRefresh()); + async refresh(): Promise { + await this.throttledDelayer.trigger(() => this.doRefresh()); } private async read(): Promise { diff --git a/src/vs/platform/policy/common/policy.ts b/src/vs/platform/policy/common/policy.ts index 4febe66904fe3..6963bf22081db 100644 --- a/src/vs/platform/policy/common/policy.ts +++ b/src/vs/platform/policy/common/policy.ts @@ -12,12 +12,14 @@ export type Policies = Map; export interface IPolicyService { readonly onDidChange: Event; initialize(): Promise; + refresh(): Promise; getPolicyValue(name: PolicyName): PolicyValue | undefined; } export class NullPolicyService implements IPolicyService { readonly onDidChange = Event.None; async initialize() { } + async refresh() { } getPolicyValue() { return undefined; } } @@ -33,6 +35,10 @@ export class MultiPolicyService implements IPolicyService { await Promise.all(this.policyServices.map(p => p.initialize())); } + async refresh() { + await Promise.all(this.policyServices.map(p => p.refresh())); + } + getPolicyValue(name: PolicyName) { for (const policyService of this.policyServices) { const result = policyService.getPolicyValue(name); diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index 406661573d2c2..17d42a088faeb 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -204,7 +204,8 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => { configuration.properties = {}; } for (let key in properties) { - const message = validateProperty(key); + const propertyConfiguration = properties[key]; + const message = validateProperty(key, propertyConfiguration); if (message) { delete properties[key]; extension.collector.warn(message); @@ -215,7 +216,6 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => { extension.collector.warn(nls.localize('config.property.duplicate', "Cannot register '{0}'. This property is already registered.", key)); continue; } - const propertyConfiguration = properties[key]; if (!isObject(propertyConfiguration)) { delete properties[key]; extension.collector.error(nls.localize('invalid.property', "configuration.properties property '{0}' must be an object", key)); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index a4c1112129062..0db722acbc186 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -342,6 +342,8 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat async reloadConfiguration(target?: ConfigurationTarget | IWorkspaceFolder): Promise { if (target === undefined) { + this.reloadDefaultConfiguration(); + await this.reloadPolicyConfiguration(); const { local, remote } = await this.reloadUserConfiguration(); await this.reloadWorkspaceConfiguration(); await this.loadConfiguration(local, remote); @@ -355,7 +357,8 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat switch (target) { case ConfigurationTarget.DEFAULT: - await this.reloadDefaultConfiguration(); + this.reloadDefaultConfiguration(); + await this.reloadPolicyConfiguration(); return; case ConfigurationTarget.USER: { @@ -596,10 +599,14 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat return { local, remote }; } - private async reloadDefaultConfiguration(): Promise { + private reloadDefaultConfiguration(): void { this.onDefaultConfigurationChanged(this.defaultConfiguration.reload()); } + private async reloadPolicyConfiguration(): Promise { + this.onPolicyConfigurationChanged(await this.policyConfiguration.reload()); + } + private async reloadUserConfiguration(): Promise<{ local: ConfigurationModel; remote: ConfigurationModel }> { const [local, remote] = await Promise.all([this.reloadLocalUserConfiguration(true), this.reloadRemoteUserConfiguration(true)]); return { local, remote }; diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts index 7d7e3cb1267e8..2fba810064f63 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts @@ -37,8 +37,9 @@ import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFil import { joinPath } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteAgentService'; -import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { getSingleFolderWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { hash } from 'vs/base/common/hash'; const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' }); @@ -52,7 +53,7 @@ export class ConfigurationCache implements IConfigurationCache { suite('ConfigurationEditingService', () => { let instantiationService: TestInstantiationService; - let environmentService: BrowserWorkbenchEnvironmentService; + let environmentService: IWorkbenchEnvironmentService; let fileService: IFileService; let workspaceService: WorkspaceService; let testObject: ConfigurationEditingService; @@ -76,6 +77,14 @@ suite('ConfigurationEditingService', () => { 'configurationEditing.service.testSettingThree': { 'type': 'string', 'default': 'isSet' + }, + 'configurationEditing.service.policySetting': { + 'type': 'string', + 'default': 'isSet', + policy: { + name: 'configurationEditing.service.policySetting', + minimumVersion: '1.0.0', + } } } }); @@ -92,12 +101,17 @@ suite('ConfigurationEditingService', () => { instantiationService = workbenchInstantiationService(undefined, disposables); environmentService = TestEnvironmentService; + environmentService.policyFile = joinPath(workspaceFolder, 'policies.json'); instantiationService.stub(IEnvironmentService, environmentService); const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null)); disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService)))); instantiationService.stub(IFileService, fileService); instantiationService.stub(IRemoteAgentService, remoteAgentService); workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + await workspaceService.initialize({ + id: hash(workspaceFolder.toString()).toString(16), + uri: workspaceFolder + }); instantiationService.stub(IWorkspaceContextService, workspaceService); await workspaceService.initialize(getSingleFolderWorkspaceIdentifier(workspaceFolder)); @@ -180,6 +194,25 @@ suite('ConfigurationEditingService', () => { assert.fail('Should fail with ERROR_CONFIGURATION_FILE_DIRTY error.'); }); + test('errors cases - ERROR_POLICY_CONFIGURATION', async () => { + await fileService.writeFile(environmentService.policyFile!, VSBuffer.fromString('{ "configurationEditing.service.policySetting": "policyValue" }')); + await instantiationService.get(IConfigurationService).reloadConfiguration(); + try { + await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.policySetting', value: 'value' }, { donotNotifyError: true }); + } catch (error) { + assert.strictEqual(error.code, ConfigurationEditingErrorCode.ERROR_POLICY_CONFIGURATION); + return; + } + assert.fail('Should fail with ERROR_POLICY_CONFIGURATION'); + }); + + test('write policy setting - when not set', async () => { + await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.policySetting', value: 'value' }, { donotNotifyError: true }); + const contents = await fileService.readFile(environmentService.settingsResource); + const parsed = json.parse(contents.value.toString()); + assert.strictEqual(parsed['configurationEditing.service.policySetting'], 'value'); + }); + test('write one setting - empty file', async () => { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }); const contents = await fileService.readFile(environmentService.settingsResource); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 352fd8e9d7e58..5a207dccd6d7b 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -669,7 +669,7 @@ suite('WorkspaceService - Initialization', () => { suite('WorkspaceConfigurationService - Folder', () => { - let testObject: WorkspaceService, workspaceService: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService; + let testObject: WorkspaceService, workspaceService: WorkspaceService, fileService: IFileService, environmentService: IWorkbenchEnvironmentService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables: DisposableStore = new DisposableStore(); @@ -708,6 +708,14 @@ suite('WorkspaceConfigurationService - Folder', () => { 'default': 'isSet', restricted: true }, + 'configurationService.folder.policySetting': { + 'type': 'string', + 'default': 'isSet', + policy: { + name: 'configurationService.folder.policySetting', + minimumVersion: '1.0.0', + } + }, } }); @@ -731,6 +739,7 @@ suite('WorkspaceConfigurationService - Folder', () => { const instantiationService = workbenchInstantiationService(undefined, disposables); environmentService = TestEnvironmentService; + environmentService.policyFile = joinPath(folder, 'policies.json'); const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); @@ -750,7 +759,7 @@ suite('WorkspaceConfigurationService - Folder', () => { teardown(() => disposables.clear()); test('defaults', () => { - assert.deepStrictEqual(testObject.getValue('configurationService'), { 'folder': { 'applicationSetting': 'isSet', 'machineSetting': 'isSet', 'machineOverridableSetting': 'isSet', 'testSetting': 'isSet', 'languageSetting': 'isSet', 'restrictedSetting': 'isSet' } }); + assert.deepStrictEqual(testObject.getValue('configurationService'), { 'folder': { 'applicationSetting': 'isSet', 'machineSetting': 'isSet', 'machineOverridableSetting': 'isSet', 'testSetting': 'isSet', 'languageSetting': 'isSet', 'restrictedSetting': 'isSet', 'policySetting': 'isSet' } }); }); test('globals override defaults', async () => { @@ -956,6 +965,23 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.strictEqual(testObject.getValue('configurationService.folder.machineSetting-3', { resource: workspaceService.getWorkspace().folders[0].uri }), 'userValue'); }); + test('policy value override all', async () => { + await fileService.writeFile(environmentService.policyFile!, VSBuffer.fromString('{ "configurationService.folder.policySetting": "policyValue" }')); + await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.policySetting": "userValue" }')); + await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.policySetting": "workspaceValue" }')); + await testObject.reloadConfiguration(); + assert.strictEqual(testObject.getValue('configurationService.folder.policySetting'), 'policyValue'); + assert.strictEqual(testObject.inspect('configurationService.folder.policySetting').policyValue, 'policyValue'); + }); + + test('policy settings when policy value is not set', async () => { + await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.policySetting": "userValue" }')); + await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.policySetting": "workspaceValue" }')); + await testObject.reloadConfiguration(); + assert.strictEqual(testObject.getValue('configurationService.folder.policySetting'), 'workspaceValue'); + assert.strictEqual(testObject.inspect('configurationService.folder.policySetting').policyValue, undefined); + }); + test('reload configuration emits events after global configuraiton changes', async () => { await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); const target = sinon.spy(); From 0d291672e501c0f6939910ae8c9e9a118f3fde19 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 11 May 2022 16:30:49 +0200 Subject: [PATCH 037/942] - add test for change event when policy value changes - fix the model. create model always --- src/vs/platform/configuration/common/configurations.ts | 7 ++++++- .../test/browser/configurationService.test.ts | 9 +++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index a9cf1f256f9fb..353ad2070d347 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -140,7 +140,11 @@ export class PolicyConfiguration extends Disposable { } if (changed.length) { - this._configurationModel = this._configurationModel.isFrozen() ? this._configurationModel.clone() : this._configurationModel; + const old = this._configurationModel; + this._configurationModel = new ConfigurationModel(); + for (const key of old.keys) { + this._configurationModel.setValue(key, old.getValue(key)); + } for (const [key, policyValue] of changed) { if (policyValue === undefined) { this._configurationModel.removeValue(key); @@ -154,4 +158,5 @@ export class PolicyConfiguration extends Disposable { } } + } diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 5a207dccd6d7b..7f980fd1d1d45 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -982,6 +982,15 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.strictEqual(testObject.inspect('configurationService.folder.policySetting').policyValue, undefined); }); + test('policy change should trigger change event ', async () => { + const promise = Event.toPromise(testObject.onDidChangeConfiguration); + await fileService.writeFile(environmentService.policyFile!, VSBuffer.fromString('{ "configurationService.folder.policySetting": "policyValue" }')); + const result = await promise; + assert.deepStrictEqual(result.affectedKeys, ['configurationService.folder.policySetting']); + assert.strictEqual(testObject.getValue('configurationService.folder.policySetting'), 'policyValue'); + assert.strictEqual(testObject.inspect('configurationService.folder.policySetting').policyValue, 'policyValue'); + }); + test('reload configuration emits events after global configuraiton changes', async () => { await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); const target = sinon.spy(); From 2752a8237260b3fcf7e329338fa55c38ef2a951e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 11 May 2022 16:34:59 +0200 Subject: [PATCH 038/942] refresh should not be throttled as it is requested explicitly --- src/vs/platform/policy/common/filePolicyService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/policy/common/filePolicyService.ts b/src/vs/platform/policy/common/filePolicyService.ts index 4779fe77c0ad0..1fbec3f40cd84 100644 --- a/src/vs/platform/policy/common/filePolicyService.ts +++ b/src/vs/platform/policy/common/filePolicyService.ts @@ -43,7 +43,7 @@ export class FilePolicyService extends Disposable implements IPolicyService { const onDidChangePolicyFile = Event.filter(fileService.onDidFilesChange, e => e.affects(file)); this._register(fileService.watch(file)); - this._register(onDidChangePolicyFile(this.refresh, this)); + this._register(onDidChangePolicyFile(() => this.throttledDelayer.trigger(() => this.doRefresh()))); } async initialize(): Promise { @@ -51,7 +51,7 @@ export class FilePolicyService extends Disposable implements IPolicyService { } async refresh(): Promise { - await this.throttledDelayer.trigger(() => this.doRefresh()); + await this.doRefresh(); } private async read(): Promise { From 78cabb9419909a5cdd2a219b518e1696dc1e0907 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 11 May 2022 16:50:11 +0200 Subject: [PATCH 039/942] make tests roboust --- .../test/common/policyConfiguration.test.ts | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/vs/platform/configuration/test/common/policyConfiguration.test.ts b/src/vs/platform/configuration/test/common/policyConfiguration.test.ts index 3f6e15bdd0c6a..44a8c34894a2e 100644 --- a/src/vs/platform/configuration/test/common/policyConfiguration.test.ts +++ b/src/vs/platform/configuration/test/common/policyConfiguration.test.ts @@ -38,6 +38,14 @@ suite('PolicyConfiguration', () => { minimumVersion: '1.0.0', } }, + 'policy.settingB': { + 'type': 'boolean', + 'default': true, + policy: { + name: 'PolicySettingB', + minimumVersion: '1.0.0', + } + }, 'nonPolicy.setting': { 'type': 'boolean', 'default': true @@ -66,6 +74,7 @@ suite('PolicyConfiguration', () => { const acutal = testObject.configurationModel; assert.strictEqual(acutal.getValue('policy.settingA'), false); + assert.strictEqual(acutal.getValue('policy.settingB'), undefined); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); assert.deepStrictEqual(acutal.keys, ['policy.settingA']); assert.deepStrictEqual(acutal.overrides, []); @@ -78,32 +87,36 @@ suite('PolicyConfiguration', () => { assert.deepStrictEqual(acutal.keys, []); assert.deepStrictEqual(acutal.overrides, []); assert.strictEqual(acutal.getValue('policy.settingA'), undefined); + assert.strictEqual(acutal.getValue('policy.settingB'), undefined); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); }); test('initialize: with policies but not registered', async () => { - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false, 'PolicySettingB': false }))); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false, 'PolicySettingB': false, 'PolicySettingC': false }))); await testObject.initialize(); const acutal = testObject.configurationModel; assert.strictEqual(acutal.getValue('policy.settingA'), false); + assert.strictEqual(acutal.getValue('policy.settingB'), false); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); - assert.deepStrictEqual(acutal.keys, ['policy.settingA']); + assert.deepStrictEqual(acutal.keys, ['policy.settingA', 'policy.settingB']); assert.deepStrictEqual(acutal.overrides, []); }); test('change: when policy is added', async () => { + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false }))); await testObject.initialize(); const promise = Event.toPromise(testObject.onDidChangeConfiguration); - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false }))); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false, 'PolicySettingB': false, 'PolicySettingC': false }))); await promise; const acutal = testObject.configurationModel; assert.strictEqual(acutal.getValue('policy.settingA'), false); + assert.strictEqual(acutal.getValue('policy.settingB'), false); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); - assert.deepStrictEqual(acutal.keys, ['policy.settingA']); + assert.deepStrictEqual(acutal.keys, ['policy.settingA', 'policy.settingB']); assert.deepStrictEqual(acutal.overrides, []); }); @@ -117,6 +130,7 @@ suite('PolicyConfiguration', () => { const acutal = testObject.configurationModel; assert.strictEqual(acutal.getValue('policy.settingA'), true); + assert.strictEqual(acutal.getValue('policy.settingB'), undefined); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); assert.deepStrictEqual(acutal.keys, ['policy.settingA']); assert.deepStrictEqual(acutal.overrides, []); @@ -132,21 +146,22 @@ suite('PolicyConfiguration', () => { const acutal = testObject.configurationModel; assert.strictEqual(acutal.getValue('policy.settingA'), undefined); + assert.strictEqual(acutal.getValue('policy.settingB'), undefined); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); assert.deepStrictEqual(acutal.keys, []); assert.deepStrictEqual(acutal.overrides, []); }); test('change: when policy setting is registered', async () => { - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingB': false }))); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingC': false }))); await testObject.initialize(); const promise = Event.toPromise(testObject.onDidChangeConfiguration); - policyConfigurationNode.properties!['policy.settingB'] = { + policyConfigurationNode.properties!['policy.settingC'] = { 'type': 'boolean', 'default': true, policy: { - name: 'PolicySettingB', + name: 'PolicySettingC', minimumVersion: '1.0.0', } }; @@ -154,10 +169,11 @@ suite('PolicyConfiguration', () => { await promise; const acutal = testObject.configurationModel; - assert.strictEqual(acutal.getValue('policy.settingB'), false); + assert.strictEqual(acutal.getValue('policy.settingC'), false); assert.strictEqual(acutal.getValue('policy.settingA'), undefined); + assert.strictEqual(acutal.getValue('policy.settingB'), undefined); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); - assert.deepStrictEqual(acutal.keys, ['policy.settingB']); + assert.deepStrictEqual(acutal.keys, ['policy.settingC']); assert.deepStrictEqual(acutal.overrides, []); }); @@ -171,6 +187,7 @@ suite('PolicyConfiguration', () => { const acutal = testObject.configurationModel; assert.strictEqual(acutal.getValue('policy.settingA'), undefined); + assert.strictEqual(acutal.getValue('policy.settingB'), undefined); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); assert.deepStrictEqual(acutal.keys, []); assert.deepStrictEqual(acutal.overrides, []); From 1c559569eabfe84fa303f795066027bd5dd845f6 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 11 May 2022 17:13:47 +0200 Subject: [PATCH 040/942] one -> 1, two -> 2 --- .../mergeEditor/browser/mergeEditor.ts | 4 +- .../mergeEditor/browser/mergeEditorInput.ts | 24 ++++++------ .../mergeEditor/browser/mergeEditorModel.ts | 39 +++++++++++-------- 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 93918c9ae43cd..81b82849c05c8 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -163,8 +163,8 @@ export class MergeEditor extends EditorPane { this._sessionDisposables.clear(); const model = await input.resolve(); - this.inputOneView.setModel(model.inputOne, localize('yours', 'Yours'), undefined); - this.inputTwoView.setModel(model.inputTwo, localize('theirs', 'Theirs',), undefined); + this.inputOneView.setModel(model.input1, localize('yours', 'Yours'), undefined); + this.inputTwoView.setModel(model.input2, localize('theirs', 'Theirs',), undefined); this.inputResultView.setModel(model.result, localize('result', 'Result',), this._labelService.getUriLabel(model.result.uri, { relative: true })); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts index 8943def874155..da2ada49ee0cc 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts @@ -35,8 +35,8 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { constructor( private readonly _anchestor: URI, - private readonly _inputOne: URI, - private readonly _inputTwo: URI, + private readonly _input1: URI, + private readonly _input2: URI, private readonly _result: URI, @IInstantiationService private readonly _instaService: IInstantiationService, @ITextModelService private readonly _textModelService: ITextModelService, @@ -94,21 +94,21 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { if (!this._model) { const anchestor = await this._textModelService.createModelReference(this._anchestor); - const inputOne = await this._textModelService.createModelReference(this._inputOne); - const inputTwo = await this._textModelService.createModelReference(this._inputTwo); + const input1 = await this._textModelService.createModelReference(this._input1); + const input2 = await this._textModelService.createModelReference(this._input2); const result = await this._textModelService.createModelReference(this._result); this._model = await this.mergeEditorModelFactory.create( anchestor.object.textEditorModel, - inputOne.object.textEditorModel, - inputTwo.object.textEditorModel, + input1.object.textEditorModel, + input2.object.textEditorModel, result.object.textEditorModel ); this._store.add(this._model); this._store.add(anchestor); - this._store.add(inputOne); - this._store.add(inputTwo); + this._store.add(input1); + this._store.add(input2); this._store.add(result); // result.object. @@ -121,16 +121,16 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { return false; } return isEqual(this._anchestor, otherInput._anchestor) - && isEqual(this._inputOne, otherInput._inputOne) - && isEqual(this._inputTwo, otherInput._inputTwo) + && isEqual(this._input1, otherInput._input1) + && isEqual(this._input2, otherInput._input2) && isEqual(this._result, otherInput._result); } toJSON(): MergeEditorInputJSON { return { anchestor: this._anchestor, - inputOne: this._inputOne, - inputTwo: this._inputTwo, + inputOne: this._input1, + inputTwo: this._input2, result: this._result, }; } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts index ce2eb6b3afbd5..13fd032a5ea71 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -19,43 +19,43 @@ export class MergeEditorModelFactory { public async create( ancestor: ITextModel, - inputOne: ITextModel, - inputTwo: ITextModel, + input1: ITextModel, + input2: ITextModel, result: ITextModel, ): Promise { - const ancestorToInputOneDiffPromise = this._editorWorkerService.computeDiff( + const ancestorToInput1DiffPromise = this._editorWorkerService.computeDiff( ancestor.uri, - inputOne.uri, + input1.uri, false, 1000 ); - const ancestorToInputTwoDiffPromise = this._editorWorkerService.computeDiff( + const ancestorToInput2DiffPromise = this._editorWorkerService.computeDiff( ancestor.uri, - inputTwo.uri, + input2.uri, false, 1000 ); - const [ancestorToInputOneDiff, ancestorToInputTwoDiff] = await Promise.all([ - ancestorToInputOneDiffPromise, - ancestorToInputTwoDiffPromise, + const [ancestorToInput1Diff, ancestorToInput2Diff] = await Promise.all([ + ancestorToInput1DiffPromise, + ancestorToInput2DiffPromise, ]); const changesInput1 = - ancestorToInputOneDiff?.changes.map((c) => - LineDiff.fromLineChange(c, ancestor, inputOne) + ancestorToInput1Diff?.changes.map((c) => + LineDiff.fromLineChange(c, ancestor, input1) ) || []; const changesInput2 = - ancestorToInputTwoDiff?.changes.map((c) => - LineDiff.fromLineChange(c, ancestor, inputTwo) + ancestorToInput2Diff?.changes.map((c) => + LineDiff.fromLineChange(c, ancestor, input2) ) || []; return new MergeEditorModel( InternalSymbol, ancestor, - inputOne, - inputTwo, + input1, + input2, result, changesInput1, changesInput2, @@ -71,8 +71,8 @@ export class MergeEditorModel extends EditorModel { constructor( symbol: typeof InternalSymbol, readonly ancestor: ITextModel, - readonly inputOne: ITextModel, - readonly inputTwo: ITextModel, + readonly input1: ITextModel, + readonly input2: ITextModel, readonly result: ITextModel, private readonly inputOneLinesDiffs: readonly LineDiff[], private readonly inputTwoLinesDiffs: readonly LineDiff[], @@ -108,6 +108,11 @@ export class MergeEditorModel extends EditorModel { public readonly mergeableDiffs = MergeableDiff.fromDiffs(this.inputOneLinesDiffs, this.inputTwoLinesDiffs); public getState(conflict: MergeableDiff): MergeState | undefined { + const existingDiff = this.resultEdits.findConflictingDiffs(conflict.originalRange); + if (existingDiff) { + //existingDiff. + } + return undefined; } From b2a380b59d788e234de12243955c07f539477971 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Wed, 11 May 2022 09:43:00 -0700 Subject: [PATCH 041/942] Show policy info in Settings editor --- .../browser/media/settingsEditor2.css | 10 ++++- .../preferences/browser/settingsTree.ts | 37 +++++++++++++++++-- .../preferences/browser/settingsTreeModels.ts | 13 +++++-- .../preferences/common/preferences.ts | 1 + .../preferences/common/preferencesModels.ts | 1 + 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 92733560d7bf5..bc6fdef7bc564 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -399,13 +399,15 @@ -webkit-user-select: text; } -.settings-editor > .settings-body .settings-tree-container .setting-item.setting-item-untrusted > .setting-item-contents .setting-item-trust-description { +.settings-editor > .settings-body .settings-tree-container .setting-item.setting-item-untrusted > .setting-item-contents .setting-item-trust-description, +.settings-editor > .settings-body .settings-tree-container .setting-item > .setting-item-contents .setting-item-policy-description { display: flex; font-weight: 600; margin: 6px 0 12px 0; } -.settings-editor > .settings-body .settings-tree-container .setting-item.setting-item-untrusted > .setting-item-contents .setting-item-trust-description > span { +.settings-editor > .settings-body .settings-tree-container .setting-item.setting-item-untrusted > .setting-item-contents .setting-item-trust-description > span, +.settings-editor > .settings-body .settings-tree-container .setting-item > .setting-item-contents .setting-item-policy-description > span { padding-right: 5px; } @@ -413,6 +415,10 @@ color: var(--workspace-trust-state-untrusted-color) !important; } +.settings-editor > .settings-body .settings-tree-container .setting-item > .setting-item-contents .setting-item-policy-description > span.codicon.codicon-lock { + color: var(--organization-policy-icon-color) !important; +} + .settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-validation-message { display: none; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 7b551ab1466fb..40cf05955e4a1 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -584,6 +584,7 @@ interface ISettingItemTemplate extends IDisposableTemplate { containerElement: HTMLElement; categoryElement: HTMLElement; labelElement: SimpleIconLabel; + policyWarningElement: HTMLElement; descriptionElement: HTMLElement; controlElement: HTMLElement; deprecationWarningElement: HTMLElement; @@ -605,6 +606,7 @@ type ISettingNumberItemTemplate = ISettingTextItemTemplate; interface ISettingEnumItemTemplate extends ISettingItemTemplate { selectBox: SelectBox; + selectElement: HTMLSelectElement | null; enumDescriptionElement: HTMLElement; } @@ -781,9 +783,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const categoryElement = DOM.append(labelCategoryContainer, $('span.setting-item-category')); const labelElementContainer = DOM.append(labelCategoryContainer, $('span.setting-item-label')); const labelElement = new SimpleIconLabel(labelElementContainer); - const miscLabel = new SettingsTreeMiscLabel(titleElement); - const descriptionElement = DOM.append(container, $('.setting-item-description')); const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator')); modifiedIndicatorElement.title = localize('modified', "The setting has been configured in the current scope."); @@ -795,6 +795,14 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const toDispose = new DisposableStore(); + const policyWarningElement = DOM.append(container, $('.setting-item-policy-description')); + const policyIcon = DOM.append(policyWarningElement, $('span.codicon.codicon-lock')); + toDispose.add(attachStylerCallback(this._themeService, { editorErrorForeground }, colors => { + policyIcon.style.setProperty('--organization-policy-icon-color', colors.editorErrorForeground?.toString() || ''); + })); + const element = DOM.append(policyWarningElement, $('span')); + element.textContent = localize('policyLabel', "This setting is not configurable due to your organization's policy."); + const toolbarContainer = DOM.append(container, $('.setting-toolbar-container')); const toolbar = this.renderSettingToolbar(toolbarContainer); @@ -805,6 +813,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre containerElement: container, categoryElement, labelElement, + policyWarningElement, descriptionElement, controlElement, deprecationWarningElement, @@ -908,6 +917,8 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre template.miscLabel.updateSyncIgnored(element, this.ignoredSettings); })); + template.policyWarningElement.hidden = !element.setting.hasPolicyValue; + this.updateSettingTabbable(element, template); template.elementDisposables.add(element.onDidChangeTabbable(() => { this.updateSettingTabbable(element, template); @@ -1538,6 +1549,7 @@ abstract class AbstractSettingTextRenderer extends AbstractSettingRenderer imple template.onChange = undefined; template.inputBox.value = dataElement.value; template.inputBox.setAriaLabel(dataElement.setting.key); + template.inputBox.inputElement.disabled = !!dataElement.setting.hasPolicyValue; template.onChange = value => { if (!renderValidations(dataElement, template, false)) { onChange(value); @@ -1633,6 +1645,7 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre const template: ISettingEnumItemTemplate = { ...common, selectBox, + selectElement, enumDescriptionElement }; @@ -1704,6 +1717,10 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre } }; + if (template.selectElement) { + template.selectElement.disabled = !!dataElement.setting.hasPolicyValue; + } + template.enumDescriptionElement.innerText = ''; } } @@ -1757,6 +1774,7 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT template.onChange = undefined; template.inputBox.value = dataElement.value; template.inputBox.setAriaLabel(dataElement.setting.key); + template.inputBox.setEnabled(!dataElement.setting.hasPolicyValue); template.onChange = value => { if (!renderValidations(dataElement, template, false)) { onChange(nullNumParseFn(value)); @@ -1789,7 +1807,6 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator')); modifiedIndicatorElement.title = localize('modified', "The setting has been configured in the current scope."); - const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); const toDispose = new DisposableStore(); @@ -1819,6 +1836,14 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre const toolbar = this.renderSettingToolbar(toolbarContainer); toDispose.add(toolbar); + const policyWarningElement = DOM.append(container, $('.setting-item-policy-description')); + const policyIcon = DOM.append(policyWarningElement, $('span.codicon.codicon-lock')); + toDispose.add(attachStylerCallback(this._themeService, { editorErrorForeground }, colors => { + policyIcon.style.setProperty('--organization-policy-icon-color', colors.editorErrorForeground?.toString() || ''); + })); + const element = DOM.append(policyWarningElement, $('span')); + element.textContent = localize('policyLabel', "This setting is not configurable due to your organization's policy."); + const template: ISettingBoolItemTemplate = { toDispose, elementDisposables: new DisposableStore(), @@ -1828,6 +1853,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre labelElement, controlElement, checkbox, + policyWarningElement, descriptionElement, deprecationWarningElement, miscLabel, @@ -1852,6 +1878,11 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre template.onChange = undefined; template.checkbox.checked = dataElement.value; template.checkbox.setTitle(dataElement.setting.key); + if (dataElement.setting.hasPolicyValue) { + template.checkbox.disable(); + } else { + template.checkbox.enable(); + } template.onChange = onChange; } } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index 6c4169e6b7c87..cdf7ede3a9eda 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -181,7 +181,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { } update(inspectResult: IInspectResult, isWorkspaceTrusted: boolean): void { - const { isConfigured, inspected, targetSelector, inspectedLanguageOverrides, languageSelector } = inspectResult; + let { isConfigured, inspected, targetSelector, inspectedLanguageOverrides, languageSelector } = inspectResult; switch (targetSelector) { case 'workspaceFolderValue': @@ -213,20 +213,25 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { } } - if (languageSelector && this.languageOverrideValues.has(languageSelector)) { + const policyValue = inspected.policyValue; + if (inspected.policyValue) { + isConfigured = false; // The user did not manually configure the setting themselves. + displayValue = policyValue; + this.scopeValue = policyValue; + this.defaultValue = inspected.defaultValue; + } else if (languageSelector && this.languageOverrideValues.has(languageSelector)) { const overrideValues = this.languageOverrideValues.get(languageSelector)!; // In the worst case, go back to using the previous display value. // Also, sometimes the override is in the form of a default value override, so consider that second. displayValue = (isConfigured ? overrideValues[targetSelector] : overrideValues.defaultValue) ?? displayValue; - this.value = displayValue; this.scopeValue = isConfigured && overrideValues[targetSelector]; this.defaultValue = overrideValues.defaultValue ?? inspected.defaultValue; } else { - this.value = displayValue; this.scopeValue = isConfigured && inspected[targetSelector]; this.defaultValue = inspected.defaultValue; } + this.value = displayValue; this.isConfigured = isConfigured; if (isConfigured || this.setting.tags || this.tags || this.setting.restricted) { // Don't create an empty Set for all 1000 settings, only if needed diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index 5aaf1198fe43c..c33637b593409 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -91,6 +91,7 @@ export interface ISetting { isLanguageTagSetting?: boolean; categoryOrder?: number; categoryLabel?: string; + hasPolicyValue?: boolean; } export interface IExtensionSetting extends ISetting { diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 000a74b77ca9a..cd9c8d784ef19 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -741,6 +741,7 @@ export class DefaultSettings extends Disposable { tags: prop.tags, disallowSyncIgnore: prop.disallowSyncIgnore, restricted: prop.restricted, + hasPolicyValue: !!prop.policy, extensionInfo: extensionInfo, deprecationMessage: prop.markdownDeprecationMessage || prop.deprecationMessage, deprecationMessageIsMarkdown: !!prop.markdownDeprecationMessage, From 7e34708dcccfbec7837c825ed85cc470ec20df89 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 11 May 2022 11:46:20 -0700 Subject: [PATCH 042/942] prepare for removing proxy kernel. --- .../api/browser/mainThreadNotebookKernels.ts | 8 ++++- .../browser/mainThreadNotebookProxyKernels.ts | 10 +++++- .../workbench/api/common/extHost.api.impl.ts | 1 + .../workbench/api/common/extHost.protocol.ts | 6 ++++ .../api/common/extHostNotebookKernels.ts | 9 +++++ src/vs/workbench/api/common/extHostTypes.ts | 5 +++ .../editorStatusBar/editorStatusBar.ts | 8 ++--- .../notebook/browser/controller/apiActions.ts | 6 ++-- .../browser/view/cellParts/cellExecution.ts | 3 +- .../view/renderers/backLayerWebView.ts | 12 +++---- .../notebookEditorWidgetContextKeys.ts | 4 +-- .../viewParts/notebookKernelActionViewItem.ts | 34 ++++++++----------- .../notebook/common/notebookKernelService.ts | 13 +++++++ ...code.proposed.notebookProxyController.d.ts | 8 +++++ 14 files changed, 89 insertions(+), 38 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index a8809c58016a4..fc5501b81629e 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -17,7 +17,7 @@ import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/no import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { IResolvedNotebookKernel, INotebookKernelChangeEvent, INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; -import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol'; +import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape, NotebookControllerState } from '../common/extHost.protocol'; abstract class MainThreadKernel implements IResolvedNotebookKernel { readonly type: NotebookKernelType.Resolved = NotebookKernelType.Resolved; @@ -35,6 +35,7 @@ abstract class MainThreadKernel implements IResolvedNotebookKernel { description?: string; detail?: string; kind?: string; + state?: NotebookControllerState; supportedLanguages: string[]; implementsExecutionOrder: boolean; localResourceRoot: URI; @@ -57,6 +58,7 @@ abstract class MainThreadKernel implements IResolvedNotebookKernel { this.description = data.description; this.detail = data.detail; this.kind = data.kind; + this.state = data.state; this.supportedLanguages = isNonEmptyArray(data.supportedLanguages) ? data.supportedLanguages : _languageService.getRegisteredLanguageIds(); this.implementsExecutionOrder = data.supportsExecutionOrder ?? false; this.localResourceRoot = URI.revive(data.extensionLocation); @@ -83,6 +85,10 @@ abstract class MainThreadKernel implements IResolvedNotebookKernel { this.kind = data.kind; event.kind = true; } + if (data.state !== undefined) { + this.state = data.state; + event.state = true; + } if (data.supportedLanguages !== undefined) { this.supportedLanguages = isNonEmptyArray(data.supportedLanguages) ? data.supportedLanguages : this._languageService.getRegisteredLanguageIds(); event.supportedLanguages = true; diff --git a/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts index 74e7290161dcc..e7a83bb863174 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts @@ -7,9 +7,10 @@ import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { INotebookKernelService, INotebookProxyKernel, INotebookProxyKernelChangeEvent, ProxyKernelState, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService, INotebookProxyKernel, INotebookProxyKernelChangeEvent, ProxyKernelState, NotebookKernelType, NotebookControllerState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { ExtHostContext, ExtHostNotebookProxyKernelsShape, INotebookProxyKernelDto, MainContext, MainThreadNotebookProxyKernelsShape } from '../common/extHost.protocol'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { URI } from 'vs/base/common/uri'; abstract class MainThreadProxyKernel implements INotebookProxyKernel { readonly type: NotebookKernelType.Proxy = NotebookKernelType.Proxy; @@ -19,11 +20,16 @@ abstract class MainThreadProxyKernel implements INotebookProxyKernel { readonly viewType: string; readonly extension: ExtensionIdentifier; readonly preloadProvides: string[] = []; + readonly preloadUris: URI[] = []; label: string; description?: string; detail?: string; kind?: string; supportedLanguages: string[] = []; + localResourceRoot: URI; + state?: NotebookControllerState | undefined; + implementsInterrupt?: boolean | undefined; + implementsExecutionOrder?: boolean | undefined; connectionState: ProxyKernelState; constructor(data: INotebookProxyKernelDto) { @@ -35,10 +41,12 @@ abstract class MainThreadProxyKernel implements INotebookProxyKernel { this.description = data.description; this.detail = data.detail; this.kind = data.kind; + this.localResourceRoot = URI.revive(data.extensionLocation); this.connectionState = ProxyKernelState.Disconnected; } + update(data: Partial) { const event: INotebookProxyKernelChangeEvent = Object.create(null); if (data.label !== undefined) { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index bba2d04f64eaf..86cb018e40414 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1320,6 +1320,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem, NotebookCellStatusBarItem: extHostTypes.NotebookCellStatusBarItem, NotebookControllerAffinity: extHostTypes.NotebookControllerAffinity, + NotebookControllerState: extHostTypes.NotebookControllerState, PortAttributes: extHostTypes.PortAttributes, LinkedEditingRanges: extHostTypes.LinkedEditingRanges, TestResultState: extHostTypes.TestResultState, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9fa08b4b6c31b..25f33cb3f4a5a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -970,6 +970,11 @@ export interface MainThreadNotebookDocumentsShape extends IDisposable { $trySaveNotebook(uri: UriComponents): Promise; } +export enum NotebookControllerState { + Idle = 1, + Connecting = 2 +} + export interface INotebookKernelDto2 { id: string; notebookType: string; @@ -979,6 +984,7 @@ export interface INotebookKernelDto2 { detail?: string; description?: string; kind?: string; + state?: NotebookControllerState; supportedLanguages?: string[]; supportsInterrupt?: boolean; supportsExecutionOrder?: boolean; diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index f99ead59ac7d7..4e4ca9640c5a9 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -196,6 +196,15 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { get rendererScripts() { return data.preloads ? data.preloads.map(extHostTypeConverters.NotebookRendererScript.to) : []; }, + get state() { + checkProposedApiEnabled(extension, 'notebookProxyController'); + return data.state; + }, + set state(value) { + checkProposedApiEnabled(extension, 'notebookProxyController'); + data.state = value; + _update(); + }, get executeHandler() { return _executeHandler; }, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 977f0cdf1423b..10844646688f4 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3369,6 +3369,11 @@ export class NotebookRendererScript { } } +export enum NotebookControllerState { + Idle = 1, + Connecting = 2 +} + //#endregion //#region Timeline diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index 920669194f665..22233100d5e3e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -28,7 +28,7 @@ import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/note import { configureKernelIcon, selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernel, INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService, NotebookControllerState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; @@ -205,8 +205,8 @@ registerAction2(class extends Action2 { }); } - if (!all.find(item => item.type === NotebookKernelType.Resolved)) { - // there is no resolved kernel, show the install from marketplace + if (!all.length) { + // there is no kernel, show the install from marketplace quickPickItems.push({ id: 'install', label: nls.localize('installKernels', "Install kernels from the marketplace"), @@ -379,7 +379,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { this._kernelInfoElement.add(this._statusbarService.addEntry( { name: nls.localize('notebook.info', "Notebook Kernel Info"), - text: `$(notebook-kernel-select) ${kernel.label}`, + text: `$(notebook-kernel-select) ${kernel.label}` + (kernel.state === NotebookControllerState.Idle ? '' : ' Connecting...'), ariaLabel: kernel.label, tooltip: isSuggested ? nls.localize('tooltop', "{0} (suggestion)", tooltip) : tooltip, command: SELECT_KERNEL_ID, diff --git a/src/vs/workbench/contrib/notebook/browser/controller/apiActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/apiActions.ts index 68d9d0f8dccc9..d0c6379a54ba1 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/apiActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/apiActions.ts @@ -7,7 +7,7 @@ import * as glob from 'vs/base/common/glob'; import { URI, UriComponents } from 'vs/base/common/uri'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { isDocumentExcludePattern, TransientCellMetadata, TransientDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernelService, IResolvedNotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; CommandsRegistry.registerCommand('_resolveNotebookContentProvider', (accessor): { @@ -66,13 +66,13 @@ CommandsRegistry.registerCommand('_resolveNotebookKernels', async (accessor, arg const uri = URI.revive(args.uri as UriComponents); const kernels = notebookKernelService.getMatchingKernel({ uri, viewType: args.viewType }); - return kernels.all.filter(kernel => kernel.type === NotebookKernelType.Resolved).map((provider) => ({ + return kernels.all.map(provider => ({ id: provider.id, label: provider.label, kind: provider.kind, description: provider.description, detail: provider.detail, isPreferred: false, // todo@jrieken,@rebornix - preloads: (provider as IResolvedNotebookKernel).preloadUris, + preloads: provider.preloadUris, })); }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts index 469e7cbef3d95..1d9865937ea2a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts @@ -9,7 +9,6 @@ import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/no import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; import { NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; export class CellExecutionPart extends CellPart { private kernelDisposables = this._register(new DisposableStore()); @@ -42,7 +41,7 @@ export class CellExecutionPart extends CellPart { } private updateExecutionOrder(internalMetadata: NotebookCellInternalMetadata): void { - if (this._notebookEditor.activeKernel?.type === NotebookKernelType.Resolved && this._notebookEditor.activeKernel?.implementsExecutionOrder) { + if (this._notebookEditor.activeKernel?.implementsExecutionOrder) { const executionOrderLabel = typeof internalMetadata.executionOrder === 'number' ? `[${internalMetadata.executionOrder}]` : '[ ]'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index cc2eb8bf93a2f..1d82b850c6d00 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -37,7 +37,7 @@ import { preloadsScriptStr, RendererMetadata } from 'vs/workbench/contrib/notebo import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { CellUri, INotebookRendererInfo, NotebookSetting, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernel, IResolvedNotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IScopedRendererMessaging } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IWebviewElement, IWebviewService, WebviewContentPurpose } from 'vs/workbench/contrib/webview/browser/webview'; @@ -907,7 +907,7 @@ var requirejs = (function() { } this._preloadsCache.clear(); - if (this._currentKernel?.type === NotebookKernelType.Resolved) { + if (this._currentKernel) { this._updatePreloadsFromKernel(this._currentKernel); } @@ -1407,14 +1407,14 @@ var requirejs = (function() { const previousKernel = this._currentKernel; this._currentKernel = kernel; - if (previousKernel?.type === NotebookKernelType.Resolved && previousKernel.preloadUris.length > 0) { + if (previousKernel && previousKernel.preloadUris.length > 0) { this.webview?.reload(); // preloads will be restored after reload - } else if (kernel?.type === NotebookKernelType.Resolved) { + } else if (kernel) { this._updatePreloadsFromKernel(kernel); } } - private _updatePreloadsFromKernel(kernel: IResolvedNotebookKernel) { + private _updatePreloadsFromKernel(kernel: INotebookKernel) { const resources: IControllerPreload[] = []; for (const preload of kernel.preloadUris) { const uri = this.environmentService.isExtensionDevelopment && (preload.scheme === 'http' || preload.scheme === 'https') @@ -1440,7 +1440,7 @@ var requirejs = (function() { const mixedResourceRoots = [ ...(this.localResourceRootsCache || []), - ...(this._currentKernel?.type === NotebookKernelType.Resolved ? [this._currentKernel.localResourceRoot] : []), + ...(this._currentKernel ? [this._currentKernel.localResourceRoot] : []), ]; this.webview.localResourcesRoot = mixedResourceRoots; diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts index b1cf70a4946c2..a90477db542a2 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts @@ -8,7 +8,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { ICellViewModel, INotebookEditorDelegate, KERNEL_EXTENSIONS } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NOTEBOOK_CELL_TOOLBAR_LOCATION, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_HAS_RUNNING_CELL, NOTEBOOK_INTERRUPTIBLE_KERNEL, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_MISSING_KERNEL_EXTENSION, NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON, NOTEBOOK_VIEW_TYPE } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; export class NotebookEditorContextKeys { @@ -148,7 +148,7 @@ export class NotebookEditorContextKeys { const { selected, all } = this._notebookKernelService.getMatchingKernel(this._editor.textModel); this._notebookKernelCount.set(all.length); - this._interruptibleKernel.set((selected?.type === NotebookKernelType.Resolved && selected.implementsInterrupt) ?? false); + this._interruptibleKernel.set(selected?.implementsInterrupt ?? false); this._notebookKernelSelected.set(Boolean(selected)); this._notebookKernel.set(selected?.id ?? ''); } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts index e5814ce98ec26..69917ca972b6f 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts @@ -10,7 +10,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; -import { INotebookKernelMatchResult, INotebookKernelService, NotebookKernelType, ProxyKernelState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelMatchResult, INotebookKernelService, NotebookControllerState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { Event } from 'vs/base/common/event'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -68,32 +68,20 @@ export class NotebooKernelActionViewItem extends ActionViewItem { private _updateActionFromKernelInfo(info: INotebookKernelMatchResult): void { this._kernelDisposable.clear(); this._action.enabled = true; - const selectedOrSuggested = info.selected ?? ((info.suggestions.length === 1 && info.suggestions[0].type === NotebookKernelType.Resolved) ? info.suggestions[0] : undefined); + const selectedOrSuggested = info.selected ?? (info.suggestions.length === 1 ? info.suggestions[0] : undefined); if (selectedOrSuggested) { // selected or suggested kernel - this._action.label = selectedOrSuggested.label; + this._action.label = this._generateKenrelLabel(selectedOrSuggested); this._action.tooltip = selectedOrSuggested.description ?? selectedOrSuggested.detail ?? ''; if (!info.selected) { // special UI for selected kernel? } - if (selectedOrSuggested.type === NotebookKernelType.Proxy) { - if (selectedOrSuggested.connectionState === ProxyKernelState.Initializing) { - this._action.label = localize('initializing', "Initializing..."); - } else { - this._action.label = selectedOrSuggested.label; + this._kernelDisposable.add(selectedOrSuggested.onDidChange(e => { + if (e.state) { + this._action.label = this._generateKenrelLabel(selectedOrSuggested); } - - this._kernelDisposable.add(selectedOrSuggested.onDidChange(e => { - if (e.connectionState) { - if (selectedOrSuggested.connectionState === ProxyKernelState.Initializing) { - this._action.label = localize('initializing', "Initializing..."); - } else { - this._action.label = selectedOrSuggested.label; - } - } - })); - } + })); } else { // many kernels or no kernels this._action.label = localize('select', "Select Kernel"); @@ -101,6 +89,14 @@ export class NotebooKernelActionViewItem extends ActionViewItem { } } + private _generateKenrelLabel(kernel: INotebookKernel) { + if (kernel.state === NotebookControllerState.Connecting) { + return localize('kernelconnecting', "Connecting..."); + } else { + return kernel.label; + } + } + private _resetAction(): void { this._action.enabled = false; this._action.label = ''; diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index 4f5304600b07d..7a23d36ecd5a9 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -27,6 +27,7 @@ export interface INotebookKernelChangeEvent { description?: true; detail?: true; kind?: true; + state?: true; supportedLanguages?: true; hasExecutionOrder?: true; } @@ -36,6 +37,11 @@ export const enum NotebookKernelType { Proxy = 1 } +export enum NotebookControllerState { + Idle = 1, + Connecting = 2 +} + export interface IResolvedNotebookKernel { readonly type: NotebookKernelType.Resolved; readonly id: string; @@ -51,6 +57,7 @@ export interface IResolvedNotebookKernel { description?: string; detail?: string; kind?: string; + state?: NotebookControllerState; supportedLanguages: string[]; implementsInterrupt?: boolean; implementsExecutionOrder?: boolean; @@ -74,13 +81,19 @@ export interface INotebookProxyKernel { readonly id: string; readonly viewType: string; readonly extension: ExtensionIdentifier; + readonly localResourceRoot: URI; + readonly preloadUris: URI[]; readonly preloadProvides: string[]; + readonly onDidChange: Event>; label: string; description?: string; detail?: string; kind?: string; + state?: NotebookControllerState; supportedLanguages: string[]; + implementsInterrupt?: boolean; + implementsExecutionOrder?: boolean; connectionState: ProxyKernelState; resolveKernel(uri: URI): Promise; } diff --git a/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts b/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts index 07f8e833f15f9..3fe7cac5428df 100644 --- a/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts @@ -5,6 +5,14 @@ declare module 'vscode' { + export enum NotebookControllerState { + Idle = 1, + Connecting = 2 + } + + export interface NotebookController { + state?: NotebookControllerState; + } export interface NotebookProxyController { /** From 3982c0ed0b9db59dad3129516bf450a923aacf97 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 12 May 2022 12:28:08 +0200 Subject: [PATCH 043/942] - change policy file location next to argv.json - introduce __enable-file-policy` flag to enable file based policies --- src/vs/platform/environment/common/argv.ts | 2 +- .../environment/common/environmentService.ts | 12 +++++++++++- src/vs/platform/environment/node/argv.ts | 2 +- src/vs/platform/policy/common/filePolicyService.ts | 6 ++++-- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 3834247b45b18..1c903e2d25f54 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -88,7 +88,7 @@ export interface NativeParsedArgs { 'sync'?: 'on' | 'off'; '__sandbox'?: boolean; 'logsPath'?: string; - 'policy-file'?: string; + '__enable-file-policy'?: boolean; // chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches 'no-proxy-server'?: boolean; diff --git a/src/vs/platform/environment/common/environmentService.ts b/src/vs/platform/environment/common/environmentService.ts index d3214f7036f21..1d7d75afbb983 100644 --- a/src/vs/platform/environment/common/environmentService.ts +++ b/src/vs/platform/environment/common/environmentService.ts @@ -238,7 +238,17 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron get disableWorkspaceTrust(): boolean { return !!this.args['disable-workspace-trust']; } @memoize - get policyFile(): URI | undefined { return this.args['policy-file'] ? URI.file(this.args['policy-file']) : undefined; } + get policyFile(): URI | undefined { + if (this.args['__enable-file-policy']) { + const vscodePortable = env['VSCODE_PORTABLE']; + if (vscodePortable) { + return URI.file(join(vscodePortable, 'policy.json')); + } + + return joinPath(this.userHome, this.productService.dataFolderName, 'policy.json'); + } + return undefined; + } get args(): NativeParsedArgs { return this._args; } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 0a69e9cd2c461..239aaaf704517 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -73,7 +73,6 @@ export const OPTIONS: OptionDescriptions> = { 'disable-extensions': { type: 'boolean', deprecates: ['disableExtensions'], cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") }, 'disable-extension': { type: 'string[]', cat: 't', args: 'ext-id', description: localize('disableExtension', "Disable an extension.") }, 'sync': { type: 'string', cat: 't', description: localize('turn sync', "Turn sync on or off."), args: ['on | off'] }, - 'policy-file': { type: 'string', cat: 't', description: localize('policyFile', "Path of the file defining policies") }, 'inspect-extensions': { type: 'string', deprecates: ['debugPluginHost'], args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") }, 'inspect-brk-extensions': { type: 'string', deprecates: ['debugBrkPluginHost'], args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") }, @@ -126,6 +125,7 @@ export const OPTIONS: OptionDescriptions> = { 'open-devtools': { type: 'boolean' }, '__sandbox': { type: 'boolean' }, 'logsPath': { type: 'string' }, + '__enable-file-policy': { type: 'boolean' }, // chromium flags 'no-proxy-server': { type: 'boolean' }, diff --git a/src/vs/platform/policy/common/filePolicyService.ts b/src/vs/platform/policy/common/filePolicyService.ts index 1fbec3f40cd84..44d3105dad3f0 100644 --- a/src/vs/platform/policy/common/filePolicyService.ts +++ b/src/vs/platform/policy/common/filePolicyService.ts @@ -9,7 +9,7 @@ import { Iterable } from 'vs/base/common/iterator'; import { Disposable } from 'vs/base/common/lifecycle'; import { isObject } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { IFileService } from 'vs/platform/files/common/files'; +import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { IPolicyService, Policies, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; @@ -69,7 +69,9 @@ export class FilePolicyService extends Disposable implements IPolicyService { policies.set(key, raw[key]); } } catch (error) { - this.logService.error(`[FilePolicyService] Failed to read policies`, error); + if ((error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) { + this.logService.error(`[FilePolicyService] Failed to read policies`, error); + } } return policies; From 281d6155e5b03cba5523b83fe504ee9f8635039b Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Thu, 12 May 2022 17:15:10 -0700 Subject: [PATCH 044/942] Tweak UI and fix hasPolicy detection --- .../browser/media/settingsEditor2.css | 4 ++++ .../preferences/browser/settingsTree.ts | 24 +++++++++---------- .../preferences/browser/settingsTreeModels.ts | 11 ++++++--- .../preferences/common/preferences.ts | 1 - .../preferences/common/preferencesModels.ts | 1 - 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index bc6fdef7bc564..7b4684b296196 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -406,6 +406,10 @@ margin: 6px 0 12px 0; } +.settings-editor > .settings-body .settings-tree-container .setting-item > .setting-item-contents .setting-item-policy-description[hidden] { + display: none; +} + .settings-editor > .settings-body .settings-tree-container .setting-item.setting-item-untrusted > .setting-item-contents .setting-item-trust-description > span, .settings-editor > .settings-body .settings-tree-container .setting-item > .setting-item-contents .setting-item-policy-description > span { padding-right: 5px; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 40cf05955e4a1..673de4bf5aa4d 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -36,7 +36,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { editorBackground, editorErrorForeground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { editorBackground, editorErrorForeground, editorInfoForeground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; @@ -797,11 +797,11 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const policyWarningElement = DOM.append(container, $('.setting-item-policy-description')); const policyIcon = DOM.append(policyWarningElement, $('span.codicon.codicon-lock')); - toDispose.add(attachStylerCallback(this._themeService, { editorErrorForeground }, colors => { - policyIcon.style.setProperty('--organization-policy-icon-color', colors.editorErrorForeground?.toString() || ''); + toDispose.add(attachStylerCallback(this._themeService, { editorInfoForeground }, colors => { + policyIcon.style.setProperty('--organization-policy-icon-color', colors.editorInfoForeground?.toString() || ''); })); const element = DOM.append(policyWarningElement, $('span')); - element.textContent = localize('policyLabel', "This setting is not configurable due to your organization's policy."); + element.textContent = localize('policyLabel', "This setting is managed by your organization."); const toolbarContainer = DOM.append(container, $('.setting-toolbar-container')); const toolbar = this.renderSettingToolbar(toolbarContainer); @@ -917,7 +917,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre template.miscLabel.updateSyncIgnored(element, this.ignoredSettings); })); - template.policyWarningElement.hidden = !element.setting.hasPolicyValue; + template.policyWarningElement.hidden = !element.hasPolicyValue; this.updateSettingTabbable(element, template); template.elementDisposables.add(element.onDidChangeTabbable(() => { @@ -1549,7 +1549,7 @@ abstract class AbstractSettingTextRenderer extends AbstractSettingRenderer imple template.onChange = undefined; template.inputBox.value = dataElement.value; template.inputBox.setAriaLabel(dataElement.setting.key); - template.inputBox.inputElement.disabled = !!dataElement.setting.hasPolicyValue; + template.inputBox.inputElement.disabled = !!dataElement.hasPolicyValue; template.onChange = value => { if (!renderValidations(dataElement, template, false)) { onChange(value); @@ -1718,7 +1718,7 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre }; if (template.selectElement) { - template.selectElement.disabled = !!dataElement.setting.hasPolicyValue; + template.selectElement.disabled = !!dataElement.hasPolicyValue; } template.enumDescriptionElement.innerText = ''; @@ -1774,7 +1774,7 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT template.onChange = undefined; template.inputBox.value = dataElement.value; template.inputBox.setAriaLabel(dataElement.setting.key); - template.inputBox.setEnabled(!dataElement.setting.hasPolicyValue); + template.inputBox.setEnabled(!dataElement.hasPolicyValue); template.onChange = value => { if (!renderValidations(dataElement, template, false)) { onChange(nullNumParseFn(value)); @@ -1838,11 +1838,11 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre const policyWarningElement = DOM.append(container, $('.setting-item-policy-description')); const policyIcon = DOM.append(policyWarningElement, $('span.codicon.codicon-lock')); - toDispose.add(attachStylerCallback(this._themeService, { editorErrorForeground }, colors => { - policyIcon.style.setProperty('--organization-policy-icon-color', colors.editorErrorForeground?.toString() || ''); + toDispose.add(attachStylerCallback(this._themeService, { editorInfoForeground }, colors => { + policyIcon.style.setProperty('--organization-policy-icon-color', colors.editorInfoForeground?.toString() || ''); })); const element = DOM.append(policyWarningElement, $('span')); - element.textContent = localize('policyLabel', "This setting is not configurable due to your organization's policy."); + element.textContent = localize('policyLabel', "This setting is managed by your organization."); const template: ISettingBoolItemTemplate = { toDispose, @@ -1878,7 +1878,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre template.onChange = undefined; template.checkbox.checked = dataElement.value; template.checkbox.setTitle(dataElement.setting.key); - if (dataElement.setting.hasPolicyValue) { + if (dataElement.hasPolicyValue) { template.checkbox.disable(); } else { template.checkbox.enable(); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index cdf7ede3a9eda..6e0c5b34b4334 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -138,6 +138,11 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { */ isUntrusted = false; + /** + * Whether the setting is under a policy that blocks all changes. + */ + hasPolicyValue = false; + tags?: Set; overriddenScopeList: string[] = []; languageOverrideValues: Map> = new Map>(); @@ -213,11 +218,11 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { } } - const policyValue = inspected.policyValue; if (inspected.policyValue) { + this.hasPolicyValue = true; isConfigured = false; // The user did not manually configure the setting themselves. - displayValue = policyValue; - this.scopeValue = policyValue; + displayValue = inspected.policyValue; + this.scopeValue = inspected.policyValue; this.defaultValue = inspected.defaultValue; } else if (languageSelector && this.languageOverrideValues.has(languageSelector)) { const overrideValues = this.languageOverrideValues.get(languageSelector)!; diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index c33637b593409..5aaf1198fe43c 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -91,7 +91,6 @@ export interface ISetting { isLanguageTagSetting?: boolean; categoryOrder?: number; categoryLabel?: string; - hasPolicyValue?: boolean; } export interface IExtensionSetting extends ISetting { diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index cd9c8d784ef19..000a74b77ca9a 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -741,7 +741,6 @@ export class DefaultSettings extends Disposable { tags: prop.tags, disallowSyncIgnore: prop.disallowSyncIgnore, restricted: prop.restricted, - hasPolicyValue: !!prop.policy, extensionInfo: extensionInfo, deprecationMessage: prop.markdownDeprecationMessage || prop.deprecationMessage, deprecationMessageIsMarkdown: !!prop.markdownDeprecationMessage, From e00030977e6eb14422594c0fa475eb133d4cbc39 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 13 May 2022 10:19:59 +0200 Subject: [PATCH 045/942] Implements glyph buttons, alignment and decorations. --- .../browser/breakpointEditorContribution.ts | 2 +- .../contrib/mergeEditor/browser/icons.ts | 10 + .../mergeEditor/browser/media/mergeEditor.css | 31 ++- .../mergeEditor/browser/mergeEditor.ts | 221 +++++++++++++++--- .../mergeEditor/browser/mergeEditorModel.ts | 47 +++- .../contrib/mergeEditor/browser/model.ts | 128 ++++++++-- 6 files changed, 375 insertions(+), 64 deletions(-) create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/icons.ts diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 4eab6791a7f23..a5bc41d7cb095 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -433,7 +433,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi if (decorations) { for (const { options } of decorations) { const clz = options.glyphMarginClassName; - if (clz && (!clz.includes('codicon-') || clz.includes('codicon-testing-'))) { + if (clz && (!clz.includes('codicon-') || clz.includes('codicon-testing-') || clz.includes('codicon-merge-'))) { return false; } } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/icons.ts b/src/vs/workbench/contrib/mergeEditor/browser/icons.ts new file mode 100644 index 0000000000000..a37f0e66faf87 --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/icons.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { localize } from 'vs/nls'; +import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; + +export const acceptConflictIcon = registerIcon('merge-accept-conflict-icon', Codicon.check, localize('acceptConflictIcon', 'TODO.')); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css index 2a1dd749cbb69..749b947af5530 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css +++ b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css @@ -3,13 +3,40 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .merge-editor .code-view>.title { +.monaco-workbench .merge-editor .code-view > .title { padding: 0 0 0 10px; height: 30px; display: flex; align-content: center; } -.monaco-workbench .merge-editor .code-view>.title .monaco-icon-label { +.monaco-workbench .merge-editor .code-view > .title .monaco-icon-label { margin: auto 0; } + +.monaco-workbench .merge-editor .code-view > .container { + display: flex; + flex-direction: row; +} + +.monaco-workbench .merge-editor .code-view > .container > .gutter { + min-width: 20px; + /* background-color: yellow; */ + margin-right: 3px; + position: relative; +} + +.monaco-editor .merge-accept-conflict-glyph { + cursor: pointer; +} + +.merge-accept-foo { + background-color: rgb(170 175 170 / 15%); +} + +.merge-accept-gutter-marker { + min-height: 30px; + min-width: 20px; + background-color: yellow; + position: absolute; +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 81b82849c05c8..c846f45c87f2f 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/mergeEditor'; -import { Dimension, reset } from 'vs/base/browser/dom'; +import { $, Dimension, reset } from 'vs/base/browser/dom'; import { Emitter } from 'vs/base/common/event'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; @@ -19,7 +19,7 @@ import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/merge import { DisposableStore } from 'vs/base/common/lifecycle'; import { Direction, Grid, IView, IViewSize, SerializableGrid } from 'vs/base/browser/ui/grid/grid'; import { Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview'; -import { ITextModel } from 'vs/editor/common/model'; +import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ScrollType } from 'vs/editor/common/editorCommon'; @@ -30,10 +30,15 @@ import { localize } from 'vs/nls'; import { ILabelService } from 'vs/platform/label/common/label'; import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IAction } from 'vs/base/common/actions'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { acceptConflictIcon } from 'vs/workbench/contrib/mergeEditor/browser/icons'; +import { ConflictGroup, MergeState } from 'vs/workbench/contrib/mergeEditor/browser/model'; + export const ctxIsMergeEditor = new RawContextKey('isMergeEditor', false); export const ctxUsesColumnLayout = new RawContextKey('mergeEditorUsesColumnLayout', false); @@ -46,8 +51,8 @@ export class MergeEditor extends EditorPane { private _grid!: Grid; - private readonly inputOneView = this.instantiation.createInstance(CodeEditorView, { readonly: true }); - private readonly inputTwoView = this.instantiation.createInstance(CodeEditorView, { readonly: true }); + private readonly input1View = this.instantiation.createInstance(CodeEditorView, { readonly: true }); + private readonly input2View = this.instantiation.createInstance(CodeEditorView, { readonly: true }); private readonly inputResultView = this.instantiation.createInstance(CodeEditorView, { readonly: false }); private readonly _ctxIsMergeEditor: IContextKey; @@ -68,18 +73,18 @@ export class MergeEditor extends EditorPane { this._ctxUsesColumnLayout = ctxUsesColumnLayout.bindTo(_contextKeyService); const reentrancyBarrier = new ReentrancyBarrier(); - this._store.add(this.inputOneView.editor.onDidScrollChange(c => { + this._store.add(this.input1View.editor.onDidScrollChange(c => { if (c.scrollTopChanged) { reentrancyBarrier.runExclusively(() => { - this.inputTwoView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + this.input2View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); this.inputResultView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); }); } })); - this._store.add(this.inputTwoView.editor.onDidScrollChange(c => { + this._store.add(this.input2View.editor.onDidScrollChange(c => { if (c.scrollTopChanged) { reentrancyBarrier.runExclusively(() => { - this.inputOneView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); this.inputResultView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); }); } @@ -87,8 +92,8 @@ export class MergeEditor extends EditorPane { this._store.add(this.inputResultView.editor.onDidScrollChange(c => { if (c.scrollTopChanged) { reentrancyBarrier.runExclusively(() => { - this.inputOneView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); - this.inputTwoView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + this.input2View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); }); } })); @@ -131,9 +136,9 @@ export class MergeEditor extends EditorPane { { size: 38, groups: [{ - data: this.inputOneView + data: this.input1View }, { - data: this.inputTwoView + data: this.input2View }] }, { @@ -163,9 +168,122 @@ export class MergeEditor extends EditorPane { this._sessionDisposables.clear(); const model = await input.resolve(); - this.inputOneView.setModel(model.input1, localize('yours', 'Yours'), undefined); - this.inputTwoView.setModel(model.input2, localize('theirs', 'Theirs',), undefined); + this.input1View.setModel(model.input1, localize('yours', 'Yours'), undefined); + this.input2View.setModel(model.input2, localize('theirs', 'Theirs',), undefined); this.inputResultView.setModel(model.result, localize('result', 'Result',), this._labelService.getUriLabel(model.result.uri, { relative: true })); + + + const input1LineNumberToMergeableDiffMap = new Map(); + + this.input1View.editor.onMouseDown(e => { + if (e.target.element && e.target.element.className.includes('merge-')) { + const lineNumber = e.target.position?.lineNumber; + if (lineNumber) { + const diff = input1LineNumberToMergeableDiffMap.get(lineNumber); + if (diff) { + const state = (model.getState(diff) || new MergeState(false, false, false)).toggleInput1(); + model.setConflictResolutionStatus(diff, state); + } + } + } + }); + + this.input1View.editor.changeDecorations(c => { + for (const mergeableDiff of model.mergeableDiffs) { + if (mergeableDiff.input1FullDiff) { + const lineNumber = mergeableDiff.input1FullDiff.modifiedRange.startLineNumber; + input1LineNumberToMergeableDiffMap.set(lineNumber, mergeableDiff); + c.addDecoration(Range.fromPositions(new Position(lineNumber, 0)), { + description: 'foo', + glyphMarginClassName: ThemeIcon.asClassName(acceptConflictIcon) + ' merge-accept-conflict-glyph', + }); + } + } + }); + + + + const input2LineNumberToMergeableDiffMap = new Map(); + + this.input2View.editor.onMouseDown(e => { + if (e.target.element && e.target.element.className.includes('merge-')) { + const lineNumber = e.target.position?.lineNumber; + if (lineNumber) { + const diff = input2LineNumberToMergeableDiffMap.get(lineNumber); + if (diff) { + const state = (model.getState(diff) || new MergeState(false, false, false)).toggleInput2(); + model.setConflictResolutionStatus(diff, state); + } + } + } + }); + + + + this.input2View.editor.changeDecorations(c => { + for (const mergeableDiff of model.mergeableDiffs) { + if (mergeableDiff.input2FullDiff) { + const lineNumber = mergeableDiff.input2FullDiff.modifiedRange.startLineNumber; + input2LineNumberToMergeableDiffMap.set(lineNumber, mergeableDiff); + c.addDecoration(Range.fromPositions(new Position(lineNumber, 1)), { + description: 'foo', + glyphMarginClassName: ThemeIcon.asClassName(acceptConflictIcon) + ' merge-accept-conflict-glyph', + }); + } + } + }); + + + let input1Decorations = new Array(); + let input2Decorations = new Array(); + + + for (const m of model.mergeableDiffs) { + + if (!m.totalInput1Range.isEmpty) { + input1Decorations.push({ + range: new Range(m.totalInput1Range.startLineNumber, 1, m.totalInput1Range.endLineNumberExclusive - 1, 1), + options: { + isWholeLine: true, + className: 'merge-accept-foo', + description: 'foo2' + } + }); + } + + if (!m.totalInput2Range.isEmpty) { + input2Decorations.push({ + range: new Range(m.totalInput2Range.startLineNumber, 1, m.totalInput2Range.endLineNumberExclusive - 1, 1), + options: { + isWholeLine: true, + className: 'merge-accept-foo', + description: 'foo2' + } + }); + } + + const max = Math.max(m.totalInput1Range.lineCount, m.totalInput2Range.lineCount, 1); + + this.input1View.editor.changeViewZones(a => { + a.addZone({ + afterLineNumber: m.totalInput1Range.endLineNumberExclusive - 1, + heightInLines: max - m.totalInput1Range.lineCount, + domNode: $('div.diagonal-fill'), + }); + }); + + this.input2View.editor.changeViewZones(a => { + a.addZone({ + afterLineNumber: m.totalInput2Range.endLineNumberExclusive - 1, + heightInLines: max - m.totalInput2Range.lineCount, + domNode: $('div.diagonal-fill'), + }); + }); + } + + this.input1View.editor.deltaDecorations([], input1Decorations); + this.input2View.editor.deltaDecorations([], input2Decorations); + } protected override setEditorVisible(visible: boolean): void { @@ -175,7 +293,7 @@ export class MergeEditor extends EditorPane { // ---- interact with "outside world" via `getControl`, `scopedContextKeyService` override getControl(): IEditorControl | undefined { - for (const view of [this.inputOneView, this.inputTwoView, this.inputResultView]) { + for (const view of [this.input1View, this.input2View, this.inputResultView]) { if (view.editor.hasWidgetFocus()) { return view.editor; } @@ -196,10 +314,10 @@ export class MergeEditor extends EditorPane { toggleLayout(): void { if (!this._usesColumnLayout) { - this._grid.moveView(this.inputResultView, Sizing.Distribute, this.inputOneView, Direction.Right); + this._grid.moveView(this.inputResultView, Sizing.Distribute, this.input1View, Direction.Right); } else { - this._grid.moveView(this.inputResultView, this._grid.height * .62, this.inputOneView, Direction.Down); - this._grid.moveView(this.inputTwoView, Sizing.Distribute, this.inputOneView, Direction.Right); + this._grid.moveView(this.inputResultView, this._grid.height * .62, this.input1View, Direction.Down); + this._grid.moveView(this.input2View, Sizing.Distribute, this.input1View, Direction.Right); } this._usesColumnLayout = !this._usesColumnLayout; this._ctxUsesColumnLayout.set(this._usesColumnLayout); @@ -215,9 +333,10 @@ class CodeEditorView implements IView { // preferredWidth?: number | undefined; // preferredHeight?: number | undefined; - element: HTMLElement = document.createElement('div'); - private _titleElement = document.createElement('div'); - private _editorElement = document.createElement('div'); + element: HTMLElement; + private _titleElement: HTMLElement; + private _editorElement: HTMLElement; + private _gutterDiv: HTMLElement; minimumWidth: number = 10; maximumWidth: number = Number.MAX_SAFE_INTEGER; @@ -229,24 +348,36 @@ class CodeEditorView implements IView { private readonly _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; - private _title = new IconLabel(this._titleElement, { supportIcons: true }); + private _title: IconLabel; - public readonly editor = this.instantiationService.createInstance( - CodeEditorWidget, - this._editorElement, - { minimap: { enabled: false }, readOnly: this._options.readonly }, - {} - ); + public readonly editor: CodeEditorWidget; + private readonly gutter: EditorGutter; constructor( private readonly _options: ICodeEditorViewOptions, @IInstantiationService private readonly instantiationService: IInstantiationService ) { - this.element.classList.add('code-view'); - this._titleElement.classList.add('title'); - this.element.appendChild(this._titleElement); - this.element.appendChild(this._editorElement); + this.element = $( + 'div.code-view', + {}, + this._titleElement = $('div.title'), + $('div.container', {}, + this._gutterDiv = $('div.gutter'), + this._editorElement = $('div'), + ), + ); + + this.editor = this.instantiationService.createInstance( + CodeEditorWidget, + this._editorElement, + { minimap: { enabled: false }, readOnly: this._options.readonly, /*glyphMargin: false,*/ lineNumbersMinChars: 2 }, + { contributions: [] } + ); + + this._title = new IconLabel(this._titleElement, { supportIcons: true }); + this.gutter = new EditorGutter(this.editor, this._gutterDiv); + console.log(this.gutter); // keep alive } public setModel(model: ITextModel, title: string, description: string | undefined): void { @@ -259,7 +390,7 @@ class CodeEditorView implements IView { this.element.style.height = `${height}px`; this.element.style.top = `${top}px`; this.element.style.left = `${left}px`; - this.editor.layout({ width, height: height - this._titleElement.clientHeight }); + this.editor.layout({ width: width - this._gutterDiv.clientWidth, height: height - this._titleElement.clientHeight }); } } @@ -278,3 +409,23 @@ class ReentrancyBarrier { } } } + +class EditorGutter { + constructor( + private readonly _editor: ICodeEditor, + private readonly _domNode: HTMLElement, + ) { + this._editor.onDidScrollChange(() => { + this.render(); + }); + } + + private node = $('div.merge-accept-gutter-marker'); + + private render(): void { + this._domNode.append(this.node); + + const top = this._editor.getTopForLineNumber(10) - this._editor.getScrollTop(); + this.node.style.top = `${top}px`; + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts index 13fd032a5ea71..081869931c31a 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -8,7 +8,7 @@ import { BugIndicatingError } from 'vs/base/common/errors'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; -import { MergeableDiff, LineEdit, LineEdits, LineDiff, MergeState, LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { ConflictGroup, LineEdit, LineEdits, LineDiff, MergeState, LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; export class MergeEditorModelFactory { constructor( @@ -75,7 +75,7 @@ export class MergeEditorModel extends EditorModel { readonly input2: ITextModel, readonly result: ITextModel, private readonly inputOneLinesDiffs: readonly LineDiff[], - private readonly inputTwoLinesDiffs: readonly LineDiff[], + private readonly inputTwoLinesDiffs: readonly LineDiff[] ) { super(); @@ -105,25 +105,52 @@ export class MergeEditorModel extends EditorModel { return this.resultEdits.diffs; } - public readonly mergeableDiffs = MergeableDiff.fromDiffs(this.inputOneLinesDiffs, this.inputTwoLinesDiffs); + public readonly mergeableDiffs = ConflictGroup.partitionDiffs( + this.ancestor, + this.input1, + this.inputOneLinesDiffs, + this.input2, + this.inputTwoLinesDiffs + ); + + public getState(conflict: ConflictGroup): MergeState | undefined { + const existingDiff = this.resultEdits.findConflictingDiffs( + conflict.totalOriginalRange + ); + if (!existingDiff) { + return new MergeState(false, false, false); + } - public getState(conflict: MergeableDiff): MergeState | undefined { - const existingDiff = this.resultEdits.findConflictingDiffs(conflict.originalRange); - if (existingDiff) { - //existingDiff. + const input1Edit = conflict.getInput1LineEdit(); + if (input1Edit && existingDiff.getLineEdit().equals(input1Edit)) { + return new MergeState(true, false, false); + } + + const input2Edit = conflict.getInput2LineEdit(); + if (input2Edit && existingDiff.getLineEdit().equals(input2Edit)) { + return new MergeState(false, true, false); } return undefined; } // Undo all edits of result that conflict with the conflict!! - public setConflictResolutionStatus(conflict: MergeableDiff, status: MergeState): void { - const existingDiff = this.resultEdits.findConflictingDiffs(conflict.originalRange); + public setConflictResolutionStatus( + conflict: ConflictGroup, + status: MergeState + ): void { + const existingDiff = this.resultEdits.findConflictingDiffs( + conflict.totalOriginalRange + ); if (existingDiff) { this.resultEdits.removeDiff(existingDiff); } - const edit = status.input1 ? conflict.getInput1LineEdit() : conflict.getInput2LineEdit(); + const edit = status.input1 + ? conflict.getInput1LineEdit() + : status.input2 + ? conflict.getInput2LineEdit() + : undefined; if (edit) { this.resultEdits.applyEditRelativeToOriginal(edit); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model.ts b/src/vs/workbench/contrib/mergeEditor/browser/model.ts index c26686a046698..deae7ace505e6 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ArrayQueue, compareBy, numberComparator } from 'vs/base/common/arrays'; +import { ArrayQueue, compareBy, equals, numberComparator } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; import { Range } from 'vs/editor/common/core/range'; import { ILineChange } from 'vs/editor/common/diff/diffComputer'; @@ -19,7 +19,7 @@ export class LineEdits { null, this.edits.map((e) => ({ range: new Range(e.range.startLineNumber, 1, e.range.endLineNumberExclusive, 1), - text: e.newLines.map(l => l + '\n').join(), + text: e.newLines.map(l => l + '\n').join(''), })), () => null ); @@ -27,6 +27,10 @@ export class LineEdits { } export class LineEdit { + equals(other: LineEdit) { + return this.range.equals(other.range) && equals(this.newLines, other.newLines); + } + constructor( public readonly range: LineRange, public readonly newLines: string[], @@ -34,11 +38,18 @@ export class LineEdit { ) { } } -export class MergeableDiff { - public static fromDiffs( +export class ConflictGroup { + /** + * diffs1 and diffs2 together with the conflict relation form a bipartite graph. + * This method computes strongly connected components of that graph while maintaining the side of each diff. + */ + public static partitionDiffs( + originalTextModel: ITextModel, + input1TextModel: ITextModel, diffs1: readonly LineDiff[], + input2TextModel: ITextModel, diffs2: readonly LineDiff[] - ): MergeableDiff[] { + ): ConflictGroup[] { const compareByStartLineNumber = compareBy( (d) => d.originalRange.startLineNumber, numberComparator @@ -51,7 +62,7 @@ export class MergeableDiff { diffs2.slice().sort(compareByStartLineNumber) ); - const result = new Array(); + const result = new Array(); while (true) { const lastDiff1 = queueDiffs1.peekLast(); @@ -79,7 +90,15 @@ export class MergeableDiff { moreConflictingWith.push(lastDiff1); result.push( - new MergeableDiff(moreConflictingWith, otherConflictingWith) + new ConflictGroup( + originalTextModel, + input1TextModel, + moreConflictingWith, + queueDiffs1.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, + input2TextModel, + otherConflictingWith, + queueDiffs2.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, + ) ); } else if (lastDiff2) { queueDiffs2.removeLast(); @@ -98,7 +117,15 @@ export class MergeableDiff { moreConflictingWith.push(lastDiff2); result.push( - new MergeableDiff(otherConflictingWith, moreConflictingWith) + new ConflictGroup( + originalTextModel, + input1TextModel, + otherConflictingWith, + queueDiffs1.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, + input2TextModel, + moreConflictingWith, + queueDiffs2.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, + ) ); } else { break; @@ -110,16 +137,49 @@ export class MergeableDiff { return result; } - public readonly originalRange = LineRange.hull( - this.input1Diffs - .map((d) => d.originalRange) - .concat(this.input2Diffs.map((d) => d.originalRange)) - )!; + public readonly input1FullDiff = LineDiff.hull(this.input1Diffs); + public readonly input2FullDiff = LineDiff.hull(this.input2Diffs); + + public readonly totalOriginalRange: LineRange; + public readonly totalInput1Range: LineRange; + public readonly totalInput2Range: LineRange; constructor( + public readonly originalTextModel: ITextModel, + public readonly input1TextModel: ITextModel, public readonly input1Diffs: readonly LineDiff[], - public readonly input2Diffs: readonly LineDiff[] - ) { } + public readonly input1DeltaLineCount: number, + public readonly input2TextModel: ITextModel, + public readonly input2Diffs: readonly LineDiff[], + public readonly input2DeltaLineCount: number, + ) { + if (this.input1Diffs.length === 0 && this.input2Diffs.length === 0) { + throw new BugIndicatingError('must have at least one diff'); + } + + const input1Diff = + this.input1FullDiff || + new LineDiff( + originalTextModel, + this.input2FullDiff!.originalRange, + input1TextModel, + this.input2FullDiff!.originalRange.delta(input1DeltaLineCount) + ); + + const input2Diff = + this.input2FullDiff || + new LineDiff( + originalTextModel, + this.input1FullDiff!.originalRange, + input1TextModel, + this.input1FullDiff!.originalRange.delta(input2DeltaLineCount) + ); + + const results = LineDiff.alignOriginalRegion([input1Diff, input2Diff]); + this.totalOriginalRange = results[0].originalRange; + this.totalInput1Range = results[0].modifiedRange; + this.totalInput2Range = results[1].modifiedRange; + } public get isConflicting(): boolean { return this.input1Diffs.length > 0 && this.input2Diffs.length > 0; @@ -198,6 +258,10 @@ export class LineRange { public toString() { return `[${this.startLineNumber},${this.endLineNumberExclusive})`; } + + public equals(originalRange: LineRange) { + return this.startLineNumber === originalRange.startLineNumber && this.lineCount === originalRange.lineCount; + } } export class LineDiff { @@ -226,7 +290,7 @@ export class LineDiff { ); } - public static hull(lineDiffs: LineDiff[]): LineDiff | undefined { + public static hull(lineDiffs: readonly LineDiff[]): LineDiff | undefined { if (lineDiffs.length === 0) { return undefined; } @@ -239,6 +303,26 @@ export class LineDiff { ); } + public static alignOriginalRegion(lineDiffs: readonly LineDiff[]): LineDiff[] { + if (lineDiffs.length === 0) { + return []; + } + const originalRange = LineRange.hull(lineDiffs.map((d) => d.originalRange))!; + return lineDiffs.map(l => { + const startDelta = originalRange.startLineNumber - l.originalRange.startLineNumber; + const endDelta = originalRange.endLineNumberExclusive - l.originalRange.endLineNumberExclusive; + return new LineDiff( + l.originalTextModel, + originalRange, + l.modifiedTextModel, + new LineRange( + l.modifiedRange.startLineNumber + startDelta, + l.modifiedRange.lineCount - startDelta + endDelta + ) + ); + }); + } + constructor( public readonly originalTextModel: ITextModel, public readonly originalRange: LineRange, @@ -247,6 +331,10 @@ export class LineDiff { ) { } + public get resultingDeltaFromOriginalToModified(): number { + return this.modifiedRange.endLineNumberExclusive - this.originalRange.endLineNumberExclusive; + } + private ensureSameOriginalModel(other: LineDiff): void { if (this.originalTextModel !== other.originalTextModel) { // Both changes must refer to the same original model @@ -321,6 +409,14 @@ export class MergeState { ); } + public toggleInput1(): MergeState { + return this.withInput1(!this.input1); + } + + public toggleInput2(): MergeState { + return this.withInput2(!this.input2); + } + public get isEmpty(): boolean { return !this.input1 && !this.input2; } From 7a3fb4b936458c3dd341e9a51e9bc9ae517e1817 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 13 May 2022 11:27:59 -0700 Subject: [PATCH 046/942] Add a high contrast border around simple find widget Fixes #149493 --- .../contrib/codeEditor/browser/find/simpleFindWidget.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index ff59944159843..5fa3bb659ab0c 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -15,7 +15,7 @@ import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBo import { SimpleButton, findPreviousMatchIcon, findNextMatchIcon, NLS_NO_RESULTS, NLS_MATCHES_LOCATION } from 'vs/editor/contrib/find/browser/findWidget'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground, errorForeground, toolbarHoverBackground, toolbarHoverOutline } from 'vs/platform/theme/common/colorRegistry'; +import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground, errorForeground, toolbarHoverBackground, toolbarHoverOutline, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput } from 'vs/platform/history/browser/contextScopedHistoryWidget'; import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; @@ -359,6 +359,11 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 0 8px 2px ${widgetShadowColor}; }`); } + const hcBorder = theme.getColor(contrastBorder); + if (hcBorder) { + collector.addRule(`.monaco-workbench .simple-find-part { border: 1px solid ${hcBorder}; }`); + } + const error = theme.getColor(errorForeground); if (error) { collector.addRule(`.no-results.matchesCount { color: ${error}; }`); From d3637bb80429e8a4d0a3b42902a147b321528ef3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 13 May 2022 13:06:02 -0700 Subject: [PATCH 047/942] Update xterm for contrast ratio luminance change Fixes #149495 --- package.json | 6 ++-- remote/package.json | 6 ++-- remote/web/package.json | 4 +-- remote/web/yarn.lock | 18 ++++++------ remote/yarn.lock | 28 +++++++++---------- .../terminal/common/terminalColorRegistry.ts | 3 +- yarn.lock | 28 +++++++++---------- 7 files changed, 46 insertions(+), 47 deletions(-) diff --git a/package.json b/package.json index acc64a5ce02a6..1422323d7b0b5 100644 --- a/package.json +++ b/package.json @@ -84,12 +84,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.41", + "xterm": "4.19.0-beta.43", "xterm-addon-search": "0.9.0-beta.35", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.33", - "xterm-headless": "4.19.0-beta.41", + "xterm-addon-webgl": "0.12.0-beta.34", + "xterm-headless": "4.19.0-beta.43", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 2d7ace1fd9168..80e835671cebb 100644 --- a/remote/package.json +++ b/remote/package.json @@ -24,12 +24,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.41", + "xterm": "4.19.0-beta.43", "xterm-addon-search": "0.9.0-beta.35", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.33", - "xterm-headless": "4.19.0-beta.41", + "xterm-addon-webgl": "0.12.0-beta.34", + "xterm-headless": "4.19.0-beta.43", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index 4a477cd72d710..cdbbf667adfef 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -10,9 +10,9 @@ "tas-client-umd": "0.1.5", "vscode-oniguruma": "1.6.1", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.41", + "xterm": "4.19.0-beta.43", "xterm-addon-search": "0.9.0-beta.35", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.33" + "xterm-addon-webgl": "0.12.0-beta.34" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index a852b0f91e9ea..8316dc85a0e3a 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -123,12 +123,12 @@ xterm-addon-unicode11@0.4.0-beta.3: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.33: - version "0.12.0-beta.33" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.33.tgz#cb539db9e41f06087b692f0f42491a73bc4bd013" - integrity sha512-seOm06exR36U0/EvR/CUNGuy99RAndoyWEdXg6S16rgEZ4G2Yj9iov/QdCtc4gwq9hFzVETFPlDW+Ge8xeHIzA== - -xterm@4.19.0-beta.41: - version "4.19.0-beta.41" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.41.tgz#acb6009028898e9cfac41d4aa2865f81f6f56c5f" - integrity sha512-WY1NuxF/yUVN3l0TgzQGjrGM26eOu5g0Dbfam8GCkgdK5yrsgPF0xwM7UEj8sDjp5FbxEkSm//X86IIsgzqqFw== +xterm-addon-webgl@0.12.0-beta.34: + version "0.12.0-beta.34" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.34.tgz#51cac31cc7a78377be5d481b624ee82948360de1" + integrity sha512-TTIwun+a45oDN54sHhdUxsEx6VflgF2p9YGqS5+gVzpvPrEqP6GoDr6XFCDsZcSqi0ZT2FNGAKWlh7XSxsKQQw== + +xterm@4.19.0-beta.43: + version "4.19.0-beta.43" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.43.tgz#f2113c8ce303d22c5cfad1a4a119b81c103285fa" + integrity sha512-eQ3fzkUApGdl4/rrhzK4OIdMb3+qO0c2iCZIMbeP9SqqDltZnhWncz+3lGa0tnxKizVoUV9kmGaP7orsQ/IavQ== diff --git a/remote/yarn.lock b/remote/yarn.lock index 968449c718483..a6201c6d4338d 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -929,20 +929,20 @@ xterm-addon-unicode11@0.4.0-beta.3: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.33: - version "0.12.0-beta.33" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.33.tgz#cb539db9e41f06087b692f0f42491a73bc4bd013" - integrity sha512-seOm06exR36U0/EvR/CUNGuy99RAndoyWEdXg6S16rgEZ4G2Yj9iov/QdCtc4gwq9hFzVETFPlDW+Ge8xeHIzA== - -xterm-headless@4.19.0-beta.41: - version "4.19.0-beta.41" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.41.tgz#f495ff173c7952aafa0c785acf15f20942c6fdc7" - integrity sha512-j09IFsM4tBSpjgY5OQSB1llojwEGyFFxgD36MYXZtopmB8p9+0l5GFq5hYfJojGfHCNaB/RwWAexGUxBK2ABRA== - -xterm@4.19.0-beta.41: - version "4.19.0-beta.41" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.41.tgz#acb6009028898e9cfac41d4aa2865f81f6f56c5f" - integrity sha512-WY1NuxF/yUVN3l0TgzQGjrGM26eOu5g0Dbfam8GCkgdK5yrsgPF0xwM7UEj8sDjp5FbxEkSm//X86IIsgzqqFw== +xterm-addon-webgl@0.12.0-beta.34: + version "0.12.0-beta.34" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.34.tgz#51cac31cc7a78377be5d481b624ee82948360de1" + integrity sha512-TTIwun+a45oDN54sHhdUxsEx6VflgF2p9YGqS5+gVzpvPrEqP6GoDr6XFCDsZcSqi0ZT2FNGAKWlh7XSxsKQQw== + +xterm-headless@4.19.0-beta.43: + version "4.19.0-beta.43" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.43.tgz#12fe4abe624265240a7de8a922bfc4fd28c5f92a" + integrity sha512-4T8TlWy5u+sS23aPtd8gBHJ0BVljbNQRPMFHzLigDNOMCwc4uWa9JsxYmKteKifcG5aMm11ALPUTxWZCgpATww== + +xterm@4.19.0-beta.43: + version "4.19.0-beta.43" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.43.tgz#f2113c8ce303d22c5cfad1a4a119b81c103285fa" + integrity sha512-eQ3fzkUApGdl4/rrhzK4OIdMb3+qO0c2iCZIMbeP9SqqDltZnhWncz+3lGa0tnxKizVoUV9kmGaP7orsQ/IavQ== yallist@^4.0.0: version "4.0.0" diff --git a/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts b/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts index 9390d7ccdd7ca..bdfbe2ad4d06e 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts @@ -26,8 +26,7 @@ export const TERMINAL_CURSOR_BACKGROUND_COLOR = registerColor('terminalCursor.ba export const TERMINAL_SELECTION_BACKGROUND_COLOR = registerColor('terminal.selectionBackground', { light: editorSelectionBackground, dark: editorSelectionBackground, - // TODO: hcDark should be editorSelectionBackground (white) when min contrast ratio moves luminance the other way when needed https://github.com/xtermjs/xterm.js/issues/3720 - hcDark: '#FFFFFF80', + hcDark: editorSelectionBackground, hcLight: editorSelectionBackground }, nls.localize('terminal.selectionBackground', 'The selection background color of the terminal.')); export const TERMINAL_COMMAND_DECORATION_DEFAULT_BACKGROUND_COLOR = registerColor('terminalCommandDecoration.defaultBackground', { diff --git a/yarn.lock b/yarn.lock index 8b7a2c516e613..c4c6630ffe387 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12209,20 +12209,20 @@ xterm-addon-unicode11@0.4.0-beta.3: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.33: - version "0.12.0-beta.33" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.33.tgz#cb539db9e41f06087b692f0f42491a73bc4bd013" - integrity sha512-seOm06exR36U0/EvR/CUNGuy99RAndoyWEdXg6S16rgEZ4G2Yj9iov/QdCtc4gwq9hFzVETFPlDW+Ge8xeHIzA== - -xterm-headless@4.19.0-beta.41: - version "4.19.0-beta.41" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.41.tgz#f495ff173c7952aafa0c785acf15f20942c6fdc7" - integrity sha512-j09IFsM4tBSpjgY5OQSB1llojwEGyFFxgD36MYXZtopmB8p9+0l5GFq5hYfJojGfHCNaB/RwWAexGUxBK2ABRA== - -xterm@4.19.0-beta.41: - version "4.19.0-beta.41" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.41.tgz#acb6009028898e9cfac41d4aa2865f81f6f56c5f" - integrity sha512-WY1NuxF/yUVN3l0TgzQGjrGM26eOu5g0Dbfam8GCkgdK5yrsgPF0xwM7UEj8sDjp5FbxEkSm//X86IIsgzqqFw== +xterm-addon-webgl@0.12.0-beta.34: + version "0.12.0-beta.34" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.34.tgz#51cac31cc7a78377be5d481b624ee82948360de1" + integrity sha512-TTIwun+a45oDN54sHhdUxsEx6VflgF2p9YGqS5+gVzpvPrEqP6GoDr6XFCDsZcSqi0ZT2FNGAKWlh7XSxsKQQw== + +xterm-headless@4.19.0-beta.43: + version "4.19.0-beta.43" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.43.tgz#12fe4abe624265240a7de8a922bfc4fd28c5f92a" + integrity sha512-4T8TlWy5u+sS23aPtd8gBHJ0BVljbNQRPMFHzLigDNOMCwc4uWa9JsxYmKteKifcG5aMm11ALPUTxWZCgpATww== + +xterm@4.19.0-beta.43: + version "4.19.0-beta.43" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.43.tgz#f2113c8ce303d22c5cfad1a4a119b81c103285fa" + integrity sha512-eQ3fzkUApGdl4/rrhzK4OIdMb3+qO0c2iCZIMbeP9SqqDltZnhWncz+3lGa0tnxKizVoUV9kmGaP7orsQ/IavQ== y18n@^3.2.1: version "3.2.2" From 027b7bf21e056d25e50365fa0fa87f93a47c1d7f Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 15 May 2022 18:24:06 -0700 Subject: [PATCH 048/942] Notebook state only. --- .../api/browser/mainThreadNotebookKernels.ts | 9 +++-- .../browser/mainThreadNotebookProxyKernels.ts | 16 +++++++++ .../browser/notebookExecutionServiceImpl.ts | 36 +++++++------------ .../notebook/common/notebookKernelService.ts | 4 +++ 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index fc5501b81629e..35ffe7bb7ec63 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -25,7 +25,8 @@ abstract class MainThreadKernel implements IResolvedNotebookKernel { private readonly _onDidChange = new Emitter(); private readonly preloads: { uri: URI; provides: string[] }[]; readonly onDidChange: Event = this._onDidChange.event; - + private readonly _onDispose = new Emitter(); + readonly onDispose = this._onDispose.event; readonly id: string; readonly viewType: string; readonly extension: ExtensionIdentifier; @@ -100,6 +101,10 @@ abstract class MainThreadKernel implements IResolvedNotebookKernel { this._onDidChange.fire(event); } + dispose() { + this._onDispose.fire(); + } + abstract executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; abstract cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; } @@ -225,7 +230,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape }); const registration = this._notebookKernelService.registerKernel(kernel); - this._kernels.set(handle, [kernel, combinedDisposable(listener, registration)]); + this._kernels.set(handle, [kernel, combinedDisposable(kernel, listener, registration)]); } $updateKernel(handle: number, data: Partial): void { diff --git a/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts index e7a83bb863174..8d13aa86f72e6 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts @@ -16,6 +16,8 @@ abstract class MainThreadProxyKernel implements INotebookProxyKernel { readonly type: NotebookKernelType.Proxy = NotebookKernelType.Proxy; protected readonly _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; + private readonly _onDispose = new Emitter(); + readonly onDispose = this._onDispose.event; readonly id: string; readonly viewType: string; readonly extension: ExtensionIdentifier; @@ -69,7 +71,13 @@ abstract class MainThreadProxyKernel implements INotebookProxyKernel { this._onDidChange.fire(event); } + dispose() { + this._onDispose.fire(); + } + abstract resolveKernel(): Promise; + abstract executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; + abstract cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; } @extHostNamedCustomer(MainContext.MainThreadNotebookProxyKernels) @@ -115,6 +123,8 @@ export class MainThreadNotebookProxyKernels implements MainThreadNotebookProxyKe return null; } } + async executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise { } + async cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise { } }(data); const registration = this._notebookKernelService.registerKernel(proxyKernel); @@ -135,4 +145,10 @@ export class MainThreadNotebookProxyKernels implements MainThreadNotebookProxyKe this._proxyKernels.delete(handle); } } + + async executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise { + } + + async cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise { + } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts index 2d4fe4cebe028..732b53f22e474 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts @@ -48,29 +48,6 @@ export class NotebookExecutionService implements INotebookExecutionService, IDis return; } - if (kernel.type === NotebookKernelType.Proxy) { - this._activeProxyKernelExecutionToken?.dispose(true); - const tokenSource = this._activeProxyKernelExecutionToken = new CancellationTokenSource(); - const resolved = await kernel.resolveKernel(notebook.uri); - const kernels = this._notebookKernelService.getMatchingKernel(notebook); - const newlyMatchedKernel = kernels.all.find(k => k.id === resolved); - - if (!newlyMatchedKernel) { - return; - } - - kernel = newlyMatchedKernel; - if (tokenSource.token.isCancellationRequested) { - // execution was cancelled but we still need to update the active kernel - this._notebookKernelService.selectKernelForNotebook(kernel, notebook); - return; - } - } - - if (kernel.type === NotebookKernelType.Proxy) { - return; - } - const executeCells: NotebookCellTextModel[] = []; for (const cell of cellsArr) { const cellExe = this._notebookExecutionStateService.getCellExecution(cell.uri); @@ -83,11 +60,24 @@ export class NotebookExecutionService implements INotebookExecutionService, IDis executeCells.push(cell); } + if (kernel.state !== undefined) { + // kernel has connection state management + const kernelId = kernel.id; + kernel.onDispose(() => { + // proxy kernel scenario, kernel disposed, we should now make way for new preferred kernel + const exes = executeCells.map(c => this._notebookExecutionStateService.createCellExecution(kernelId, notebook.uri, c.handle)); + exes.forEach(e => e.complete({})); + + this.executeNotebookCells(notebook, executeCells); + }); + } + if (executeCells.length > 0) { this._notebookKernelService.selectKernelForNotebook(kernel, notebook); const exes = executeCells.map(c => this._notebookExecutionStateService.createCellExecution(kernel!.id, notebook.uri, c.handle)); await kernel.executeNotebookCellsRequest(notebook.uri, executeCells.map(c => c.handle)); + // the connecting state can change before the kernel resolves executeNotebookCellsRequest const unconfirmed = exes.filter(exe => exe.state === NotebookCellExecutionState.Unconfirmed); if (unconfirmed.length) { this._logService.debug(`NotebookExecutionService#executeNotebookCells completing unconfirmed executions ${JSON.stringify(unconfirmed.map(exe => exe.cellHandle))}`); diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index 7a23d36ecd5a9..081eab0180ec5 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -64,6 +64,7 @@ export interface IResolvedNotebookKernel { executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; + onDispose: Event; } export const enum ProxyKernelState { @@ -96,6 +97,9 @@ export interface INotebookProxyKernel { implementsExecutionOrder?: boolean; connectionState: ProxyKernelState; resolveKernel(uri: URI): Promise; + executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; + cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; + onDispose: Event; } export type INotebookKernel = IResolvedNotebookKernel | INotebookProxyKernel; From bd99c998a14e34dc17a3a745bb60f00a13391564 Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 15 May 2022 18:56:22 -0700 Subject: [PATCH 049/942] Remove proxy controller. --- .../api/browser/mainThreadNotebookKernels.ts | 6 +- .../browser/mainThreadNotebookProxyKernels.ts | 154 ----------------- .../workbench/api/common/extHost.api.impl.ts | 6 - .../api/common/extHostNotebookProxyKernels.ts | 157 ------------------ .../browser/notebookExecutionServiceImpl.ts | 8 +- .../notebook/common/notebookKernelService.ts | 35 +--- .../browser/notebookExecutionService.test.ts | 11 +- .../notebookExecutionStateService.test.ts | 6 +- .../browser/notebookKernelService.test.ts | 7 +- ...code.proposed.notebookProxyController.d.ts | 48 ------ 10 files changed, 19 insertions(+), 419 deletions(-) delete mode 100644 src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts delete mode 100644 src/vs/workbench/api/common/extHostNotebookProxyKernels.ts diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index 35ffe7bb7ec63..38c81e4ba0e6d 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -15,13 +15,11 @@ import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/ext import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService'; import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { IResolvedNotebookKernel, INotebookKernelChangeEvent, INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelChangeEvent, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape, NotebookControllerState } from '../common/extHost.protocol'; -abstract class MainThreadKernel implements IResolvedNotebookKernel { - readonly type: NotebookKernelType.Resolved = NotebookKernelType.Resolved; - +abstract class MainThreadKernel implements INotebookKernel { private readonly _onDidChange = new Emitter(); private readonly preloads: { uri: URI; provides: string[] }[]; readonly onDidChange: Event = this._onDidChange.event; diff --git a/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts deleted file mode 100644 index 8d13aa86f72e6..0000000000000 --- a/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts +++ /dev/null @@ -1,154 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Emitter, Event } from 'vs/base/common/event'; -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { INotebookKernelService, INotebookProxyKernel, INotebookProxyKernelChangeEvent, ProxyKernelState, NotebookKernelType, NotebookControllerState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; -import { ExtHostContext, ExtHostNotebookProxyKernelsShape, INotebookProxyKernelDto, MainContext, MainThreadNotebookProxyKernelsShape } from '../common/extHost.protocol'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { URI } from 'vs/base/common/uri'; - -abstract class MainThreadProxyKernel implements INotebookProxyKernel { - readonly type: NotebookKernelType.Proxy = NotebookKernelType.Proxy; - protected readonly _onDidChange = new Emitter(); - readonly onDidChange: Event = this._onDidChange.event; - private readonly _onDispose = new Emitter(); - readonly onDispose = this._onDispose.event; - readonly id: string; - readonly viewType: string; - readonly extension: ExtensionIdentifier; - readonly preloadProvides: string[] = []; - readonly preloadUris: URI[] = []; - label: string; - description?: string; - detail?: string; - kind?: string; - supportedLanguages: string[] = []; - localResourceRoot: URI; - state?: NotebookControllerState | undefined; - implementsInterrupt?: boolean | undefined; - implementsExecutionOrder?: boolean | undefined; - connectionState: ProxyKernelState; - - constructor(data: INotebookProxyKernelDto) { - this.id = data.id; - this.viewType = data.notebookType; - this.extension = data.extensionId; - - this.label = data.label; - this.description = data.description; - this.detail = data.detail; - this.kind = data.kind; - this.localResourceRoot = URI.revive(data.extensionLocation); - - this.connectionState = ProxyKernelState.Disconnected; - } - - - update(data: Partial) { - const event: INotebookProxyKernelChangeEvent = Object.create(null); - if (data.label !== undefined) { - this.label = data.label; - event.label = true; - } - if (data.description !== undefined) { - this.description = data.description; - event.description = true; - } - if (data.detail !== undefined) { - this.detail = data.detail; - event.detail = true; - } - if (data.kind !== undefined) { - this.kind = data.kind; - event.kind = true; - } - - this._onDidChange.fire(event); - } - - dispose() { - this._onDispose.fire(); - } - - abstract resolveKernel(): Promise; - abstract executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; - abstract cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; -} - -@extHostNamedCustomer(MainContext.MainThreadNotebookProxyKernels) -export class MainThreadNotebookProxyKernels implements MainThreadNotebookProxyKernelsShape { - - private readonly _disposables = new DisposableStore(); - - private readonly _proxyKernels = new Map(); - private readonly _proxyKernelProxy: ExtHostNotebookProxyKernelsShape; - - constructor( - extHostContext: IExtHostContext, - @INotebookKernelService private readonly _notebookKernelService: INotebookKernelService, - ) { - this._proxyKernelProxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebookProxyKernels); - } - - dispose(): void { - this._disposables.dispose(); - - for (let [, registration] of this._proxyKernels.values()) { - registration.dispose(); - } - } - - // -- Proxy kernel - - async $addProxyKernel(handle: number, data: INotebookProxyKernelDto): Promise { - const that = this; - const proxyKernel = new class extends MainThreadProxyKernel { - async resolveKernel(): Promise { - try { - this.connectionState = ProxyKernelState.Initializing; - this._onDidChange.fire({ connectionState: true }); - const delegateKernel = await that._proxyKernelProxy.$resolveKernel(handle); - this.connectionState = ProxyKernelState.Connected; - this._onDidChange.fire({ connectionState: true }); - return delegateKernel; - } catch (err) { - onUnexpectedError(err); - this.connectionState = ProxyKernelState.Disconnected; - this._onDidChange.fire({ connectionState: true }); - return null; - } - } - async executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise { } - async cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise { } - }(data); - - const registration = this._notebookKernelService.registerKernel(proxyKernel); - this._proxyKernels.set(handle, [proxyKernel, registration]); - } - - $updateProxyKernel(handle: number, data: Partial): void { - const tuple = this._proxyKernels.get(handle); - if (tuple) { - tuple[0].update(data); - } - } - - $removeProxyKernel(handle: number): void { - const tuple = this._proxyKernels.get(handle); - if (tuple) { - tuple[1].dispose(); - this._proxyKernels.delete(handle); - } - } - - async executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise { - } - - async cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise { - } -} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 86cb018e40414..43e08b3776bfd 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -93,7 +93,6 @@ import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive'; import { combinedDisposable } from 'vs/base/common/lifecycle'; import { checkProposedApiEnabled, ExtensionIdentifierSet, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug'; -import { ExtHostNotebookProxyKernels } from 'vs/workbench/api/common/extHostNotebookProxyKernels'; export interface IExtensionRegistries { mine: ExtensionDescriptionRegistry; @@ -161,7 +160,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostNotebookDocuments = rpcProtocol.set(ExtHostContext.ExtHostNotebookDocuments, new ExtHostNotebookDocuments(extHostNotebook)); const extHostNotebookEditors = rpcProtocol.set(ExtHostContext.ExtHostNotebookEditors, new ExtHostNotebookEditors(extHostLogService, rpcProtocol, extHostNotebook)); const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, extHostNotebook, extHostCommands, extHostLogService)); - const extHostNotebookProxyKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookProxyKernels, new ExtHostNotebookProxyKernels(rpcProtocol, extHostNotebookKernels, extHostLogService)); const extHostNotebookRenderers = rpcProtocol.set(ExtHostContext.ExtHostNotebookRenderers, new ExtHostNotebookRenderers(rpcProtocol, extHostNotebook)); const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors)); const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService)); @@ -1153,10 +1151,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I onDidChangeNotebookCellExecutionState(listener, thisArgs?, disposables?) { checkProposedApiEnabled(extension, 'notebookCellExecutionState'); return extHostNotebookKernels.onDidChangeNotebookCellExecutionState(listener, thisArgs, disposables); - }, - createNotebookProxyController(id: string, notebookType: string, label: string, handler: () => vscode.NotebookController | string | Thenable) { - checkProposedApiEnabled(extension, 'notebookProxyController'); - return extHostNotebookProxyKernels.createNotebookProxyController(extension, id, notebookType, label, handler); } }; diff --git a/src/vs/workbench/api/common/extHostNotebookProxyKernels.ts b/src/vs/workbench/api/common/extHostNotebookProxyKernels.ts deleted file mode 100644 index 786101bf360f3..0000000000000 --- a/src/vs/workbench/api/common/extHostNotebookProxyKernels.ts +++ /dev/null @@ -1,157 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Emitter } from 'vs/base/common/event'; -import { DisposableStore } from 'vs/base/common/lifecycle'; -import { ResourceMap } from 'vs/base/common/map'; -import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { ILogService } from 'vs/platform/log/common/log'; -import { ExtHostNotebookProxyKernelsShape, IMainContext, INotebookProxyKernelDto, MainContext, MainThreadNotebookProxyKernelsShape } from 'vs/workbench/api/common/extHost.protocol'; -import { createKernelId, ExtHostNotebookKernels } from 'vs/workbench/api/common/extHostNotebookKernels'; -import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; -import * as vscode from 'vscode'; - -interface IProxyKernelData { - extensionId: ExtensionIdentifier; - controller: vscode.NotebookProxyController; - onDidChangeSelection: Emitter<{ selected: boolean; notebook: vscode.NotebookDocument }>; - associatedNotebooks: ResourceMap; -} - -export type SelectKernelReturnArgs = ControllerInfo | { notebookEditorId: string } | ControllerInfo & { notebookEditorId: string } | undefined; -type ControllerInfo = { id: string; extension: string }; - - -export class ExtHostNotebookProxyKernels implements ExtHostNotebookProxyKernelsShape { - - private readonly _proxy: MainThreadNotebookProxyKernelsShape; - - private readonly _proxyKernelData: Map = new Map(); - private _handlePool: number = 0; - - private readonly _onDidChangeCellExecutionState = new Emitter(); - readonly onDidChangeNotebookCellExecutionState = this._onDidChangeCellExecutionState.event; - - constructor( - mainContext: IMainContext, - private readonly extHostNotebook: ExtHostNotebookKernels, - @ILogService private readonly _logService: ILogService - ) { - this._proxy = mainContext.getProxy(MainContext.MainThreadNotebookProxyKernels); - } - - createNotebookProxyController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler: () => vscode.NotebookController | string | Thenable): vscode.NotebookProxyController { - const handle = this._handlePool++; - - let isDisposed = false; - const commandDisposables = new DisposableStore(); - const onDidChangeSelection = new Emitter<{ selected: boolean; notebook: vscode.NotebookDocument }>(); - - const data: INotebookProxyKernelDto = { - id: createKernelId(extension.identifier, id), - notebookType: viewType, - extensionId: extension.identifier, - extensionLocation: extension.extensionLocation, - label: label || extension.identifier.value, - }; - - let _resolveHandler = handler; - - this._proxy.$addProxyKernel(handle, data).catch(err => { - // this can happen when a kernel with that ID is already registered - console.log(err); - isDisposed = true; - }); - - let tokenPool = 0; - const _update = () => { - if (isDisposed) { - return; - } - const myToken = ++tokenPool; - Promise.resolve().then(() => { - if (myToken === tokenPool) { - this._proxy.$updateProxyKernel(handle, data); - } - }); - }; - - // notebook documents that are associated to this controller - const associatedNotebooks = new ResourceMap(); - - const controller: vscode.NotebookProxyController = { - get id() { return id; }, - get notebookType() { return data.notebookType; }, - onDidChangeSelectedNotebooks: onDidChangeSelection.event, - get label() { - return data.label; - }, - set label(value) { - data.label = value ?? extension.displayName ?? extension.name; - _update(); - }, - get detail() { - return data.detail ?? ''; - }, - set detail(value) { - data.detail = value; - _update(); - }, - get description() { - return data.description ?? ''; - }, - set description(value) { - data.description = value; - _update(); - }, - get kind() { - checkProposedApiEnabled(extension, 'notebookControllerKind'); - return data.kind ?? ''; - }, - set kind(value) { - checkProposedApiEnabled(extension, 'notebookControllerKind'); - data.kind = value; - _update(); - }, - get resolveHandler() { - return _resolveHandler; - }, - dispose: () => { - if (!isDisposed) { - this._logService.trace(`NotebookProxyController[${handle}], DISPOSED`); - isDisposed = true; - this._proxyKernelData.delete(handle); - commandDisposables.dispose(); - onDidChangeSelection.dispose(); - this._proxy.$removeProxyKernel(handle); - } - } - }; - - this._proxyKernelData.set(handle, { - extensionId: extension.identifier, - controller, - onDidChangeSelection, - associatedNotebooks - }); - return controller; - } - - async $resolveKernel(handle: number): Promise { - const obj = this._proxyKernelData.get(handle); - if (!obj) { - // extension can dispose kernels in the meantime - return null; - } - - const controller = await obj.controller.resolveHandler(); - if (typeof controller === 'string') { - return controller; - } else { - return this.extHostNotebook.getIdByController(controller); - } - } -} - diff --git a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts index 732b53f22e474..c92696d34ebab 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts @@ -14,7 +14,7 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/mode import { CellKind, INotebookTextModel, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; export class NotebookExecutionService implements INotebookExecutionService, IDisposable { declare _serviceBrand: undefined; @@ -91,11 +91,7 @@ export class NotebookExecutionService implements INotebookExecutionService, IDis this._logService.debug(`NotebookExecutionService#cancelNotebookCellHandles ${JSON.stringify(cellsArr)}`); const kernel = this._notebookKernelService.getSelectedOrSuggestedKernel(notebook); if (kernel) { - if (kernel.type === NotebookKernelType.Proxy) { - this._activeProxyKernelExecutionToken?.dispose(true); - } else { - await kernel.cancelNotebookCellExecution(notebook.uri, cellsArr); - } + await kernel.cancelNotebookCellExecution(notebook.uri, cellsArr); } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index 081eab0180ec5..13fd479ef1ac0 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -32,18 +32,12 @@ export interface INotebookKernelChangeEvent { hasExecutionOrder?: true; } -export const enum NotebookKernelType { - Resolved, - Proxy = 1 -} - export enum NotebookControllerState { Idle = 1, Connecting = 2 } -export interface IResolvedNotebookKernel { - readonly type: NotebookKernelType.Resolved; +export interface INotebookKernel { readonly id: string; readonly viewType: string; readonly onDidChange: Event>; @@ -77,33 +71,6 @@ export interface INotebookProxyKernelChangeEvent extends INotebookKernelChangeEv connectionState?: true; } -export interface INotebookProxyKernel { - readonly type: NotebookKernelType.Proxy; - readonly id: string; - readonly viewType: string; - readonly extension: ExtensionIdentifier; - readonly localResourceRoot: URI; - readonly preloadUris: URI[]; - readonly preloadProvides: string[]; - - readonly onDidChange: Event>; - label: string; - description?: string; - detail?: string; - kind?: string; - state?: NotebookControllerState; - supportedLanguages: string[]; - implementsInterrupt?: boolean; - implementsExecutionOrder?: boolean; - connectionState: ProxyKernelState; - resolveKernel(uri: URI): Promise; - executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; - cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; - onDispose: Event; -} - -export type INotebookKernel = IResolvedNotebookKernel | INotebookProxyKernel; - export interface INotebookTextModelLike { uri: URI; viewType: string } export const INotebookKernelService = createDecorator('INotebookKernelService'); diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts index 8d3bff7a83a58..9ceec332a7a0f 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts @@ -20,7 +20,7 @@ import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CellKind, IOutputDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernelService, IResolvedNotebookKernel, ISelectedNotebooksChangeEvent, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService, ISelectedNotebooksChangeEvent, NotebookControllerState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; @@ -166,8 +166,7 @@ suite('NotebookExecutionService', () => { }); }); -class TestNotebookKernel implements IResolvedNotebookKernel { - type: NotebookKernelType.Resolved = NotebookKernelType.Resolved; +class TestNotebookKernel implements INotebookKernel { id: string = 'test'; label: string = ''; viewType = '*'; @@ -179,14 +178,18 @@ class TestNotebookKernel implements IResolvedNotebookKernel { preloadUris: URI[] = []; preloadProvides: string[] = []; supportedLanguages: string[] = []; + onDispose = Event.None; executeNotebookCellsRequest(): Promise { throw new Error('Method not implemented.'); } cancelNotebookCellExecution(): Promise { throw new Error('Method not implemented.'); } - constructor(opts?: { languages: string[] }) { this.supportedLanguages = opts?.languages ?? [PLAINTEXT_LANGUAGE_ID]; } + kind?: string | undefined; + state?: NotebookControllerState | undefined; + implementsInterrupt?: boolean | undefined; + implementsExecutionOrder?: boolean | undefined; } diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts index e5b7f84b8bf42..4763ee855c6df 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts @@ -21,7 +21,7 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { CellEditType, CellKind, CellUri, IOutputDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernelService, IResolvedNotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; @@ -170,8 +170,7 @@ suite('NotebookExecutionStateService', () => { }); }); -class TestNotebookKernel implements IResolvedNotebookKernel { - type: NotebookKernelType.Resolved = NotebookKernelType.Resolved; +class TestNotebookKernel implements INotebookKernel { id: string = 'test'; label: string = ''; viewType = '*'; @@ -183,6 +182,7 @@ class TestNotebookKernel implements IResolvedNotebookKernel { preloadUris: URI[] = []; preloadProvides: string[] = []; supportedLanguages: string[] = []; + onDispose = Event.None; async executeNotebookCellsRequest(): Promise { } async cancelNotebookCellExecution(): Promise { } diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts index 62d6afecb8b13..54b3b52cb3c48 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; import { Emitter, Event } from 'vs/base/common/event'; -import { INotebookKernelService, IResolvedNotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService, NotebookControllerState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookKernelService } from 'vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { mock } from 'vs/base/test/common/mock'; @@ -159,8 +159,7 @@ suite('NotebookKernelService', () => { }); }); -class TestNotebookKernel implements IResolvedNotebookKernel { - type: NotebookKernelType.Resolved = NotebookKernelType.Resolved; +class TestNotebookKernel implements INotebookKernel { id: string = Math.random() + 'kernel'; label: string = 'test-label'; viewType = '*'; @@ -172,6 +171,8 @@ class TestNotebookKernel implements IResolvedNotebookKernel { preloadUris: URI[] = []; preloadProvides: string[] = []; supportedLanguages: string[] = []; + state?: NotebookControllerState | undefined = undefined; + onDispose = Event.None; executeNotebookCellsRequest(): Promise { throw new Error('Method not implemented.'); } diff --git a/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts b/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts index 3fe7cac5428df..df429123049a7 100644 --- a/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts @@ -13,52 +13,4 @@ declare module 'vscode' { export interface NotebookController { state?: NotebookControllerState; } - - export interface NotebookProxyController { - /** - * The identifier of this notebook controller. - * - * _Note_ that controllers are remembered by their identifier and that extensions should use - * stable identifiers across sessions. - */ - readonly id: string; - - /** - * The notebook type this controller is for. - */ - readonly notebookType: string; - - /** - * The human-readable label of this notebook controller. - */ - label: string; - - /** - * The human-readable description which is rendered less prominent. - */ - description?: string; - - /** - * The human-readable detail which is rendered less prominent. - */ - detail?: string; - - /** - * The human-readable label used to categorise controllers. - */ - kind?: string; - - resolveHandler: () => NotebookController | string | Thenable; - - readonly onDidChangeSelectedNotebooks: Event<{ readonly notebook: NotebookDocument; readonly selected: boolean }>; - - /** - * Dispose and free associated resources. - */ - dispose(): void; - } - - export namespace notebooks { - export function createNotebookProxyController(id: string, notebookType: string, label: string, resolveHandler: () => NotebookController | string | Thenable): NotebookProxyController; - } } From cf615d5700c64f4e4707b6753ca227803906e8e4 Mon Sep 17 00:00:00 2001 From: jeanp413 Date: Mon, 16 May 2022 02:45:49 -0500 Subject: [PATCH 050/942] Fixes #148983 --- .../terminal/browser/media/terminal.css | 7 +-- .../contrib/terminal/browser/terminal.ts | 3 +- .../terminal/browser/terminalEditor.ts | 8 +-- .../contrib/terminal/browser/terminalGroup.ts | 5 -- .../terminal/browser/terminalGroupService.ts | 18 +++++-- .../terminal/browser/terminalInstance.ts | 51 ++++++++++--------- .../terminal/browser/terminalService.ts | 4 -- .../contrib/terminal/browser/terminalView.ts | 6 +-- .../test/browser/workbenchTestServices.ts | 1 + 9 files changed, 51 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 945161cad7f48..73c2a11c08a12 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -39,7 +39,7 @@ .monaco-workbench .editor-instance .terminal-wrapper, .monaco-workbench .pane-body.integrated-terminal .terminal-wrapper { - display: none; + display: block; height: 100%; box-sizing: border-box; } @@ -113,11 +113,6 @@ .xterm.xterm-cursor-pointer .xterm-screen { cursor: pointer; } .xterm.column-select.focus .xterm-screen { cursor: crosshair; } -.monaco-workbench .editor-instance .terminal-wrapper.active, -.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper.active { - display: block; -} - .monaco-workbench .editor-instance .xterm { padding-left: 20px !important; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 6d1494b10d3e9..3368395091873 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -339,6 +339,7 @@ export interface ITerminalGroupService extends ITerminalInstanceHost, ITerminalF hidePanel(): void; focusTabs(): void; showTabs(): void; + updateVisibility(): void; } /** @@ -741,7 +742,7 @@ export interface ITerminalInstance { * * @param container The element to attach the terminal instance to. */ - attachToElement(container: HTMLElement): Promise | void; + attachToElement(container: HTMLElement): void; /** * Detaches the terminal instance from the terminal editor DOM element. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEditor.ts b/src/vs/workbench/contrib/terminal/browser/terminalEditor.ts index 71eeff2a7ace5..61412cfb781e7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalEditor.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalEditor.ts @@ -31,6 +31,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { openContextMenu } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; const findWidgetSelector = '.simple-find-part-wrapper'; @@ -68,7 +69,8 @@ export class TerminalEditor extends EditorPane { @IInstantiationService private readonly _instantiationService: IInstantiationService, @IContextMenuService private readonly _contextMenuService: IContextMenuService, @INotificationService private readonly _notificationService: INotificationService, - @ITerminalProfileService private readonly _terminalProfileService: ITerminalProfileService + @ITerminalProfileService private readonly _terminalProfileService: ITerminalProfileService, + @IWorkbenchLayoutService private readonly _workbenchLayoutService: IWorkbenchLayoutService ) { super(terminalEditorId, telemetryService, themeService, storageService); this._findState = new FindReplaceState(); @@ -86,7 +88,7 @@ export class TerminalEditor extends EditorPane { if (this._lastDimension) { this.layout(this._lastDimension); } - this._editorInput.terminalInstance?.setVisible(this.isVisible()); + this._editorInput.terminalInstance?.setVisible(this.isVisible() && this._workbenchLayoutService.isVisible(Parts.EDITOR_PART)); if (this._editorInput.terminalInstance) { // since the editor does not monitor focus changes, for ex. between the terminal // panel and the editors, this is needed so that the active instance gets set @@ -208,7 +210,7 @@ export class TerminalEditor extends EditorPane { override setVisible(visible: boolean, group?: IEditorGroup): void { super.setVisible(visible, group); - return this._editorInput?.terminalInstance?.setVisible(visible); + this._editorInput?.terminalInstance?.setVisible(visible && this._workbenchLayoutService.isVisible(Parts.EDITOR_PART)); } override getActionViewItem(action: IAction): IActionViewItem | undefined { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index 357c121317f3c..7e70c6912192d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -251,7 +251,6 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { private _instanceDisposables: Map = new Map(); private _activeInstanceIndex: number = -1; - private _isVisible: boolean = false; get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } @@ -313,8 +312,6 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { this._splitPaneContainer!.split(instance, parentIndex + 1); } - instance.setVisible(this._isVisible); - this._onInstancesChanged.fire(); } @@ -478,7 +475,6 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { this._initialRelativeSizes = undefined; } } - this.setVisible(this._isVisible); } get title(): string { @@ -511,7 +507,6 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { } setVisible(visible: boolean): void { - this._isVisible = visible; if (this._groupElement) { this._groupElement.style.display = visible ? '' : 'none'; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts index 4e057f1cb4d2f..a26f793e03305 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts @@ -5,7 +5,7 @@ import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { timeout } from 'vs/base/common/async'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { FindReplaceState } from 'vs/editor/contrib/find/browser/findState'; @@ -75,6 +75,8 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe this.onDidChangeGroups(() => this._terminalGroupCountContextKey.set(this.groups.length)); this._findState = new FindReplaceState(); + + Event.any(this.onDidChangeActiveGroup, this.onDidChangeInstances)(() => this.updateVisibility()); } hidePanel(): void { @@ -271,7 +273,6 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe const oldActiveGroup = this.activeGroup; this.activeGroupIndex = index; if (force || oldActiveGroup !== this.activeGroup) { - this.groups.forEach((g, i) => g.setVisible(i === this.activeGroupIndex)); this._onDidChangeActiveGroup.fire(this.activeGroup); this._onDidChangeActiveInstance.fire(this.activeInstance); } @@ -309,8 +310,6 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe this.activeGroupIndex = instanceLocation.groupIndex; this._onDidChangeActiveGroup.fire(this.activeGroup); instanceLocation.group.setActiveInstanceByIndex(activeInstanceIndex, true); - this.groups.forEach((g, i) => g.setVisible(i === instanceLocation.groupIndex)); - } setActiveGroupToNext() { @@ -476,6 +475,17 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe return `${index + 1}: ${group.title ? group.title : ''}`; }); } + + /** + * Visibility should be updated in the following cases: + * 1. Toggle `TERMINAL_VIEW_ID` visibility + * 2. Change active group + * 3. Change instances in active group + */ + updateVisibility() { + const visible = this._viewsService.isViewVisible(TERMINAL_VIEW_ID); + this.groups.forEach((g, i) => g.setVisible(visible && i === this.activeGroupIndex)); + } } interface IInstanceLocation { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 88b3e2d041d5b..995bb43a50771 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -552,8 +552,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // The terminal panel needs to have been created to get the real view dimensions if (!this._container) { // Set the fallback dimensions if not - this._cols = 80; - this._rows = 30; + this._cols = Constants.DefaultCols; + this._rows = Constants.DefaultRows; return; } @@ -736,6 +736,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._pathService.userHome().then(userHome => { this._userHome = userHome.fsPath; }); + + if (this._isVisible) { + this._open(); + } + return xterm; } @@ -954,7 +959,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._container = undefined; } - attachToElement(container: HTMLElement): Promise | void { + attachToElement(container: HTMLElement): void { // The container did not change, do nothing if (this._container === container) { return; @@ -962,24 +967,28 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._attachBarrier.open(); - // Attach has not occurred yet - if (!this._wrapperElement) { - return this._attachToElement(container); - } - this.xterm?.attachToElement(this._wrapperElement); - // The container changed, reattach this._container = container; - this._container.appendChild(this._wrapperElement); + if (this._wrapperElement) { + this._container.appendChild(this._wrapperElement); + } setTimeout(() => this._initDragAndDrop(container)); } - private async _attachToElement(container: HTMLElement): Promise { - if (this._wrapperElement) { - throw new Error('The terminal instance has already been attached to a container'); + /** + * Opens the the terminal instance inside the parent DOM element previously set with + * `attachToElement`, you must ensure the parent DOM element is explicitly visible before + * invoking this function as it performs some DOM calculations internally + */ + private _open(): void { + if (this._wrapperElement || !this.xterm) { + return; + } + + if (!this._container || !this._container.isConnected) { + throw new Error('A container element needs to be set with `attachToElement` and be part of the DOM before calling `_open`'); } - this._container = container; this._wrapperElement = document.createElement('div'); this._wrapperElement.classList.add('terminal-wrapper'); const xtermElement = document.createElement('div'); @@ -987,7 +996,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._container.appendChild(this._wrapperElement); - const xterm = await this._xtermReadyPromise; + const xterm = this.xterm; // Attach the xterm object to the DOM, exposing it to the smoke tests this._wrapperElement.xterm = xterm.raw; @@ -1106,21 +1115,16 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._register(dom.addDisposableListener(xterm.raw.textarea, 'blur', () => this._setFocus(false))); this._register(dom.addDisposableListener(xterm.raw.textarea, 'focusout', () => this._setFocus(false))); - this._initDragAndDrop(container); + this._initDragAndDrop(this._container); this._widgetManager.attachToElement(screenElement); this._processManager.onProcessReady((e) => { this._linkManager?.setWidgetManager(this._widgetManager); }); - // const computedStyle = window.getComputedStyle(this._container); - // const computedStyle = window.getComputedStyle(this._container.parentElement!); - // const width = parseInt(computedStyle.getPropertyValue('width').replace('px', ''), 10); - // const height = parseInt(computedStyle.getPropertyValue('height').replace('px', ''), 10); if (this._lastLayoutDimensions) { this.layout(this._lastLayoutDimensions); } - this.setVisible(this._isVisible); this.updateConfig(); // If IShellLaunchConfig.waitOnExit was true and the process finished before the terminal @@ -1376,10 +1380,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { setVisible(visible: boolean): void { this._isVisible = visible; - if (this._wrapperElement) { - this._wrapperElement.classList.toggle('active', visible); - } + this._wrapperElement?.classList.toggle('active', visible); if (visible && this.xterm) { + this._open(); // Resize to re-evaluate dimensions, this will ensure when switching to a terminal it is // using the most up to date dimensions (eg. when terminal is created in the background // using cached dimensions of a split terminal). diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 62d52e82cc942..f7ea738dde3af 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -764,8 +764,6 @@ export class TerminalService implements ITerminalService { group.addInstance(source); this.setActiveInstance(source); await this._terminalGroupService.showPanel(true); - // TODO: Shouldn't this happen automatically? - source.setVisible(true); if (target && side) { const index = group.terminalInstances.indexOf(target) + (side === 'after' ? 1 : 0); @@ -775,7 +773,6 @@ export class TerminalService implements ITerminalService { // Fire events this._onDidChangeInstances.fire(); this._onDidChangeActiveGroup.fire(this._terminalGroupService.activeGroup); - this._terminalGroupService.showPanel(true); this._onDidRequestHideFindWidget.fire(); } @@ -1033,7 +1030,6 @@ export class TerminalService implements ITerminalService { } shellLaunchConfig.parentTerminalId = parent.instanceId; instance = group.split(shellLaunchConfig); - this._terminalGroupService.groups.forEach((g, i) => g.setVisible(i === this._terminalGroupService.activeGroupIndex)); } return instance; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index d450e1d66098c..f2a8e3a5aaede 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -171,12 +171,8 @@ export class TerminalViewPane extends ViewPane { // defer focusing the panel to the focus() call // to prevent overriding preserveFocus for extensions this._terminalGroupService.showPanel(false); - if (hadTerminals) { - this._terminalGroupService.activeGroup?.setVisible(visible); - } - } else { - this._terminalGroupService.activeGroup?.setVisible(false); } + this._terminalGroupService.updateVisibility(); })); this.layoutBody(this._parentDomElement.offsetHeight, this._parentDomElement.offsetWidth); } diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 9a1025debd886..7973cf4475559 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -1830,6 +1830,7 @@ export class TestTerminalGroupService implements ITerminalGroupService { getFindState(): FindReplaceState { throw new Error('Method not implemented.'); } findNext(): void { throw new Error('Method not implemented.'); } findPrevious(): void { throw new Error('Method not implemented.'); } + updateVisibility(): void { throw new Error('Method not implemented.'); } } export class TestTerminalProfileService implements ITerminalProfileService { From f790b3ba1f03b7071b77000213f386c5e92205b4 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 16 May 2022 14:15:23 +0200 Subject: [PATCH 051/942] Implements gutter buttons. --- .../mergeEditor/browser/editorGutterWidget.ts | 100 ++++ .../mergeEditor/browser/media/mergeEditor.css | 47 +- .../mergeEditor/browser/mergeEditor.ts | 234 +++++----- .../mergeEditor/browser/mergeEditorModel.ts | 158 ++++--- .../contrib/mergeEditor/browser/model.ts | 434 ++++++++++-------- 5 files changed, 595 insertions(+), 378 deletions(-) create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/editorGutterWidget.ts diff --git a/src/vs/workbench/contrib/mergeEditor/browser/editorGutterWidget.ts b/src/vs/workbench/contrib/mergeEditor/browser/editorGutterWidget.ts new file mode 100644 index 0000000000000..9b3954eed488f --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/editorGutterWidget.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; + +export class EditorGutterWidget { + constructor( + private readonly _editor: ICodeEditor, + private readonly _domNode: HTMLElement, + private readonly itemProvider: IGutterItemProvider, + ) { + this._domNode.className = 'gutter'; + + this._editor.onDidScrollChange(() => { + this.render(); + }); + } + + private readonly views = new Map(); + + private render(): void { + const visibleRange = this._editor.getVisibleRanges()[0]; + const visibleRange2 = new LineRange( + visibleRange.startLineNumber, + visibleRange.endLineNumber - visibleRange.startLineNumber + ); + + const gutterItems = this.itemProvider.getIntersectingGutterItems(visibleRange2); + + const lineHeight = this._editor.getOptions().get(EditorOption.lineHeight); + + const unusedIds = new Set(this.views.keys()); + for (const gutterItem of gutterItems) { + if (!gutterItem.range.touches(visibleRange2)) { + continue; + } + + unusedIds.delete(gutterItem.id); + let view = this.views.get(gutterItem.id); + if (!view) { + const viewDomNode = document.createElement('div'); + viewDomNode.className = 'gutter-item'; + this._domNode.appendChild(viewDomNode); + const itemView = this.itemProvider.createView(gutterItem, viewDomNode); + view = new ManagedGutterItemView(itemView, viewDomNode); + this.views.set(gutterItem.id, view); + } + + const scrollTop = this._editor.getScrollTop(); + const top = this._editor.getTopForLineNumber(gutterItem.range.startLineNumber) - scrollTop; + const height = lineHeight * gutterItem.range.lineCount; + + view.domNode.style.top = `${top}px`; + view.domNode.style.height = `${height}px`; + + view.gutterItemView.layout(top, height, 0, -1); + } + + for (const id of unusedIds) { + const view = this.views.get(id)!; + view.gutterItemView.dispose(); + this._domNode.removeChild(view.domNode); + this.views.delete(id); + } + } +} + +class ManagedGutterItemView { + constructor( + public readonly gutterItemView: IGutterItemView, + public readonly domNode: HTMLDivElement + ) { } +} + +export interface IGutterItemProvider { + // onDidChange + getIntersectingGutterItems(range: LineRange): TItem[]; + + createView(item: TItem, target: HTMLElement): IGutterItemView; +} + +export interface IGutterItemInfo { + id: string; + range: LineRange; + + // To accommodate view zones: + offsetInPx: number; + additionalHeightInPx: number; +} + +export interface IGutterItemView extends IDisposable { + update(item: T): void; + layout(top: number, height: number, viewTop: number, viewHeight: number): void; +} + diff --git a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css index 749b947af5530..2e3929e236c83 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css +++ b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css @@ -20,10 +20,12 @@ } .monaco-workbench .merge-editor .code-view > .container > .gutter { - min-width: 20px; - /* background-color: yellow; */ - margin-right: 3px; + width: 24px; position: relative; + overflow: hidden; + + flex-shrink: 0; + flex-grow: 0; } .monaco-editor .merge-accept-conflict-glyph { @@ -34,9 +36,42 @@ background-color: rgb(170 175 170 / 15%); } +.gutter-item { + position: absolute; +} + .merge-accept-gutter-marker { - min-height: 30px; - min-width: 20px; - background-color: yellow; + width: 28px; + /* background-color: yellow; */ + + + margin-left: 4px; +} + +.merge-accept-gutter-marker .background { + height: 100%; + width: 50%; position: absolute; } + +.merge-accept-gutter-marker.multi-line .background { + border: 2px solid #323232FF; + border-right: 0; + left: 8px; + width: 10px; +} + +.merge-accept-gutter-marker .checkbox { + height: 100%; + width: 100%; + position: absolute; + display: flex; + flex-direction: column; + justify-content: center; +} + +.merge-accept-gutter-marker .checkbox .accept-conflict-group.monaco-custom-toggle.monaco-checkbox { + margin: 0; + padding: 0; + background-color: #3C3C3CFF; +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index c846f45c87f2f..b3f0ffcbbab60 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -16,7 +16,7 @@ import { IEditorControl, IEditorOpenContext } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { BugIndicatingError } from 'vs/base/common/errors'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { Direction, Grid, IView, IViewSize, SerializableGrid } from 'vs/base/browser/ui/grid/grid'; import { Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview'; import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; @@ -30,15 +30,17 @@ import { localize } from 'vs/nls'; import { ILabelService } from 'vs/platform/label/common/label'; import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IAction } from 'vs/base/common/actions'; import { Range } from 'vs/editor/common/core/range'; -import { Position } from 'vs/editor/common/core/position'; -import { acceptConflictIcon } from 'vs/workbench/contrib/mergeEditor/browser/icons'; -import { ConflictGroup, MergeState } from 'vs/workbench/contrib/mergeEditor/browser/model'; - +import { Checkbox, Toggle } from 'vs/base/browser/ui/toggle/toggle'; +import { EditorGutterWidget, IGutterItemInfo, IGutterItemView } from './editorGutterWidget'; +import { noBreakWhitespace } from 'vs/base/common/strings'; +import { ModifiedBaseRange, ModifiedBaseRangeState, ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { autorun, derivedObservable, derivedWritebaleObservable, IObservable, ITransaction } from 'vs/workbench/contrib/audioCues/browser/observable'; +import { Codicon } from 'vs/base/common/codicons'; export const ctxIsMergeEditor = new RawContextKey('isMergeEditor', false); export const ctxUsesColumnLayout = new RawContextKey('mergeEditorUsesColumnLayout', false); @@ -172,77 +174,13 @@ export class MergeEditor extends EditorPane { this.input2View.setModel(model.input2, localize('theirs', 'Theirs',), undefined); this.inputResultView.setModel(model.result, localize('result', 'Result',), this._labelService.getUriLabel(model.result.uri, { relative: true })); - - const input1LineNumberToMergeableDiffMap = new Map(); - - this.input1View.editor.onMouseDown(e => { - if (e.target.element && e.target.element.className.includes('merge-')) { - const lineNumber = e.target.position?.lineNumber; - if (lineNumber) { - const diff = input1LineNumberToMergeableDiffMap.get(lineNumber); - if (diff) { - const state = (model.getState(diff) || new MergeState(false, false, false)).toggleInput1(); - model.setConflictResolutionStatus(diff, state); - } - } - } - }); - - this.input1View.editor.changeDecorations(c => { - for (const mergeableDiff of model.mergeableDiffs) { - if (mergeableDiff.input1FullDiff) { - const lineNumber = mergeableDiff.input1FullDiff.modifiedRange.startLineNumber; - input1LineNumberToMergeableDiffMap.set(lineNumber, mergeableDiff); - c.addDecoration(Range.fromPositions(new Position(lineNumber, 0)), { - description: 'foo', - glyphMarginClassName: ThemeIcon.asClassName(acceptConflictIcon) + ' merge-accept-conflict-glyph', - }); - } - } - }); - - - - const input2LineNumberToMergeableDiffMap = new Map(); - - this.input2View.editor.onMouseDown(e => { - if (e.target.element && e.target.element.className.includes('merge-')) { - const lineNumber = e.target.position?.lineNumber; - if (lineNumber) { - const diff = input2LineNumberToMergeableDiffMap.get(lineNumber); - if (diff) { - const state = (model.getState(diff) || new MergeState(false, false, false)).toggleInput2(); - model.setConflictResolutionStatus(diff, state); - } - } - } - }); - - - - this.input2View.editor.changeDecorations(c => { - for (const mergeableDiff of model.mergeableDiffs) { - if (mergeableDiff.input2FullDiff) { - const lineNumber = mergeableDiff.input2FullDiff.modifiedRange.startLineNumber; - input2LineNumberToMergeableDiffMap.set(lineNumber, mergeableDiff); - c.addDecoration(Range.fromPositions(new Position(lineNumber, 1)), { - description: 'foo', - glyphMarginClassName: ThemeIcon.asClassName(acceptConflictIcon) + ' merge-accept-conflict-glyph', - }); - } - } - }); - - let input1Decorations = new Array(); let input2Decorations = new Array(); - - for (const m of model.mergeableDiffs) { - - if (!m.totalInput1Range.isEmpty) { + for (const m of model.modifiedBaseRanges) { + if (!m.input1Range.isEmpty) { input1Decorations.push({ - range: new Range(m.totalInput1Range.startLineNumber, 1, m.totalInput1Range.endLineNumberExclusive - 1, 1), + range: new Range(m.input1Range.startLineNumber, 1, m.input1Range.endLineNumberExclusive - 1, 1), options: { isWholeLine: true, className: 'merge-accept-foo', @@ -251,9 +189,9 @@ export class MergeEditor extends EditorPane { }); } - if (!m.totalInput2Range.isEmpty) { + if (!m.input2Range.isEmpty) { input2Decorations.push({ - range: new Range(m.totalInput2Range.startLineNumber, 1, m.totalInput2Range.endLineNumberExclusive - 1, 1), + range: new Range(m.input2Range.startLineNumber, 1, m.input2Range.endLineNumberExclusive - 1, 1), options: { isWholeLine: true, className: 'merge-accept-foo', @@ -262,20 +200,20 @@ export class MergeEditor extends EditorPane { }); } - const max = Math.max(m.totalInput1Range.lineCount, m.totalInput2Range.lineCount, 1); + const max = Math.max(m.input1Range.lineCount, m.input2Range.lineCount, 1); this.input1View.editor.changeViewZones(a => { a.addZone({ - afterLineNumber: m.totalInput1Range.endLineNumberExclusive - 1, - heightInLines: max - m.totalInput1Range.lineCount, + afterLineNumber: m.input1Range.endLineNumberExclusive - 1, + heightInLines: max - m.input1Range.lineCount, domNode: $('div.diagonal-fill'), }); }); this.input2View.editor.changeViewZones(a => { a.addZone({ - afterLineNumber: m.totalInput2Range.endLineNumberExclusive - 1, - heightInLines: max - m.totalInput2Range.lineCount, + afterLineNumber: m.input2Range.endLineNumberExclusive - 1, + heightInLines: max - m.input2Range.lineCount, domNode: $('div.diagonal-fill'), }); }); @@ -284,6 +222,61 @@ export class MergeEditor extends EditorPane { this.input1View.editor.deltaDecorations([], input1Decorations); this.input2View.editor.deltaDecorations([], input2Decorations); + new EditorGutterWidget(this.input1View.editor, this.input1View._gutterDiv, { + getIntersectingGutterItems: (range) => + model.modifiedBaseRanges + .filter((r) => r.input1Diffs.length > 0) + .map((baseRange, idx) => ({ + id: idx.toString(), + additionalHeightInPx: 0, + offsetInPx: 0, + range: baseRange.input1Range, + toggleState: derivedObservable( + 'toggle', + (reader) => model.getState(baseRange).read(reader)?.input1 + ), + setState(value, tx) { + model.setState( + baseRange, + ( + model.getState(baseRange).get() || + new ModifiedBaseRangeState(false, false, false) + ).withInput1(value), + tx + ); + }, + })), + createView: (item, target) => new ButtonView(item, target), + }); + + new EditorGutterWidget(this.input2View.editor, this.input2View._gutterDiv, { + getIntersectingGutterItems: (range) => + model.modifiedBaseRanges + .filter((r) => r.input2Diffs.length > 0) + .map((baseRange, idx) => ({ + id: idx.toString(), + additionalHeightInPx: 0, + offsetInPx: 0, + range: baseRange.input2Range, + baseRange, + toggleState: derivedObservable( + 'toggle', + (reader) => model.getState(baseRange).read(reader)?.input2 + ), + setState(value, tx) { + model.setState( + baseRange, + ( + model.getState(baseRange).get() || + new ModifiedBaseRangeState(false, false, false) + ).withInput2(value), + tx + ); + }, + })), + createView: (item, target) => new ButtonView(item, target), + }); + } protected override setEditorVisible(visible: boolean): void { @@ -324,6 +317,43 @@ export class MergeEditor extends EditorPane { } } +interface ButtonViewData extends IGutterItemInfo { + toggleState: IObservable; + setState(value: boolean, tx: ITransaction | undefined): void; +} + +class ButtonView extends Disposable implements IGutterItemView { + constructor(item: ButtonViewData, target: HTMLElement) { + super(); + + target.classList.add('merge-accept-gutter-marker'); + target.classList.add(item.range.lineCount > 1 ? 'multi-line' : 'single-line'); + + const checkBox = new Toggle({ isChecked: false, title: 'TODO', icon: Codicon.check, actionClassName: 'monaco-checkbox' }); + checkBox.domNode.classList.add('accept-conflict-group'); + + this._register( + autorun((reader) => { + const value = item.toggleState.read(reader); + checkBox.checked = value === true; + }, 'Update Toggle State') + ); + + this._register(checkBox.onChange(() => { + item.setState(checkBox.checked, undefined); + })); + + target.appendChild($('div.background', {}, noBreakWhitespace)); + target.appendChild($('div.checkbox', {}, checkBox.domNode)); + } + layout(top: number, height: number, viewTop: number, viewHeight: number): void { + + } + + update(baseRange: ButtonViewData): void { + } +} + interface ICodeEditorViewOptions { readonly: boolean; } @@ -336,7 +366,7 @@ class CodeEditorView implements IView { element: HTMLElement; private _titleElement: HTMLElement; private _editorElement: HTMLElement; - private _gutterDiv: HTMLElement; + public _gutterDiv: HTMLElement; minimumWidth: number = 10; maximumWidth: number = Number.MAX_SAFE_INTEGER; @@ -351,7 +381,7 @@ class CodeEditorView implements IView { private _title: IconLabel; public readonly editor: CodeEditorWidget; - private readonly gutter: EditorGutter; + // private readonly gutter: EditorGutterWidget; constructor( @@ -371,13 +401,11 @@ class CodeEditorView implements IView { this.editor = this.instantiationService.createInstance( CodeEditorWidget, this._editorElement, - { minimap: { enabled: false }, readOnly: this._options.readonly, /*glyphMargin: false,*/ lineNumbersMinChars: 2 }, + { minimap: { enabled: false }, readOnly: this._options.readonly, glyphMargin: false, lineNumbersMinChars: 2 }, { contributions: [] } ); this._title = new IconLabel(this._titleElement, { supportIcons: true }); - this.gutter = new EditorGutter(this.editor, this._gutterDiv); - console.log(this.gutter); // keep alive } public setModel(model: ITextModel, title: string, description: string | undefined): void { @@ -393,39 +421,3 @@ class CodeEditorView implements IView { this.editor.layout({ width: width - this._gutterDiv.clientWidth, height: height - this._titleElement.clientHeight }); } } - -class ReentrancyBarrier { - private isActive = false; - - public runExclusively(fn: () => void): void { - if (this.isActive) { - return; - } - this.isActive = true; - try { - fn(); - } finally { - this.isActive = false; - } - } -} - -class EditorGutter { - constructor( - private readonly _editor: ICodeEditor, - private readonly _domNode: HTMLElement, - ) { - this._editor.onDidScrollChange(() => { - this.render(); - }); - } - - private node = $('div.merge-accept-gutter-marker'); - - private render(): void { - this._domNode.append(this.node); - - const top = this._editor.getTopForLineNumber(10) - this._editor.getScrollTop(); - this.node.style.top = `${top}px`; - } -} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts index 081869931c31a..989febccdb718 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -3,62 +3,64 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Emitter } from 'vs/base/common/event'; import { compareBy, numberComparator } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; -import { ConflictGroup, LineEdit, LineEdits, LineDiff, MergeState, LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; +import { ModifiedBaseRange, LineEdit, LineEdits, LineDiff, ModifiedBaseRangeState, LineRange, ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/model'; export class MergeEditorModelFactory { constructor( - @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, - //@IInstantiationService private readonly instantiationService: IInstantiationService, + @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService ) { } public async create( - ancestor: ITextModel, + base: ITextModel, input1: ITextModel, input2: ITextModel, result: ITextModel, ): Promise { - const ancestorToInput1DiffPromise = this._editorWorkerService.computeDiff( - ancestor.uri, + const baseToInput1DiffPromise = this._editorWorkerService.computeDiff( + base.uri, input1.uri, false, 1000 ); - const ancestorToInput2DiffPromise = this._editorWorkerService.computeDiff( - ancestor.uri, + const baseToInput2DiffPromise = this._editorWorkerService.computeDiff( + base.uri, input2.uri, false, 1000 ); - const [ancestorToInput1Diff, ancestorToInput2Diff] = await Promise.all([ - ancestorToInput1DiffPromise, - ancestorToInput2DiffPromise, + const [baseToInput1Diff, baseToInput2Diff] = await Promise.all([ + baseToInput1DiffPromise, + baseToInput2DiffPromise, ]); const changesInput1 = - ancestorToInput1Diff?.changes.map((c) => - LineDiff.fromLineChange(c, ancestor, input1) + baseToInput1Diff?.changes.map((c) => + LineDiff.fromLineChange(c, base, input1) ) || []; const changesInput2 = - ancestorToInput2Diff?.changes.map((c) => - LineDiff.fromLineChange(c, ancestor, input2) + baseToInput2Diff?.changes.map((c) => + LineDiff.fromLineChange(c, base, input2) ) || []; return new MergeEditorModel( InternalSymbol, - ancestor, + base, input1, input2, result, changesInput1, changesInput2, + this._editorWorkerService ); } } @@ -66,20 +68,29 @@ export class MergeEditorModelFactory { const InternalSymbol = Symbol(); export class MergeEditorModel extends EditorModel { - private resultEdits = new ResultEdits([], this.ancestor, this.result); + private resultEdits = new ResultEdits([], this.base, this.result, this.editorWorkerService); constructor( symbol: typeof InternalSymbol, - readonly ancestor: ITextModel, + readonly base: ITextModel, readonly input1: ITextModel, readonly input2: ITextModel, readonly result: ITextModel, private readonly inputOneLinesDiffs: readonly LineDiff[], - private readonly inputTwoLinesDiffs: readonly LineDiff[] + private readonly inputTwoLinesDiffs: readonly LineDiff[], + private readonly editorWorkerService: IEditorWorkerService ) { super(); - result.setValue(ancestor.getValue()); + result.setValue(base.getValue()); + + this.resultEdits.onDidChange(() => { + transaction(tx => { + for (const [key, value] of this.modifiedBaseRangeStateStores) { + value.set(this.computeState(key), tx); + } + }); + }); /* // Apply all non-conflicts diffs @@ -96,60 +107,75 @@ export class MergeEditorModel extends EditorModel { } new LineEdits(lineEditsArr).apply(result); */ - - console.log(this, 'hello'); - (globalThis as any)['mergeEditorModel'] = this; } public get resultDiffs(): readonly LineDiff[] { return this.resultEdits.diffs; } - public readonly mergeableDiffs = ConflictGroup.partitionDiffs( - this.ancestor, + public readonly modifiedBaseRanges = ModifiedBaseRange.fromDiffs( + this.base, this.input1, this.inputOneLinesDiffs, this.input2, this.inputTwoLinesDiffs ); - public getState(conflict: ConflictGroup): MergeState | undefined { + private readonly modifiedBaseRangeStateStores = new Map>( + this.modifiedBaseRanges.map(s => ([s, new ObservableValue(new ModifiedBaseRangeState(false, false, false), 'State')])) + ); + + private computeState(baseRange: ModifiedBaseRange): ModifiedBaseRangeState | undefined { const existingDiff = this.resultEdits.findConflictingDiffs( - conflict.totalOriginalRange + baseRange.baseRange ); if (!existingDiff) { - return new MergeState(false, false, false); + return new ModifiedBaseRangeState(false, false, false); } - const input1Edit = conflict.getInput1LineEdit(); + const input1Edit = baseRange.getInput1LineEdit(); if (input1Edit && existingDiff.getLineEdit().equals(input1Edit)) { - return new MergeState(true, false, false); + return new ModifiedBaseRangeState(true, false, false); } - const input2Edit = conflict.getInput2LineEdit(); + const input2Edit = baseRange.getInput2LineEdit(); if (input2Edit && existingDiff.getLineEdit().equals(input2Edit)) { - return new MergeState(false, true, false); + return new ModifiedBaseRangeState(false, true, false); } return undefined; } - // Undo all edits of result that conflict with the conflict!! - public setConflictResolutionStatus( - conflict: ConflictGroup, - status: MergeState + public getState(baseRange: ModifiedBaseRange): IObservable { + const existingState = this.modifiedBaseRangeStateStores.get(baseRange); + if (!existingState) { + throw new BugIndicatingError('object must be from this instance'); + } + return existingState; + } + + public setState( + baseRange: ModifiedBaseRange, + state: ModifiedBaseRangeState, + transaction: ITransaction | undefined ): void { + const existingState = this.modifiedBaseRangeStateStores.get(baseRange); + if (!existingState) { + throw new BugIndicatingError('object must be from this instance'); + } + existingState.set(state, transaction); + const existingDiff = this.resultEdits.findConflictingDiffs( - conflict.totalOriginalRange + baseRange.baseRange ); if (existingDiff) { this.resultEdits.removeDiff(existingDiff); } - const edit = status.input1 - ? conflict.getInput1LineEdit() - : status.input2 - ? conflict.getInput2LineEdit() + const edit = state.input1 + ? baseRange.getInput1LineEdit() + : state.input2 + ? baseRange.getInput2LineEdit() : undefined; if (edit) { this.resultEdits.applyEditRelativeToOriginal(edit); @@ -158,12 +184,36 @@ export class MergeEditorModel extends EditorModel { } class ResultEdits { + private readonly barrier = new ReentrancyBarrier(); + private readonly onDidChangeEmitter = new Emitter(); + public readonly onDidChange = this.onDidChangeEmitter.event; + constructor( private _diffs: LineDiff[], - private readonly originalTextModel: ITextModel, - private readonly textModel: ITextModel, + private readonly baseTextModel: ITextModel, + private readonly resultTextModel: ITextModel, + private readonly _editorWorkerService: IEditorWorkerService ) { this._diffs.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator)); + + resultTextModel.onDidChangeContent(e => { + this.barrier.runExclusively(() => { + this._editorWorkerService.computeDiff( + baseTextModel.uri, + resultTextModel.uri, + false, + 1000 + ).then(e => { + const diffs = + e?.changes.map((c) => + LineDiff.fromLineChange(c, baseTextModel, resultTextModel) + ) || []; + this._diffs = diffs; + + this.onDidChangeEmitter.fire(undefined); + }); + }); + }); } public get diffs(): readonly LineDiff[] { @@ -177,8 +227,9 @@ class ResultEdits { throw new BugIndicatingError(); } - const edits = new LineEdits([diffToRemove.getReverseLineEdit()]); - edits.apply(this.textModel); + this.barrier.runExclusivelyOrThrow(() => { + diffToRemove.getReverseLineEdit().apply(this.resultTextModel); + }); this._diffs = this._diffs.map((d) => d.modifiedRange.isAfter(diffToRemove.modifiedRange) @@ -204,16 +255,16 @@ class ResultEdits { for (let i = 0; i < this._diffs.length; i++) { const diff = this._diffs[i]; - if (diff.originalRange.intersects(edit.range)) { + if (diff.originalRange.touches(edit.range)) { throw new BugIndicatingError(); } else if (diff.originalRange.isAfter(edit.range)) { if (!firstAfter) { firstAfter = true; newDiffs.push(new LineDiff( - this.originalTextModel, + this.baseTextModel, edit.range, - this.textModel, + this.resultTextModel, new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) )); } @@ -237,21 +288,22 @@ class ResultEdits { firstAfter = true; newDiffs.push(new LineDiff( - this.originalTextModel, + this.baseTextModel, edit.range, - this.textModel, + this.resultTextModel, new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) )); } this._diffs = newDiffs; - const edits = new LineEdits([new LineEdit(edit.range.delta(delta), edit.newLines, edit.data)]); - edits.apply(this.textModel); + this.barrier.runExclusivelyOrThrow(() => { + new LineEdit(edit.range.delta(delta), edit.newLines).apply(this.resultTextModel); + }); } // TODO return many! public findConflictingDiffs(rangeInOriginalTextModel: LineRange): LineDiff | undefined { // TODO binary search - return this.diffs.find(d => d.originalRange.intersects(rangeInOriginalTextModel)); + return this.diffs.find(d => d.originalRange.touches(rangeInOriginalTextModel)); } } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model.ts b/src/vs/workbench/contrib/mergeEditor/browser/model.ts index deae7ace505e6..5d21dc1718088 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model.ts @@ -9,11 +9,24 @@ import { Range } from 'vs/editor/common/core/range'; import { ILineChange } from 'vs/editor/common/diff/diffComputer'; import { ITextModel } from 'vs/editor/common/model'; -export class LineEdits { - constructor(public readonly edits: readonly LineEdit[]) { +export class LineEdit { + constructor( + public readonly range: LineRange, + public readonly newLines: string[] + ) { } + public equals(other: LineEdit): boolean { + return this.range.equals(other.range) && equals(this.newLines, other.newLines); } + public apply(model: ITextModel): void { + new LineEdits([this]).apply(model); + } +} + +export class LineEdits { + constructor(public readonly edits: readonly LineEdit[]) { } + public apply(model: ITextModel): void { model.pushEditOperations( null, @@ -26,188 +39,6 @@ export class LineEdits { } } -export class LineEdit { - equals(other: LineEdit) { - return this.range.equals(other.range) && equals(this.newLines, other.newLines); - } - - constructor( - public readonly range: LineRange, - public readonly newLines: string[], - public readonly data: T - ) { } -} - -export class ConflictGroup { - /** - * diffs1 and diffs2 together with the conflict relation form a bipartite graph. - * This method computes strongly connected components of that graph while maintaining the side of each diff. - */ - public static partitionDiffs( - originalTextModel: ITextModel, - input1TextModel: ITextModel, - diffs1: readonly LineDiff[], - input2TextModel: ITextModel, - diffs2: readonly LineDiff[] - ): ConflictGroup[] { - const compareByStartLineNumber = compareBy( - (d) => d.originalRange.startLineNumber, - numberComparator - ); - - const queueDiffs1 = new ArrayQueue( - diffs1.slice().sort(compareByStartLineNumber) - ); - const queueDiffs2 = new ArrayQueue( - diffs2.slice().sort(compareByStartLineNumber) - ); - - const result = new Array(); - - while (true) { - const lastDiff1 = queueDiffs1.peekLast(); - const lastDiff2 = queueDiffs2.peekLast(); - - if ( - lastDiff1 && - (!lastDiff2 || - lastDiff1.originalRange.startLineNumber >= - lastDiff2.originalRange.startLineNumber) - ) { - queueDiffs1.removeLast(); - - const otherConflictingWith = - queueDiffs2.takeFromEndWhile((d) => d.conflicts(lastDiff1)) || []; - - const singleLinesDiff = LineDiff.hull(otherConflictingWith); - - const moreConflictingWith = - (singleLinesDiff && - queueDiffs1.takeFromEndWhile((d) => - d.conflicts(singleLinesDiff) - )) || - []; - moreConflictingWith.push(lastDiff1); - - result.push( - new ConflictGroup( - originalTextModel, - input1TextModel, - moreConflictingWith, - queueDiffs1.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, - input2TextModel, - otherConflictingWith, - queueDiffs2.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, - ) - ); - } else if (lastDiff2) { - queueDiffs2.removeLast(); - - const otherConflictingWith = - queueDiffs1.takeFromEndWhile((d) => d.conflicts(lastDiff2)) || []; - - const singleLinesDiff = LineDiff.hull(otherConflictingWith); - - const moreConflictingWith = - (singleLinesDiff && - queueDiffs2.takeFromEndWhile((d) => - d.conflicts(singleLinesDiff) - )) || - []; - moreConflictingWith.push(lastDiff2); - - result.push( - new ConflictGroup( - originalTextModel, - input1TextModel, - otherConflictingWith, - queueDiffs1.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, - input2TextModel, - moreConflictingWith, - queueDiffs2.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, - ) - ); - } else { - break; - } - } - - result.reverse(); - - return result; - } - - public readonly input1FullDiff = LineDiff.hull(this.input1Diffs); - public readonly input2FullDiff = LineDiff.hull(this.input2Diffs); - - public readonly totalOriginalRange: LineRange; - public readonly totalInput1Range: LineRange; - public readonly totalInput2Range: LineRange; - - constructor( - public readonly originalTextModel: ITextModel, - public readonly input1TextModel: ITextModel, - public readonly input1Diffs: readonly LineDiff[], - public readonly input1DeltaLineCount: number, - public readonly input2TextModel: ITextModel, - public readonly input2Diffs: readonly LineDiff[], - public readonly input2DeltaLineCount: number, - ) { - if (this.input1Diffs.length === 0 && this.input2Diffs.length === 0) { - throw new BugIndicatingError('must have at least one diff'); - } - - const input1Diff = - this.input1FullDiff || - new LineDiff( - originalTextModel, - this.input2FullDiff!.originalRange, - input1TextModel, - this.input2FullDiff!.originalRange.delta(input1DeltaLineCount) - ); - - const input2Diff = - this.input2FullDiff || - new LineDiff( - originalTextModel, - this.input1FullDiff!.originalRange, - input1TextModel, - this.input1FullDiff!.originalRange.delta(input2DeltaLineCount) - ); - - const results = LineDiff.alignOriginalRegion([input1Diff, input2Diff]); - this.totalOriginalRange = results[0].originalRange; - this.totalInput1Range = results[0].modifiedRange; - this.totalInput2Range = results[1].modifiedRange; - } - - public get isConflicting(): boolean { - return this.input1Diffs.length > 0 && this.input2Diffs.length > 0; - } - - public getInput1LineEdit(): LineEdit | undefined { - if (this.input1Diffs.length === 0) { - return undefined; - } - if (this.input1Diffs.length === 1) { - return this.input1Diffs[0].getLineEdit(); - } else { - throw new Error('Method not implemented.'); - } - } - - public getInput2LineEdit(): LineEdit | undefined { - if (this.input2Diffs.length === 0) { - return undefined; - } - if (this.input2Diffs.length === 1) { - return this.input2Diffs[0].getLineEdit(); - } else { - throw new Error('Method not implemented.'); - } - } -} - export class LineRange { public static hull(ranges: LineRange[]): LineRange | undefined { if (ranges.length === 0) { @@ -240,7 +71,10 @@ export class LineRange { return this.lineCount === 0; } - public intersects(other: LineRange): boolean { + /** + * Returns false if there is at least one line between `this` and `other`. + */ + public touches(other: LineRange): boolean { return ( this.endLineNumberExclusive >= other.startLineNumber && other.endLineNumberExclusive >= this.startLineNumber @@ -303,7 +137,7 @@ export class LineDiff { ); } - public static alignOriginalRegion(lineDiffs: readonly LineDiff[]): LineDiff[] { + public static alignOriginalRange(lineDiffs: readonly LineDiff[]): LineDiff[] { if (lineDiffs.length === 0) { return []; } @@ -344,7 +178,7 @@ export class LineDiff { public conflicts(other: LineDiff): boolean { this.ensureSameOriginalModel(other); - return this.originalRange.intersects(other.originalRange); + return this.originalRange.touches(other.originalRange); } public isStrictBefore(other: LineDiff): boolean { @@ -355,16 +189,14 @@ export class LineDiff { public getLineEdit(): LineEdit { return new LineEdit( this.originalRange, - this.getModifiedLines(), - undefined + this.getModifiedLines() ); } public getReverseLineEdit(): LineEdit { return new LineEdit( this.modifiedRange, - this.getOriginalLines(), - undefined + this.getOriginalLines() ); } @@ -386,34 +218,212 @@ export class LineDiff { } -export class MergeState { +/** + * Describes modifications in input 1 and input 2 for a specific range in base. + * + * The UI offers a mechanism to either apply all changes from input 1 or input 2 or both. + * + * Immutable. +*/ +export class ModifiedBaseRange { + /** + * diffs1 and diffs2 together with the conflict relation form a bipartite graph. + * This method computes strongly connected components of that graph while maintaining the side of each diff. + */ + public static fromDiffs( + originalTextModel: ITextModel, + input1TextModel: ITextModel, + diffs1: readonly LineDiff[], + input2TextModel: ITextModel, + diffs2: readonly LineDiff[] + ): ModifiedBaseRange[] { + const compareByStartLineNumber = compareBy( + (d) => d.originalRange.startLineNumber, + numberComparator + ); + + const queueDiffs1 = new ArrayQueue( + diffs1.slice().sort(compareByStartLineNumber) + ); + const queueDiffs2 = new ArrayQueue( + diffs2.slice().sort(compareByStartLineNumber) + ); + + const result = new Array(); + + while (true) { + const lastDiff1 = queueDiffs1.peekLast(); + const lastDiff2 = queueDiffs2.peekLast(); + + if ( + lastDiff1 && + (!lastDiff2 || + lastDiff1.originalRange.startLineNumber >= + lastDiff2.originalRange.startLineNumber) + ) { + queueDiffs1.removeLast(); + + const otherConflictingWith = + queueDiffs2.takeFromEndWhile((d) => d.conflicts(lastDiff1)) || []; + + const singleLinesDiff = LineDiff.hull(otherConflictingWith); + + const moreConflictingWith = + (singleLinesDiff && + queueDiffs1.takeFromEndWhile((d) => + d.conflicts(singleLinesDiff) + )) || + []; + moreConflictingWith.push(lastDiff1); + + result.push( + new ModifiedBaseRange( + originalTextModel, + input1TextModel, + moreConflictingWith, + queueDiffs1.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, + input2TextModel, + otherConflictingWith, + queueDiffs2.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, + ) + ); + } else if (lastDiff2) { + queueDiffs2.removeLast(); + + const otherConflictingWith = + queueDiffs1.takeFromEndWhile((d) => d.conflicts(lastDiff2)) || []; + + const singleLinesDiff = LineDiff.hull(otherConflictingWith); + + const moreConflictingWith = + (singleLinesDiff && + queueDiffs2.takeFromEndWhile((d) => + d.conflicts(singleLinesDiff) + )) || + []; + moreConflictingWith.push(lastDiff2); + + result.push( + new ModifiedBaseRange( + originalTextModel, + input1TextModel, + otherConflictingWith, + queueDiffs1.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, + input2TextModel, + moreConflictingWith, + queueDiffs2.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, + ) + ); + } else { + break; + } + } + + result.reverse(); + + return result; + } + + private readonly input1FullDiff = LineDiff.hull(this.input1Diffs); + private readonly input2FullDiff = LineDiff.hull(this.input2Diffs); + + public readonly baseRange: LineRange; + public readonly input1Range: LineRange; + public readonly input2Range: LineRange; + + constructor( + public readonly baseTextModel: ITextModel, + public readonly input1TextModel: ITextModel, + public readonly input1Diffs: readonly LineDiff[], + public readonly input1DeltaLineCount: number, + public readonly input2TextModel: ITextModel, + public readonly input2Diffs: readonly LineDiff[], + public readonly input2DeltaLineCount: number, + ) { + if (this.input1Diffs.length === 0 && this.input2Diffs.length === 0) { + throw new BugIndicatingError('must have at least one diff'); + } + + const input1Diff = + this.input1FullDiff || + new LineDiff( + baseTextModel, + this.input2FullDiff!.originalRange, + input1TextModel, + this.input2FullDiff!.originalRange.delta(input1DeltaLineCount) + ); + + const input2Diff = + this.input2FullDiff || + new LineDiff( + baseTextModel, + this.input1FullDiff!.originalRange, + input1TextModel, + this.input1FullDiff!.originalRange.delta(input2DeltaLineCount) + ); + + const results = LineDiff.alignOriginalRange([input1Diff, input2Diff]); + this.baseRange = results[0].originalRange; + this.input1Range = results[0].modifiedRange; + this.input2Range = results[1].modifiedRange; + } + + public get isConflicting(): boolean { + return this.input1Diffs.length > 0 && this.input2Diffs.length > 0; + } + + public getInput1LineEdit(): LineEdit | undefined { + if (this.input1Diffs.length === 0) { + return undefined; + } + //new LineDiff(this.baseTextModel, this.tota) + if (this.input1Diffs.length === 1) { + return this.input1Diffs[0].getLineEdit(); + } else { + throw new Error('Method not implemented.'); + } + } + + public getInput2LineEdit(): LineEdit | undefined { + if (this.input2Diffs.length === 0) { + return undefined; + } + if (this.input2Diffs.length === 1) { + return this.input2Diffs[0].getLineEdit(); + } else { + throw new Error('Method not implemented.'); + } + } +} + +export class ModifiedBaseRangeState { constructor( public readonly input1: boolean, public readonly input2: boolean, public readonly input2First: boolean ) { } - public withInput1(value: boolean): MergeState { - return new MergeState( + public withInput1(value: boolean): ModifiedBaseRangeState { + return new ModifiedBaseRangeState( value, this.input2, value && this.isEmpty ? false : this.input2First ); } - public withInput2(value: boolean): MergeState { - return new MergeState( + public withInput2(value: boolean): ModifiedBaseRangeState { + return new ModifiedBaseRangeState( this.input1, value, value && this.isEmpty ? true : this.input2First ); } - public toggleInput1(): MergeState { + public toggleInput1(): ModifiedBaseRangeState { return this.withInput1(!this.input1); } - public toggleInput2(): MergeState { + public toggleInput2(): ModifiedBaseRangeState { return this.withInput2(!this.input2); } @@ -435,3 +445,31 @@ export class MergeState { return arr.join(','); } } + +export class ReentrancyBarrier { + private isActive = false; + + public runExclusively(fn: () => void): void { + if (this.isActive) { + return; + } + this.isActive = true; + try { + fn(); + } finally { + this.isActive = false; + } + } + + public runExclusivelyOrThrow(fn: () => void): void { + if (this.isActive) { + throw new BugIndicatingError(); + } + this.isActive = true; + try { + fn(); + } finally { + this.isActive = false; + } + } +} From 3601028fd6582dc3483c4d9d3a345879f5d0acc3 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 16 May 2022 16:06:32 +0200 Subject: [PATCH 052/942] Fixes unused imports. --- .../mergeEditor/browser/mergeEditor.ts | 56 +++++++++---------- .../mergeEditor/browser/mergeEditorModel.ts | 2 +- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index b3f0ffcbbab60..541b8995161e5 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -3,44 +3,44 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/mergeEditor'; import { $, Dimension, reset } from 'vs/base/browser/dom'; +import { Direction, Grid, IView, IViewSize, SerializableGrid } from 'vs/base/browser/ui/grid/grid'; +import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview'; +import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; +import { IAction } from 'vs/base/common/actions'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Codicon } from 'vs/base/common/codicons'; +import { Color } from 'vs/base/common/color'; +import { BugIndicatingError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { noBreakWhitespace } from 'vs/base/common/strings'; +import 'vs/css!./media/mergeEditor'; +import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { Range } from 'vs/editor/common/core/range'; +import { ScrollType } from 'vs/editor/common/editorCommon'; +import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { localize } from 'vs/nls'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILabelService } from 'vs/platform/label/common/label'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IEditorControl, IEditorOpenContext } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { BugIndicatingError } from 'vs/base/common/errors'; +import { autorun, derivedObservable, IObservable, ITransaction } from 'vs/workbench/contrib/audioCues/browser/observable'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { Direction, Grid, IView, IViewSize, SerializableGrid } from 'vs/base/browser/ui/grid/grid'; -import { Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview'; -import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ScrollType } from 'vs/editor/common/editorCommon'; +import { ModifiedBaseRangeState, ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/model'; import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; -import { Color } from 'vs/base/common/color'; -import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; -import { localize } from 'vs/nls'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; -import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; -import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IAction } from 'vs/base/common/actions'; -import { Range } from 'vs/editor/common/core/range'; -import { Checkbox, Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { EditorGutterWidget, IGutterItemInfo, IGutterItemView } from './editorGutterWidget'; -import { noBreakWhitespace } from 'vs/base/common/strings'; -import { ModifiedBaseRange, ModifiedBaseRangeState, ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/model'; -import { autorun, derivedObservable, derivedWritebaleObservable, IObservable, ITransaction } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { Codicon } from 'vs/base/common/codicons'; export const ctxIsMergeEditor = new RawContextKey('isMergeEditor', false); export const ctxUsesColumnLayout = new RawContextKey('mergeEditorUsesColumnLayout', false); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts index 989febccdb718..2305e1b80d519 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -10,7 +10,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { ModifiedBaseRange, LineEdit, LineEdits, LineDiff, ModifiedBaseRangeState, LineRange, ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { ModifiedBaseRange, LineEdit, LineDiff, ModifiedBaseRangeState, LineRange, ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/model'; export class MergeEditorModelFactory { constructor( From d8e76554d1d4b36ebc216ce9689cba609660a397 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 16 May 2022 17:08:31 +0200 Subject: [PATCH 053/942] Avoid symbol instantiation. --- .../workbench/contrib/mergeEditor/browser/mergeEditorModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts index 2305e1b80d519..eb8a9f238566b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -65,7 +65,7 @@ export class MergeEditorModelFactory { } } -const InternalSymbol = Symbol(); +const InternalSymbol: unique symbol = null!; export class MergeEditorModel extends EditorModel { private resultEdits = new ResultEdits([], this.base, this.result, this.editorWorkerService); From f5f8a85c8771bfd5987cf03d91079f478c32a8db Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 16 May 2022 17:09:01 +0200 Subject: [PATCH 054/942] support deprecated extensions --- .../abstractExtensionManagementService.ts | 6 ++-- .../common/extensionGalleryService.ts | 23 ++------------ .../common/extensionManagement.ts | 2 +- .../common/unsupportedExtensionsMigration.ts | 11 +++++-- .../extensions/browser/extensionEditor.ts | 2 +- .../extensions/browser/extensionsActions.ts | 30 ++++++++++++++----- .../extensions/browser/extensionsList.ts | 2 +- .../extensions/browser/extensionsWidgets.ts | 5 +++- .../browser/extensionsWorkbenchService.ts | 18 ++++------- .../contrib/extensions/common/extensions.ts | 2 +- .../browser/webExtensionsScannerService.ts | 11 +++---- 11 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 81fa42981c25d..d096eaa5ff732 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -10,6 +10,7 @@ import { CancellationError, getErrorMessage } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; +import { isBoolean } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { @@ -363,8 +364,9 @@ export abstract class AbstractExtensionManagementService extends Disposable impl throw new ExtensionManagementError(nls.localize('malicious extension', "Can't install '{0}' extension since it was reported to be problematic.", extension.identifier.id), ExtensionManagementErrorCode.Malicious); } - if (!!report.unsupportedPreReleaseExtensions && !!report.unsupportedPreReleaseExtensions[extension.identifier.id]) { - throw new ExtensionManagementError(nls.localize('unsupported prerelease extension', "Can't install '{0}' extension because it is no longer supported. It is now part of the '{1}' extension as a pre-release version.", extension.identifier.id, report.unsupportedPreReleaseExtensions[extension.identifier.id].displayName), ExtensionManagementErrorCode.UnsupportedPreRelease); + const deprecated = report.deprecated ? report.deprecated[extension.identifier.id.toLowerCase()] : undefined; + if (deprecated && !isBoolean(deprecated)) { + throw new ExtensionManagementError(nls.localize('unsupported prerelease extension', "Can't install '{0}' extension because it is deprecated. Instead use '{1}' extension.", extension.identifier.id, deprecated.displayName), ExtensionManagementErrorCode.UnsupportedPreRelease); } if (!await this.canInstall(extension)) { diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index eba50f040e429..b75a76752b81d 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -535,11 +535,9 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller }; } -type PreReleaseMigrationInfo = { id: string; displayName: string; migrateStorage?: boolean; engine?: string }; interface IRawExtensionsControlManifest { malicious: string[]; - unsupported?: IStringDictionary; - migrateToPreRelease?: IStringDictionary; + deprecated?: IStringDictionary; } abstract class AbstractExtensionGalleryService implements IExtensionGalleryService { @@ -1150,30 +1148,13 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi const result = await asJson(context); const malicious: IExtensionIdentifier[] = []; - const unsupportedPreReleaseExtensions: IStringDictionary<{ id: string; displayName: string; migrateStorage?: boolean }> = {}; - if (result) { for (const id of result.malicious) { malicious.push({ id }); } - if (result.unsupported) { - for (const extensionId of Object.keys(result.unsupported)) { - const value = result.unsupported[extensionId]; - if (!isBoolean(value)) { - unsupportedPreReleaseExtensions[extensionId.toLowerCase()] = value.preReleaseExtension; - } - } - } - if (result.migrateToPreRelease) { - for (const [unsupportedPreReleaseExtensionId, preReleaseExtensionInfo] of Object.entries(result.migrateToPreRelease)) { - if (!preReleaseExtensionInfo.engine || isEngineValid(preReleaseExtensionInfo.engine, this.productService.version, this.productService.date)) { - unsupportedPreReleaseExtensions[unsupportedPreReleaseExtensionId.toLowerCase()] = preReleaseExtensionInfo; - } - } - } } - return { malicious, unsupportedPreReleaseExtensions }; + return { malicious, deprecated: result?.deprecated }; } } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index b28421b4bf17c..061de6580a902 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -284,7 +284,7 @@ export const enum StatisticType { export interface IExtensionsControlManifest { malicious: IExtensionIdentifier[]; - unsupportedPreReleaseExtensions?: IStringDictionary<{ id: string; displayName: string; migrateStorage?: boolean }>; + deprecated?: IStringDictionary; } export const enum InstallOperation { diff --git a/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts b/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts index 15ea0319efc2f..87eaf1447a6ce 100644 --- a/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts +++ b/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; +import { isBoolean } from 'vs/base/common/types'; import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions, getExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; @@ -20,18 +21,22 @@ import { ILogService } from 'vs/platform/log/common/log'; export async function migrateUnsupportedExtensions(extensionManagementService: IExtensionManagementService, galleryService: IExtensionGalleryService, extensionStorageService: IExtensionStorageService, extensionEnablementService: IGlobalExtensionEnablementService, logService: ILogService): Promise { try { const extensionsControlManifest = await extensionManagementService.getExtensionsControlManifest(); - if (!extensionsControlManifest.unsupportedPreReleaseExtensions) { + if (!extensionsControlManifest.deprecated) { return; } const installed = await extensionManagementService.getInstalled(ExtensionType.User); - for (const [unsupportedExtensionId, { id: preReleaseExtensionId, migrateStorage }] of Object.entries(extensionsControlManifest.unsupportedPreReleaseExtensions)) { + for (const [unsupportedExtensionId, deprecated] of Object.entries(extensionsControlManifest.deprecated)) { + if (isBoolean(deprecated)) { + continue; + } + const { id: preReleaseExtensionId, migrateStorage, preRelease } = deprecated; const unsupportedExtension = installed.find(i => areSameExtensions(i.identifier, { id: unsupportedExtensionId })); // Unsupported Extension is not installed if (!unsupportedExtension) { continue; } - const gallery = (await galleryService.getExtensions([{ id: preReleaseExtensionId, preRelease: true }], { targetPlatform: await extensionManagementService.getTargetPlatform(), compatible: true }, CancellationToken.None))[0]; + const gallery = (await galleryService.getExtensions([{ id: preReleaseExtensionId, preRelease }], { targetPlatform: await extensionManagementService.getTargetPlatform(), compatible: true }, CancellationToken.None))[0]; if (!gallery) { logService.info(`Skipping migrating '${unsupportedExtension.identifier.id}' extension because, the comaptible target '${preReleaseExtensionId}' extension is not found`); continue; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index ba209ca06a909..2f1244e6ebd0e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -658,7 +658,7 @@ export class ExtensionEditor extends EditorPane { } }; reset(template.recommendation); - if (extension.state === ExtensionState.Installed) { + if (extension.deprecated || extension.state === ExtensionState.Installed) { return; } updateRecommendationText(false); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 6e4013f727275..06033ae52a722 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -250,6 +250,7 @@ export abstract class AbstractInstallAction extends ExtensionAction { @IExtensionService private readonly runtimeExtensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, @ILabelService private readonly labelService: ILabelService, + @IDialogService private readonly dialogService: IDialogService, ) { super(id, localize('install', "Install"), cssClass, false); this.update(); @@ -274,6 +275,19 @@ export abstract class AbstractInstallAction extends ExtensionAction { if (!this.extension) { return; } + + if (this.extension.deprecated && isBoolean(this.extension.deprecated)) { + const result = await this.dialogService.confirm({ + type: 'warning', + title: localize('install confirmation', "Are you sure you want to install '{0}'?", this.extension.displayName), + message: localize('deprecated message', "This extension is no longer being maintained and is deprecated."), + primaryButton: localize('install anyway', "Install Anyway"), + }); + if (!result.confirmed) { + return; + } + } + this.extensionsWorkbenchService.open(this.extension, { showPreReleaseVersion: this.installPreReleaseVersion }); alert(localize('installExtensionStart', "Installing extension {0} started. An editor is now open with more details on this extension", this.extension.displayName)); @@ -373,12 +387,13 @@ export class InstallAction extends AbstractInstallAction { @IExtensionService runtimeExtensionService: IExtensionService, @IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService, @ILabelService labelService: ILabelService, + @IDialogService dialogService: IDialogService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IWorkbenchExtensionManagementService private readonly workbenchExtensioManagementService: IWorkbenchExtensionManagementService, @IUserDataSyncEnablementService protected readonly userDataSyncEnablementService: IUserDataSyncEnablementService, ) { super(`extensions.install`, installPreReleaseVersion, InstallAction.Class, - extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService); + extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService, dialogService); this.updateLabel(); this._register(labelService.onDidChangeFormatters(() => this.updateLabel(), this)); this._register(Event.any(userDataSyncEnablementService.onDidChangeEnablement, @@ -438,11 +453,12 @@ export class InstallAndSyncAction extends AbstractInstallAction { @IExtensionService runtimeExtensionService: IExtensionService, @IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService, @ILabelService labelService: ILabelService, + @IDialogService dialogService: IDialogService, @IProductService productService: IProductService, @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService, ) { super('extensions.installAndSync', installPreReleaseVersion, AbstractInstallAction.Class, - extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService); + extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService, dialogService); this.tooltip = localize({ key: 'install everywhere tooltip', comment: ['Placeholder is the name of the product. Eg: Visual Studio Code or Visual Studio Code - Insiders'] }, "Install this extension in all your synced {0} instances", productService.nameLong); this._register(Event.any(userDataSyncEnablementService.onDidChangeEnablement, Event.filter(userDataSyncEnablementService.onDidChangeResourceEnablement, e => e[0] === SyncResource.Extensions))(() => this.update())); @@ -2156,13 +2172,13 @@ export class ExtensionStatusAction extends ExtensionAction { return; } - if (this.extension.isUnsupported) { - if (isBoolean(this.extension.isUnsupported)) { - this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('unsupported tooltip', "This extension no longer supported.")) }, true); + if (this.extension.deprecated) { + if (isBoolean(this.extension.deprecated)) { + this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('unsupported tooltip', "This extension is no longer being maintained and is deprecated.")) }, true); } else { - const link = `[${this.extension.isUnsupported.preReleaseExtension.displayName}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.isUnsupported.preReleaseExtension.id]))}`)})`; + const link = `[${this.extension.deprecated.displayName}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.deprecated.id]))}`)})`; if (this.extension.state !== ExtensionState.Installed) { - this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('unsupported prerelease switch tooltip', "This extension is now part of the {0} extension as a pre-release version.", link)) }, true); + this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('unsupported prerelease switch tooltip', "This extension has been deprecated. Instead use {0}.", link)) }, true); } } return; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 9a31eb974cc7d..4755c67258111 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -188,7 +188,7 @@ export class Renderer implements IPagedRenderer { const updateEnablement = async () => { let isDisabled = false; if (extension.state === ExtensionState.Uninstalled) { - isDisabled = !(await this.extensionsWorkbenchService.canInstall(extension)); + isDisabled = !!extension.deprecated || !(await this.extensionsWorkbenchService.canInstall(extension)); } else if (extension.local && !isLanguagePackExtension(extension.local.manifest)) { const runningExtensions = await this.extensionService.getExtensions(); const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, extension.identifier))[0]; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 9b38a12029690..e4592383fe646 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -185,7 +185,7 @@ export class RecommendationWidget extends ExtensionWidget { render(): void { this.clear(); - if (!this.extension || this.extension.state === ExtensionState.Installed) { + if (!this.extension || this.extension.state === ExtensionState.Installed || this.extension.deprecated) { return; } const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); @@ -549,6 +549,9 @@ export class ExtensionHoverWidget extends ExtensionWidget { if (extension.state === ExtensionState.Installed) { return undefined; } + if (extension.deprecated) { + return undefined; + } const recommendation = this.extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]; if (!recommendation?.reasonText) { return undefined; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 8f96b5e7fcf01..3aacb593829f8 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -188,7 +188,7 @@ export class Extension implements IExtension { } public isMalicious: boolean = false; - public isUnsupported: boolean | { preReleaseExtension: { id: string; displayName: string } } = false; + public deprecated: boolean | { id: string; displayName: string } = false; get installCount(): number | undefined { return this.gallery ? this.gallery.installCount : undefined; @@ -390,17 +390,9 @@ class Extensions extends Disposable { static updateExtensionFromControlManifest(extension: Extension, extensionsControlManifest: IExtensionsControlManifest): void { const isMalicious = extensionsControlManifest.malicious.some(identifier => areSameExtensions(extension.identifier, identifier)); - if (extension.isMalicious !== isMalicious) { - extension.isMalicious = isMalicious; - } - const unsupportedPreRelease = extensionsControlManifest.unsupportedPreReleaseExtensions ? extensionsControlManifest.unsupportedPreReleaseExtensions[extension.identifier.id.toLowerCase()] : undefined; - if (unsupportedPreRelease) { - if (isBoolean(extension.isUnsupported) || !areSameExtensions({ id: extension.isUnsupported.preReleaseExtension.id }, { id: unsupportedPreRelease.id })) { - extension.isUnsupported = { preReleaseExtension: unsupportedPreRelease }; - } - } else if (extension.isUnsupported) { - extension.isUnsupported = false; - } + extension.isMalicious = isMalicious; + const deprecated = extensionsControlManifest.deprecated ? extensionsControlManifest.deprecated[extension.identifier.id.toLowerCase()] : undefined; + extension.deprecated = deprecated ?? false; } private readonly _onChange: Emitter<{ extension: Extension; operation?: InstallOperation } | undefined> = this._register(new Emitter<{ extension: Extension; operation?: InstallOperation } | undefined>()); @@ -1155,7 +1147,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return false; } - if (extension.isUnsupported) { + if (extension.deprecated && !isBoolean(extension.deprecated)) { return false; } diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index b3846d038fc07..23744cd4f9b36 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -78,7 +78,7 @@ export interface IExtension { readonly local?: ILocalExtension; gallery?: IGalleryExtension; readonly isMalicious: boolean; - readonly isUnsupported: boolean | { preReleaseExtension: { id: string; displayName: string } }; + readonly deprecated: boolean | { id: string; displayName: string }; } export const SERVICE_ID = 'extensionsWorkbenchService'; diff --git a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts index 8438e2f337773..aa1eb99bc0e0e 100644 --- a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts @@ -21,7 +21,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls'; import { localize } from 'vs/nls'; import * as semver from 'vs/base/common/semver/semver'; -import { isString } from 'vs/base/common/types'; +import { isBoolean, isString } from 'vs/base/common/types'; import { getErrorMessage } from 'vs/base/common/errors'; import { ResourceMap } from 'vs/base/common/map'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; @@ -140,10 +140,11 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten this.logService.info(`Checking additional builtin extensions: Ignoring '${extension.id}' because it is reported to be malicious.`); continue; } - if (extensionsControlManifest.unsupportedPreReleaseExtensions && extensionsControlManifest.unsupportedPreReleaseExtensions[extension.id.toLowerCase()]) { - const preReleaseExtensionId = extensionsControlManifest.unsupportedPreReleaseExtensions[extension.id.toLowerCase()].id; - this.logService.info(`Checking additional builtin extensions: '${extension.id}' is no longer supported, instead using '${preReleaseExtensionId}'`); - result.push({ id: preReleaseExtensionId, preRelease: true }); + const deprecated = extensionsControlManifest.deprecated ? extensionsControlManifest.deprecated[extension.id.toLowerCase()] : undefined; + if (deprecated && !isBoolean(deprecated)) { + const preReleaseExtensionId = deprecated.id; + this.logService.info(`Checking additional builtin extensions: '${extension.id}' is deprecated, instead using '${preReleaseExtensionId}'`); + result.push({ id: preReleaseExtensionId, preRelease: !!deprecated.preRelease }); } else { result.push(extension); } From d90d07adef925477222c83ae090a233544ab35e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 16 May 2022 17:46:39 +0200 Subject: [PATCH 055/942] wip: policy --- .eslintrc.json | 1 + .../configuration/common/configurations.ts | 5 +- src/vs/platform/policy/common/policy.ts | 6 -- src/vs/platform/policy/common/policyIpc.ts | 73 +++++++++++++++++ .../policy/node/vscode-policy-watcher.d.ts | 30 +++++++ .../policy/node/windowsPolicyService.ts | 79 +++++++++++++++++++ 6 files changed, 187 insertions(+), 7 deletions(-) create mode 100644 src/vs/platform/policy/common/policyIpc.ts create mode 100644 src/vs/platform/policy/node/vscode-policy-watcher.d.ts create mode 100644 src/vs/platform/policy/node/windowsPolicyService.ts diff --git a/.eslintrc.json b/.eslintrc.json index 22157840d8449..da169cdcc0c8e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -248,6 +248,7 @@ "url", "util", "v8-inspect-profiler", + "vscode-policy-watcher", "vscode-proxy-agent", "vscode-regexpp", "vscode-textmate", diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index 353ad2070d347..dad918f94e105 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -109,7 +109,10 @@ export class PolicyConfiguration extends Disposable { } async reload(): Promise { - await this.policyService.refresh(); + if (this.policyService instanceof FilePolicyService) { + await this.policyService.refresh(); + } + this.update(this.defaultConfiguration.configurationModel.keys, false); return this._configurationModel; } diff --git a/src/vs/platform/policy/common/policy.ts b/src/vs/platform/policy/common/policy.ts index 6963bf22081db..4febe66904fe3 100644 --- a/src/vs/platform/policy/common/policy.ts +++ b/src/vs/platform/policy/common/policy.ts @@ -12,14 +12,12 @@ export type Policies = Map; export interface IPolicyService { readonly onDidChange: Event; initialize(): Promise; - refresh(): Promise; getPolicyValue(name: PolicyName): PolicyValue | undefined; } export class NullPolicyService implements IPolicyService { readonly onDidChange = Event.None; async initialize() { } - async refresh() { } getPolicyValue() { return undefined; } } @@ -35,10 +33,6 @@ export class MultiPolicyService implements IPolicyService { await Promise.all(this.policyServices.map(p => p.initialize())); } - async refresh() { - await Promise.all(this.policyServices.map(p => p.refresh())); - } - getPolicyValue(name: PolicyName) { for (const policyService of this.policyServices) { const result = policyService.getPolicyValue(name); diff --git a/src/vs/platform/policy/common/policyIpc.ts b/src/vs/platform/policy/common/policyIpc.ts new file mode 100644 index 0000000000000..00a0913f69306 --- /dev/null +++ b/src/vs/platform/policy/common/policyIpc.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IPolicyService, Policies, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; + +export class PolicyChannel implements IServerChannel { + + private readonly disposables = new DisposableStore(); + + constructor(private service: IPolicyService) { } + + listen(_: unknown, event: string): Event { + switch (event) { + case 'onDidChange': return Event.map( + this.service.onDidChange, + names => new Map( + names + .map(name => [name, this.service.getPolicyValue(name)]) + .filter(pair => pair[1] !== undefined) as [PolicyName, PolicyValue][]), + this.disposables + ); + } + + throw new Error(`Event not found: ${event}`); + } + + call(_: unknown, command: string): Promise { + switch (command) { + case 'initialize': return this.service.initialize(); + } + + throw new Error(`Call not found: ${command}`); + } + + dispose() { + this.disposables.dispose(); + } +} + +export class PolicyChannelClient implements IPolicyService { + + declare readonly _serviceBrand: undefined; + + private policies: Policies = new Map(); + + private readonly _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; + + constructor(private readonly channel: IChannel) { + this.channel.listen('onDidChange')(policies => { + for (const [name, value] of policies) { + if (value === undefined) { + this.policies.delete(name); + } else { + this.policies.set(name, value); + } + } + }); + } + + initialize(): Promise { + return this.channel.call('initialize'); + } + + getPolicyValue(name: PolicyName): PolicyValue | undefined { + return this.policies.get(name); + } +} diff --git a/src/vs/platform/policy/node/vscode-policy-watcher.d.ts b/src/vs/platform/policy/node/vscode-policy-watcher.d.ts new file mode 100644 index 0000000000000..c305f178822e7 --- /dev/null +++ b/src/vs/platform/policy/node/vscode-policy-watcher.d.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode-policy-watcher' { + interface Watcher { + dispose(): void; + } + + interface Policies { + [policyName: string]: { + type: string + }; + } + + interface PolicyValues { + [policyName: string]: string | number | boolean; + } + + function createWatcher( + productName: string, + policies: Policies, + onDidChange: (update: PolicyValues) => void + ): Watcher; + + namespace createWatcher { } + + export = createWatcher; +} diff --git a/src/vs/platform/policy/node/windowsPolicyService.ts b/src/vs/platform/policy/node/windowsPolicyService.ts new file mode 100644 index 0000000000000..64e7b8bb7d612 --- /dev/null +++ b/src/vs/platform/policy/node/windowsPolicyService.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IPolicyService, Policies, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; +import * as createWatcher from 'vscode-policy-watcher'; +import { IStringDictionary } from 'vs/base/common/collections'; + +export class WindowsPolicyService extends Disposable implements IPolicyService { + + private policies: Policies = new Map(); + + private readonly _onDidChange = new Emitter(); + readonly onDidChange = this._onDidChange.event; + + constructor( + @IProductService private readonly productService: IProductService + ) { + super(); + } + + initialize(): Promise { + return new Promise(c => { + if (!this.productService.win32RegValueName) { + return; + } + + const policies: IStringDictionary<{ type: string }> = {}; + const configRegistry = Registry.as(Extensions.Configuration); + + for (const configuration of configRegistry.getConfigurations()) { + if (configuration.properties) { + for (const key in configuration.properties) { + const config = configuration.properties[key]; + const policy = config.policy; + + if (policy) { + if (config.type !== 'string' && config.type !== 'number') { + console.warn(`Policy ${policy.name} has unsupported type ${config.type}`); + continue; + } + + policies[policy.name] = { type: config.type }; + } + } + } + } + + let first = true; + + this._register(createWatcher(this.productService.win32RegValueName, policies, update => () => { + for (const key in update) { + this.policies.set(key, update[key]); + } + + if (first) { + first = false; + c(); + } else { + this._onDidChange.fire(Object.keys(update)); + } + })); + }); + } + + async refresh(): Promise { + // NOOP + } + + getPolicyValue(name: PolicyName): PolicyValue | undefined { + return this.policies.get(name); + } +} From 9a9240dc36f15365efb854a6e6d770eb7d07731b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 16 May 2022 18:40:55 +0200 Subject: [PATCH 056/942] do not support refresh for policies --- .../configuration/common/configurations.ts | 6 ------ .../platform/policy/common/filePolicyService.ts | 10 +++------- src/vs/platform/policy/common/policy.ts | 6 ------ .../configuration/browser/configurationService.ts | 6 ------ .../browser/configurationEditingService.test.ts | 4 +++- .../test/browser/configurationService.test.ts | 15 +++------------ 6 files changed, 9 insertions(+), 38 deletions(-) diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index 353ad2070d347..2dd2290898e74 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -108,12 +108,6 @@ export class PolicyConfiguration extends Disposable { return this._configurationModel; } - async reload(): Promise { - await this.policyService.refresh(); - this.update(this.defaultConfiguration.configurationModel.keys, false); - return this._configurationModel; - } - private onDidChangePolicies(policyNames: readonly PolicyName[]): void { const policyConfigurations = Registry.as(Extensions.Configuration).getPolicyConfigurations(); const keys = coalesce(policyNames.map(policyName => policyConfigurations.get(policyName))); diff --git a/src/vs/platform/policy/common/filePolicyService.ts b/src/vs/platform/policy/common/filePolicyService.ts index 44d3105dad3f0..e3f34bafc3d51 100644 --- a/src/vs/platform/policy/common/filePolicyService.ts +++ b/src/vs/platform/policy/common/filePolicyService.ts @@ -43,15 +43,11 @@ export class FilePolicyService extends Disposable implements IPolicyService { const onDidChangePolicyFile = Event.filter(fileService.onDidFilesChange, e => e.affects(file)); this._register(fileService.watch(file)); - this._register(onDidChangePolicyFile(() => this.throttledDelayer.trigger(() => this.doRefresh()))); + this._register(onDidChangePolicyFile(() => this.throttledDelayer.trigger(() => this.refresh()))); } async initialize(): Promise { - await this.doRefresh(); - } - - async refresh(): Promise { - await this.doRefresh(); + await this.refresh(); } private async read(): Promise { @@ -77,7 +73,7 @@ export class FilePolicyService extends Disposable implements IPolicyService { return policies; } - private async doRefresh(): Promise { + private async refresh(): Promise { const policies = await this.read(); const diff = keysDiff(this.policies, policies); this.policies = policies; diff --git a/src/vs/platform/policy/common/policy.ts b/src/vs/platform/policy/common/policy.ts index 6963bf22081db..4febe66904fe3 100644 --- a/src/vs/platform/policy/common/policy.ts +++ b/src/vs/platform/policy/common/policy.ts @@ -12,14 +12,12 @@ export type Policies = Map; export interface IPolicyService { readonly onDidChange: Event; initialize(): Promise; - refresh(): Promise; getPolicyValue(name: PolicyName): PolicyValue | undefined; } export class NullPolicyService implements IPolicyService { readonly onDidChange = Event.None; async initialize() { } - async refresh() { } getPolicyValue() { return undefined; } } @@ -35,10 +33,6 @@ export class MultiPolicyService implements IPolicyService { await Promise.all(this.policyServices.map(p => p.initialize())); } - async refresh() { - await Promise.all(this.policyServices.map(p => p.refresh())); - } - getPolicyValue(name: PolicyName) { for (const policyService of this.policyServices) { const result = policyService.getPolicyValue(name); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 0db722acbc186..752eec13a2799 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -343,7 +343,6 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat async reloadConfiguration(target?: ConfigurationTarget | IWorkspaceFolder): Promise { if (target === undefined) { this.reloadDefaultConfiguration(); - await this.reloadPolicyConfiguration(); const { local, remote } = await this.reloadUserConfiguration(); await this.reloadWorkspaceConfiguration(); await this.loadConfiguration(local, remote); @@ -358,7 +357,6 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat switch (target) { case ConfigurationTarget.DEFAULT: this.reloadDefaultConfiguration(); - await this.reloadPolicyConfiguration(); return; case ConfigurationTarget.USER: { @@ -603,10 +601,6 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat this.onDefaultConfigurationChanged(this.defaultConfiguration.reload()); } - private async reloadPolicyConfiguration(): Promise { - this.onPolicyConfigurationChanged(await this.policyConfiguration.reload()); - } - private async reloadUserConfiguration(): Promise<{ local: ConfigurationModel; remote: ConfigurationModel }> { const [local, remote] = await Promise.all([this.reloadLocalUserConfiguration(true), this.reloadRemoteUserConfiguration(true)]); return { local, remote }; diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts index 2fba810064f63..4b54d2e7a5d95 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts @@ -6,6 +6,7 @@ import * as sinon from 'sinon'; import * as assert from 'assert'; import * as json from 'vs/base/common/json'; +import { Event } from 'vs/base/common/event'; import { Registry } from 'vs/platform/registry/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -195,8 +196,9 @@ suite('ConfigurationEditingService', () => { }); test('errors cases - ERROR_POLICY_CONFIGURATION', async () => { + const promise = Event.toPromise(instantiationService.get(IConfigurationService).onDidChangeConfiguration); await fileService.writeFile(environmentService.policyFile!, VSBuffer.fromString('{ "configurationEditing.service.policySetting": "policyValue" }')); - await instantiationService.get(IConfigurationService).reloadConfiguration(); + await promise; try { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.policySetting', value: 'value' }, { donotNotifyError: true }); } catch (error) { diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 7f980fd1d1d45..9236183075d59 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -966,10 +966,10 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('policy value override all', async () => { + const promise = Event.toPromise(testObject.onDidChangeConfiguration); await fileService.writeFile(environmentService.policyFile!, VSBuffer.fromString('{ "configurationService.folder.policySetting": "policyValue" }')); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.policySetting": "userValue" }')); - await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.policySetting": "workspaceValue" }')); - await testObject.reloadConfiguration(); + const result = await promise; + assert.deepStrictEqual(result.affectedKeys, ['configurationService.folder.policySetting']); assert.strictEqual(testObject.getValue('configurationService.folder.policySetting'), 'policyValue'); assert.strictEqual(testObject.inspect('configurationService.folder.policySetting').policyValue, 'policyValue'); }); @@ -982,15 +982,6 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.strictEqual(testObject.inspect('configurationService.folder.policySetting').policyValue, undefined); }); - test('policy change should trigger change event ', async () => { - const promise = Event.toPromise(testObject.onDidChangeConfiguration); - await fileService.writeFile(environmentService.policyFile!, VSBuffer.fromString('{ "configurationService.folder.policySetting": "policyValue" }')); - const result = await promise; - assert.deepStrictEqual(result.affectedKeys, ['configurationService.folder.policySetting']); - assert.strictEqual(testObject.getValue('configurationService.folder.policySetting'), 'policyValue'); - assert.strictEqual(testObject.inspect('configurationService.folder.policySetting').policyValue, 'policyValue'); - }); - test('reload configuration emits events after global configuraiton changes', async () => { await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); const target = sinon.spy(); From 4f2785ebd3868d613ad0823890251ec905f6c536 Mon Sep 17 00:00:00 2001 From: Jean Pierre Date: Mon, 16 May 2022 12:17:56 -0500 Subject: [PATCH 057/942] Fixes #147289 (#149594) Co-authored-by: Megan Rogge --- .../workbench/contrib/terminal/browser/terminalGroup.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index 357c121317f3c..20847bea41f97 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -121,12 +121,14 @@ class SplitPaneContainer extends Disposable { } } - getRelativePaneSize(instance: ITerminalInstance): number { + getPaneSize(instance: ITerminalInstance): number { const paneForInstance = this._terminalToPane.get(instance); if (!paneForInstance) { return 0; } - return ((this.orientation === Orientation.HORIZONTAL ? paneForInstance.element.clientWidth : paneForInstance.element.clientHeight) / (this.orientation === Orientation.HORIZONTAL ? this._width : this._height)); + + const index = this._children.indexOf(paneForInstance); + return this._splitView.getViewSize(index); } private _addChild(instance: ITerminalInstance, index: number): void { @@ -337,12 +339,13 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { getLayoutInfo(isActive: boolean): ITerminalTabLayoutInfoById { const instances = this.terminalInstances.filter(instance => typeof instance.persistentProcessId === 'number' && instance.shouldPersist); + const totalSize = instances.map(t => this._splitPaneContainer?.getPaneSize(t) || 0).reduce((total, size) => total += size, 0); return { isActive: isActive, activePersistentProcessId: this.activeInstance ? this.activeInstance.persistentProcessId : undefined, terminals: instances.map(t => { return { - relativeSize: this._splitPaneContainer?.getRelativePaneSize(t) || 0, + relativeSize: totalSize > 0 ? this._splitPaneContainer!.getPaneSize(t) / totalSize : 0, terminal: t.persistentProcessId || 0 }; }) From 729de0a3cfbd87a8094006328010a8db430b9bcd Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 16 May 2022 20:09:23 +0200 Subject: [PATCH 058/942] define HC border colors, also suppress default dashed-outlined --- src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css | 3 ++- src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index fa767a858cb66..35b374ece47d5 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -112,8 +112,9 @@ line-height: 18px; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu:HOVER .quickopen .action-label{ +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu:HOVER .quickopen .action-label { background-color: transparent !important; + outline-color: transparent !important; } .monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen>.action-label.search { diff --git a/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts b/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts index 746ad9031a6c8..a68616a47d8ef 100644 --- a/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts @@ -177,7 +177,7 @@ const activeBackground = colors.registerColor( // border: defaults to active background colors.registerColor( 'titleMenu.border', - { dark: activeBackground, hcDark: activeBackground, light: activeBackground, hcLight: activeBackground }, + { dark: activeBackground, hcDark: colors.inputBorder, light: activeBackground, hcLight: colors.inputBorder }, localize('titleMenu-border', "Border color of the title menu"), false ); From 49828c413e38499f3af65dbae30a9682368ffb0f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 16 May 2022 11:13:50 -0700 Subject: [PATCH 059/942] cleanup --- src/vs/platform/terminal/common/terminal.ts | 3 +-- .../common/xterm/shellIntegrationAddon.ts | 5 ++++- .../terminal/browser/xterm/xtermTerminal.ts | 18 +++++------------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 95527630c529a..dda984c4b5d31 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -247,8 +247,7 @@ export const enum ShellIntegrationTelemetry { ActivationTimeout = 'terminal/shellIntegrationActivationTimeout', FailureProcessExit = 'terminal/shellIntegrationFailureProcessExit', FailureCustomArgs = 'terminal/shellIntegrationActivationFailureCustomArgs', - Success = 'terminal/shellIntegrationActivationSucceeded', - DisabledByUser = 'terminal/shellIntegrationDisabledByUser' + Success = 'terminal/shellIntegrationActivationSucceeded' } export interface IPtyHostController { diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index aa9aca4d9d7fe..0b809d33db49d 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -125,6 +125,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private _terminal?: Terminal; readonly capabilities = new TerminalCapabilityStore(); private _hasUpdatedTelemetry: boolean = false; + private _activationTimeout!: NodeJS.Timeout; constructor( @ILogService private readonly _logService: ILogService, @@ -145,6 +146,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati if (!this._hasUpdatedTelemetry && didHandle) { this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.Success); this._hasUpdatedTelemetry = true; + clearTimeout(this._activationTimeout); } return didHandle; } @@ -225,11 +227,12 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati } private async _ensureCapabilitiesOrAddFailureTelemetry(): Promise { - setTimeout(() => { + this._activationTimeout = setTimeout(() => { if (!this.capabilities.get(TerminalCapability.CommandDetection) && !this.capabilities.get(TerminalCapability.CwdDetection)) { this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.ActivationTimeout); this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); } + this._hasUpdatedTelemetry = true; }, 10000); } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 1384d20c51c1e..7f6faec3f2453 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -13,7 +13,7 @@ import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configur import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { IShellIntegration, ShellIntegrationTelemetry, TerminalLocation, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { IShellIntegration, TerminalLocation, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { ITerminalFont, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { isSafari } from 'vs/base/browser/browser'; import { ICommandTracker, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -60,7 +60,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { // Always on addons private _commandNavigationAddon: CommandNavigationAddon; - private _shellIntegrationAddon: ShellIntegrationAddon | undefined; + private _shellIntegrationAddon: ShellIntegrationAddon; private _decorationAddon: DecorationAddon | undefined; // Optional addons @@ -80,7 +80,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { readonly onDidChangeSelection = this._onDidChangeSelection.event; get commandTracker(): ICommandTracker { return this._commandNavigationAddon; } - get shellIntegration(): IShellIntegration | undefined { return this._shellIntegrationAddon; } + get shellIntegration(): IShellIntegration { return this._shellIntegrationAddon; } private _target: TerminalLocation | undefined; set target(location: TerminalLocation | undefined) { @@ -175,6 +175,8 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { this._updateUnicodeVersion(); this._commandNavigationAddon = this._instantiationService.createInstance(CommandNavigationAddon, _capabilities); this.raw.loadAddon(this._commandNavigationAddon); + this._shellIntegrationAddon = new ShellIntegrationAddon(this._logService, this._telemetryService); + this.raw.loadAddon(this._shellIntegrationAddon); this._updateShellIntegrationAddons(); } @@ -610,9 +612,6 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { const shellIntegrationEnabled = this._configurationService.getValue(TerminalSettingId.ShellIntegrationEnabled); const decorationsEnabled = this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationsEnabled); if (shellIntegrationEnabled) { - if (!this._shellIntegrationAddon) { - this._createShellIntegrationAddon(); - } if (decorationsEnabled && !this._decorationAddon) { this._createDecorationAddon(); } else if (this._decorationAddon && !decorationsEnabled) { @@ -624,14 +623,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { if (this._decorationAddon) { this._decorationAddon.dispose(); this._decorationAddon = undefined; - this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.DisabledByUser - ); } } } - - private _createShellIntegrationAddon(): void { - this._shellIntegrationAddon = new ShellIntegrationAddon(this._logService, this._telemetryService); - this.raw.loadAddon(this._shellIntegrationAddon); - } } From a3164a321965b82191bad2bc406b74264fbd8c9b Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 16 May 2022 11:16:18 -0700 Subject: [PATCH 060/942] Move to new bot name (#149658) --- .github/workflows/deep-classifier-assign-monitor.yml | 2 +- .github/workflows/deep-classifier-unassign-monitor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deep-classifier-assign-monitor.yml b/.github/workflows/deep-classifier-assign-monitor.yml index 9d74e30847222..97e375694e6c8 100644 --- a/.github/workflows/deep-classifier-assign-monitor.yml +++ b/.github/workflows/deep-classifier-assign-monitor.yml @@ -19,6 +19,6 @@ jobs: - name: "Run Classifier: Monitor" uses: ./actions/classifier-deep/monitor with: - botName: vscode-triage-bot + botName: VSCodeTriageBot token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} diff --git a/.github/workflows/deep-classifier-unassign-monitor.yml b/.github/workflows/deep-classifier-unassign-monitor.yml index 35505a4015ef9..6e9a2b136218f 100644 --- a/.github/workflows/deep-classifier-unassign-monitor.yml +++ b/.github/workflows/deep-classifier-unassign-monitor.yml @@ -19,6 +19,6 @@ jobs: - name: "Run Classifier: Monitor" uses: ./actions/classifier-deep/monitor with: - botName: vscode-triage-bot + botName: VSCodeTriageBot token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} From cfbf3d5dd789dc90f222843970a8b9ab87c3045d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 16 May 2022 11:17:36 -0700 Subject: [PATCH 061/942] Refine notebookEditor proposal (#149656) For #149271 - `NotebookEditor.document` -> `NotebookEditor.notebook` - Add `selection` to for setting primary selections. Matches `TextDocument.selection` - Deprecate `showNotebookDocument` --- .../api/common/extHostNotebookEditor.ts | 9 +++++++ .../vscode.proposed.notebookEditor.d.ts | 26 ++++++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebookEditor.ts b/src/vs/workbench/api/common/extHostNotebookEditor.ts index 3b1a53197b341..b18e816f18148 100644 --- a/src/vs/workbench/api/common/extHostNotebookEditor.ts +++ b/src/vs/workbench/api/common/extHostNotebookEditor.ts @@ -105,6 +105,15 @@ export class ExtHostNotebookEditor { get document() { return that.notebookData.apiNotebook; }, + get notebook() { + return that.notebookData.apiNotebook; + }, + get selection() { + return that._selections[0]; + }, + set selection(selection: vscode.NotebookRange) { + this.selections = [selection]; + }, get selections() { return that._selections; }, diff --git a/src/vscode-dts/vscode.proposed.notebookEditor.d.ts b/src/vscode-dts/vscode.proposed.notebookEditor.d.ts index 5ed457eedbd3e..fd1d91db119af 100644 --- a/src/vscode-dts/vscode.proposed.notebookEditor.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookEditor.d.ts @@ -39,11 +39,23 @@ declare module 'vscode' { export interface NotebookEditor { /** * The document associated with this notebook editor. + * + * @deprecated Use {@linkcode NotebookEditor.notebook} instead. */ readonly document: NotebookDocument; /** - * The selections on this notebook editor. + * The {@link NotebookDocument notebook document} associated with this notebook editor. + */ + readonly notebook: NotebookDocument; + + /** + * The primary selection in this notebook editor. + */ + selection: NotebookRange; + + /** + * All selections in this notebook editor. * * The primary selection (or focused range) is `selections[0]`. When the document has no cells, the primary selection is empty `{ start: 0, end: 0 }`; */ @@ -54,6 +66,11 @@ declare module 'vscode' { */ readonly visibleRanges: readonly NotebookRange[]; + /** + * The column in which this editor shows. + */ + readonly viewColumn?: ViewColumn; + /** * Scroll as indicated by `revealType` in order to reveal the given range. * @@ -61,11 +78,6 @@ declare module 'vscode' { * @param revealType The scrolling strategy for revealing `range`. */ revealRange(range: NotebookRange, revealType?: NotebookEditorRevealType): void; - - /** - * The column in which this editor shows. - */ - readonly viewColumn?: ViewColumn; } /** @@ -180,6 +192,8 @@ declare module 'vscode' { /** * A short-hand for `openNotebookDocument(uri).then(document => showNotebookDocument(document, options))`. * + * @deprecated Will not be finalized. + * * @see {@link workspace.openNotebookDocument} * * @param uri The resource to open. From 64030273a62b9075b3369eaf0da6cd68251a3215 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 16 May 2022 11:24:05 -0700 Subject: [PATCH 062/942] remove main thread proxy kenrel --- .../api/browser/extensionHost.contribution.ts | 1 - src/vs/workbench/api/common/extHost.protocol.ts | 12 ------------ 2 files changed, 13 deletions(-) diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 2fa247a959b1d..37afee52e07b7 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -64,7 +64,6 @@ import './mainThreadWorkspace'; import './mainThreadComments'; import './mainThreadNotebook'; import './mainThreadNotebookKernels'; -import './mainThreadNotebookProxyKernels'; import './mainThreadNotebookDocumentsAndEditors'; import './mainThreadNotebookRenderers'; import './mainThreadInteractive'; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 25f33cb3f4a5a..607bc5ed01dba 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1035,12 +1035,6 @@ export interface MainThreadNotebookKernelsShape extends IDisposable { $completeExecution(handle: number, data: SerializableObjectWithBuffers): void; } -export interface MainThreadNotebookProxyKernelsShape extends IDisposable { - $addProxyKernel(handle: number, data: INotebookProxyKernelDto): Promise; - $updateProxyKernel(handle: number, data: Partial): void; - $removeProxyKernel(handle: number): void; -} - export interface MainThreadNotebookRenderersShape extends IDisposable { $postMessage(editorId: string | undefined, rendererId: string, message: unknown): Promise; } @@ -2129,10 +2123,6 @@ export interface ExtHostNotebookKernelsShape { $cellExecutionChanged(uri: UriComponents, cellHandle: number, state: notebookCommon.NotebookCellExecutionState | undefined): void; } -export interface ExtHostNotebookProxyKernelsShape { - $resolveKernel(handle: number): Promise; -} - export interface ExtHostInteractiveShape { $willAddInteractiveDocument(uri: UriComponents, eol: string, languageId: string, notebookUri: UriComponents): void; $willRemoveInteractiveDocument(uri: UriComponents, notebookUri: UriComponents): void; @@ -2309,7 +2299,6 @@ export const MainContext = { MainThreadNotebookDocuments: createProxyIdentifier('MainThreadNotebookDocumentsShape'), MainThreadNotebookEditors: createProxyIdentifier('MainThreadNotebookEditorsShape'), MainThreadNotebookKernels: createProxyIdentifier('MainThreadNotebookKernels'), - MainThreadNotebookProxyKernels: createProxyIdentifier('MainThreadNotebookProxyKernels'), MainThreadNotebookRenderers: createProxyIdentifier('MainThreadNotebookRenderers'), MainThreadInteractive: createProxyIdentifier('MainThreadInteractive'), MainThreadTheming: createProxyIdentifier('MainThreadTheming'), @@ -2362,7 +2351,6 @@ export const ExtHostContext = { ExtHostNotebookDocuments: createProxyIdentifier('ExtHostNotebookDocuments'), ExtHostNotebookEditors: createProxyIdentifier('ExtHostNotebookEditors'), ExtHostNotebookKernels: createProxyIdentifier('ExtHostNotebookKernels'), - ExtHostNotebookProxyKernels: createProxyIdentifier('ExtHostNotebookProxyKernels'), ExtHostNotebookRenderers: createProxyIdentifier('ExtHostNotebookRenderers'), ExtHostInteractive: createProxyIdentifier('ExtHostInteractive'), ExtHostTheming: createProxyIdentifier('ExtHostTheming'), From 3f531a7de33e8a1030b5ad2f13abc1c31b050fdf Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 16 May 2022 11:25:14 -0700 Subject: [PATCH 063/942] Add notebookWorkspaceEdit api proposal (#149128) * Add notebookWorkspaceEdit api proposal Splits a new `notebookWorkspaceEdit` out of the existing `notebookEditorEdit` proposal. The notebookWorkspaceEdit reflects the api that we believe should be finalized instead of `notebookEditorEdit`. It lets extensions use workspaceedits to change cells in a notebook or replace the metadata for a notebook document As part of this change, I've also marked all of the `notebookEditorEdit` apis as deprecated (except for `replaceNotebookMetadata` which exists in the new proposal too) * Export type from extHost --- .../workbench/api/common/extHost.api.impl.ts | 1 + src/vs/workbench/api/common/extHostTypes.ts | 51 +++++++++++++- .../common/extensionsApiProposals.ts | 1 + .../vscode.proposed.notebookEditorEdit.d.ts | 39 ++++++----- ...vscode.proposed.notebookWorkspaceEdit.d.ts | 70 +++++++++++++++++++ 5 files changed, 141 insertions(+), 21 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 366db99920d7c..c2ad7565594ce 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1321,6 +1321,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem, NotebookCellStatusBarItem: extHostTypes.NotebookCellStatusBarItem, NotebookControllerAffinity: extHostTypes.NotebookControllerAffinity, + NotebookEdit: extHostTypes.NotebookEdit, PortAttributes: extHostTypes.PortAttributes, LinkedEditingRanges: extHostTypes.LinkedEditingRanges, TestResultState: extHostTypes.TestResultState, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 977f0cdf1423b..5976d7d182e17 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -598,6 +598,43 @@ export class TextEdit { } } +@es5ClassCompat +export class NotebookEdit implements vscode.NotebookEdit { + + static isNotebookCellEdit(thing: any): thing is NotebookEdit { + if (thing instanceof NotebookEdit) { + return true; + } + if (!thing) { + return false; + } + return NotebookRange.isNotebookRange((thing)) + && Array.isArray((thing).newCells); + } + + static replaceCells(range: NotebookRange, newCells: NotebookCellData[]): NotebookEdit { + return new NotebookEdit(range, newCells); + } + + static deleteCells(range: NotebookRange): NotebookEdit { + return new NotebookEdit(range, []); + } + + static updateCellMetadata(index: number, newMetadata: { [key: string]: any }): NotebookEdit { + return new NotebookEdit(new NotebookRange(index, index), [], newMetadata); + } + + readonly range: NotebookRange; + readonly newCells: NotebookCellData[]; + readonly newCellMetadata?: { [key: string]: any }; + + constructor(range: NotebookRange, newCells: NotebookCellData[], newCellMetadata?: { [key: string]: any }) { + this.range = range; + this.newCells = newCells; + this.newCellMetadata = newCellMetadata; + } +} + export class SnippetTextEdit implements vscode.SnippetTextEdit { range: vscode.Range; @@ -741,7 +778,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { return this._edits.some(edit => edit._type === FileEditType.Text && edit.uri.toString() === uri.toString()); } - set(uri: URI, edits: TextEdit[]): void { + set(uri: URI, edits: TextEdit[] | unknown): void { if (!edits) { // remove all text edits for `uri` for (let i = 0; i < this._edits.length; i++) { @@ -753,9 +790,17 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { coalesceInPlace(this._edits); } else { // append edit to the end - for (const edit of edits) { + for (const edit of edits as TextEdit[] | NotebookEdit[]) { if (edit) { - this._edits.push({ _type: FileEditType.Text, uri, edit }); + if (NotebookEdit.isNotebookCellEdit(edit)) { + if (edit.newCellMetadata) { + this.replaceNotebookCellMetadata(uri, edit.range.start, edit.newCellMetadata); + } else { + this.replaceNotebookCells(uri, edit.range, edit.newCells); + } + } else { + this._edits.push({ _type: FileEditType.Text, uri, edit }); + } } } } diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 310cf6d7745ba..c562da5671fc5 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -42,6 +42,7 @@ export const allApiProposals = Object.freeze({ notebookMessaging: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts', notebookMime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMime.d.ts', notebookProxyController: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts', + notebookWorkspaceEdit: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts', portsAttributes: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.portsAttributes.d.ts', quickPickSortByLabel: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts', resolvers: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.resolvers.d.ts', diff --git a/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts b/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts index 6c954b5f76cc7..b41878fd319b0 100644 --- a/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts @@ -7,32 +7,34 @@ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/106744 - // todo@API add NotebookEdit-type which handles all these cases? - // export class NotebookEdit { - // range: NotebookRange; - // newCells: NotebookCellData[]; - // newMetadata?: NotebookDocumentMetadata; - // constructor(range: NotebookRange, newCells: NotebookCellData) - // } - - // export class NotebookCellEdit { - // newMetadata?: NotebookCellMetadata; - // } - - // export interface WorkspaceEdit { - // set(uri: Uri, edits: TextEdit[] | NotebookEdit[]): void - // } - export interface WorkspaceEdit { - // todo@API add NotebookEdit-type which handles all these cases? replaceNotebookMetadata(uri: Uri, value: { [key: string]: any }): void; + + /** + * @deprecated Please migrate to the new `notebookWorkspaceEdit` proposed API. + */ replaceNotebookCells(uri: Uri, range: NotebookRange, cells: NotebookCellData[], metadata?: WorkspaceEditEntryMetadata): void; + + /** + * @deprecated Please migrate to the new `notebookWorkspaceEdit` proposed API. + */ replaceNotebookCellMetadata(uri: Uri, index: number, cellMetadata: { [key: string]: any }, metadata?: WorkspaceEditEntryMetadata): void; } export interface NotebookEditorEdit { + /** + * @deprecated Please migrate to the new `notebookWorkspaceEdit` proposed API. + */ replaceMetadata(value: { [key: string]: any }): void; + + /** + * @deprecated Please migrate to the new `notebookWorkspaceEdit` proposed API. + */ replaceCells(start: number, end: number, cells: NotebookCellData[]): void; + + /** + * @deprecated Please migrate to the new `notebookWorkspaceEdit` proposed API. + */ replaceCellMetadata(index: number, metadata: { [key: string]: any }): void; } @@ -44,10 +46,11 @@ declare module 'vscode' { * be used to make edits. Note that the edit-builder is only valid while the * callback executes. * + * @deprecated Please migrate to the new `notebookWorkspaceEdit` proposed API. + * * @param callback A function which can create edits using an {@link NotebookEditorEdit edit-builder}. * @return A promise that resolves with a value indicating if the edits could be applied. */ - // @jrieken REMOVE maybe edit(callback: (editBuilder: NotebookEditorEdit) => void): Thenable; } } diff --git a/src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts b/src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts new file mode 100644 index 0000000000000..01330ec9cdd1f --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/106744 + + /** + * A notebook edit represents edits that should be applied to the contents of a notebook. + */ + export class NotebookEdit { + + /** + * Utility to create a edit that replaces cells in a notebook. + * + * @param range The range of cells to replace + * @param newCells The new notebook cells. + */ + static replaceCells(range: NotebookRange, newCells: NotebookCellData[]): NotebookEdit; + + /** + * Utility to create a edit that deletes cells in a notebook. + * + * @param range The range of cells to delete. + */ + static deleteCells(range: NotebookRange): NotebookEdit; + + /** + * Utility to update a cells metadata. + * + * @param index The index of the cell to update. + * @param newMetadata The new metadata for the cell. + */ + static updateCellMetadata(index: number, newMetadata: { [key: string]: any }): NotebookEdit; + + /** + * Range of the cells being edited + */ + readonly range: NotebookRange; + + /** + * New cells being inserted. May be empty. + */ + readonly newCells: NotebookCellData[]; + + /** + * Optional new metadata for the cells. + */ + readonly newCellMetadata?: { [key: string]: any }; + + constructor(range: NotebookRange, newCells: NotebookCellData[], newCellMetadata?: { [key: string]: any }); + } + + export interface WorkspaceEdit { + /** + * Replaces the metadata for a notebook document. + */ + replaceNotebookMetadata(uri: Uri, value: { [key: string]: any }): void; + + /** + * Set (and replace) edits for a resource. + * + * @param uri A resource identifier. + * @param edits An array of text or notebook edits. + */ + set(uri: Uri, edits: TextEdit[] | NotebookEdit[]): void; + } +} From 101f6fb31dc62d02f746e0601175dc88b594b2f9 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 16 May 2022 11:52:27 -0700 Subject: [PATCH 064/942] Enable enabling js/ts.implicitProjectConfig.checkJs by default (#149660) Fixes #149659 --- extensions/typescript-language-features/package.json | 2 +- .../typescript-language-features/src/utils/configuration.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 932626b6a124e..e5ca635d50cd9 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -686,7 +686,7 @@ }, "js/ts.implicitProjectConfig.checkJs": { "type": "boolean", - "default": false, + "default": true, "markdownDescription": "%configuration.implicitProjectConfig.checkJs%", "scope": "window" }, diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index 3085fd074ceda..34355b5e3e5ab 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -82,7 +82,7 @@ export class ImplicitProjectConfiguration { private static readCheckJs(configuration: vscode.WorkspaceConfiguration): boolean { return configuration.get('js/ts.implicitProjectConfig.checkJs') - ?? configuration.get('javascript.implicitProjectConfig.checkJs', false); + ?? configuration.get('javascript.implicitProjectConfig.checkJs', true); } private static readExperimentalDecorators(configuration: vscode.WorkspaceConfiguration): boolean { From 4f741d6370be488aa3d68d7eda76376a962ce885 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 16 May 2022 21:44:12 +0200 Subject: [PATCH 065/942] add ability to provide detail and description for `input1` and `input2`. This isn't wire up yet because it requires bigger changes in git-land --- .../browser/mergeEditor.contribution.ts | 90 ++++++++++++++----- .../mergeEditor/browser/mergeEditor.ts | 4 +- .../mergeEditor/browser/mergeEditorInput.ts | 28 ++++-- .../mergeEditor/browser/mergeEditorModel.ts | 14 ++- .../browser/mergeEditorSerializer.ts | 10 ++- 5 files changed, 111 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts index 3f72bfca52422..b4495ccc657ce 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -13,7 +13,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor'; import { ctxIsMergeEditor, ctxUsesColumnLayout, MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditor'; -import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; +import { MergeEditorInput, MergeEditorInputData } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { MergeEditorSerializer } from './mergeEditorSerializer'; import { Codicon } from 'vs/base/common/codicons'; @@ -71,38 +71,84 @@ registerAction2(class Open extends Action2 { }); } run(accessor: ServicesAccessor, ...args: any[]): void { - const validatedArgs = ITestMergeEditorArgs.validate(args[0]); - - function normalize(uri: URI | UriComponents | string): URI { - if (typeof uri === 'string') { - return URI.parse(uri); - } else { - return URI.revive(uri); - } - } + const validatedArgs = IRelaxedOpenArgs.validate(args[0]); const instaService = accessor.get(IInstantiationService); const input = instaService.createInstance( MergeEditorInput, - normalize(validatedArgs.ancestor), - normalize(validatedArgs.input1), - normalize(validatedArgs.input2), - normalize(validatedArgs.output), + validatedArgs.ancestor, + validatedArgs.input1, + validatedArgs.input2, + validatedArgs.output, ); accessor.get(IEditorService).openEditor(input); } }); -namespace ITestMergeEditorArgs { - export function validate(args: any): ITestMergeEditorArgs { - return args as ITestMergeEditorArgs; +namespace IRelaxedOpenArgs { + function toUri(obj: unknown): URI { + if (typeof obj === 'string') { + return URI.parse(obj, true); + } else if (obj && typeof obj === 'object') { + return URI.revive(obj); + } + throw new TypeError('invalid argument'); + } + + function isUriComponents(obj: unknown): obj is UriComponents { + if (!obj || typeof obj !== 'object') { + return false; + } + return typeof (obj).scheme === 'string' + && typeof (obj).authority === 'string' + && typeof (obj).path === 'string' + && typeof (obj).query === 'string' + && typeof (obj).fragment === 'string'; + } + + function toInputResource(obj: unknown): MergeEditorInputData { + if (typeof obj === 'string') { + return new MergeEditorInputData(URI.parse(obj, true), undefined, undefined); + } + if (!obj || typeof obj !== 'object') { + throw new TypeError('invalid argument'); + } + + if (isUriComponents(obj)) { + return new MergeEditorInputData(URI.revive(obj), undefined, undefined); + } + + let uri = toUri((obj).uri); + let detail = (obj).detail; + let description = (obj).description; + return new MergeEditorInputData(uri, detail, description); + } + + export function validate(obj: unknown): IOpenEditorArgs { + if (!obj || typeof obj !== 'object') { + throw new TypeError('invalid argument'); + } + const ancestor = toUri((obj).ancestor); + const output = toUri((obj).output); + const input1 = toInputResource((obj).input1); + const input2 = toInputResource((obj).input2); + return { ancestor, input1, input2, output }; } } -interface ITestMergeEditorArgs { - ancestor: URI | string; - input1: URI | string; - input2: URI | string; - output: URI | string; +type IRelaxedInputData = { uri: UriComponents; detail?: string; description?: string }; + +type IRelaxedOpenArgs = { + ancestor: UriComponents | string; + input1: IRelaxedInputData | string; + input2: IRelaxedInputData | string; + output: UriComponents | string; +}; + +interface IOpenEditorArgs { + ancestor: URI; + input1: MergeEditorInputData; + input2: MergeEditorInputData; + output: URI; } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 541b8995161e5..bb5cb6334e694 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -170,8 +170,8 @@ export class MergeEditor extends EditorPane { this._sessionDisposables.clear(); const model = await input.resolve(); - this.input1View.setModel(model.input1, localize('yours', 'Yours'), undefined); - this.input2View.setModel(model.input2, localize('theirs', 'Theirs',), undefined); + this.input1View.setModel(model.input1, localize('yours', 'Yours'), model.input1Detail); + this.input2View.setModel(model.input2, localize('theirs', 'Theirs',), model.input2Detail); this.inputResultView.setModel(model.result, localize('result', 'Result',), this._labelService.getUriLabel(model.result.uri, { relative: true })); let input1Decorations = new Array(); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts index da2ada49ee0cc..f6b69488351d6 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts @@ -20,11 +20,19 @@ import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/te export interface MergeEditorInputJSON { anchestor: URI; - inputOne: URI; - inputTwo: URI; + inputOne: { uri: URI; detail?: string; description?: string }; + inputTwo: { uri: URI; detail?: string; description?: string }; result: URI; } +export class MergeEditorInputData { + constructor( + readonly uri: URI, + readonly detail: string | undefined, + readonly description: string | undefined, + ) { } +} + export class MergeEditorInput extends AbstractTextResourceEditorInput { static readonly ID = 'mergeEditor.Input'; @@ -35,8 +43,8 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { constructor( private readonly _anchestor: URI, - private readonly _input1: URI, - private readonly _input2: URI, + private readonly _input1: MergeEditorInputData, + private readonly _input2: MergeEditorInputData, private readonly _result: URI, @IInstantiationService private readonly _instaService: IInstantiationService, @ITextModelService private readonly _textModelService: ITextModelService, @@ -94,14 +102,18 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { if (!this._model) { const anchestor = await this._textModelService.createModelReference(this._anchestor); - const input1 = await this._textModelService.createModelReference(this._input1); - const input2 = await this._textModelService.createModelReference(this._input2); + const input1 = await this._textModelService.createModelReference(this._input1.uri); + const input2 = await this._textModelService.createModelReference(this._input2.uri); const result = await this._textModelService.createModelReference(this._result); this._model = await this.mergeEditorModelFactory.create( anchestor.object.textEditorModel, input1.object.textEditorModel, + this._input1.detail, + this._input1.description, input2.object.textEditorModel, + this._input2.detail, + this._input2.description, result.object.textEditorModel ); @@ -121,8 +133,8 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { return false; } return isEqual(this._anchestor, otherInput._anchestor) - && isEqual(this._input1, otherInput._input1) - && isEqual(this._input2, otherInput._input2) + && isEqual(this._input1.uri, otherInput._input1.uri) + && isEqual(this._input2.uri, otherInput._input2.uri) && isEqual(this._result, otherInput._result); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts index eb8a9f238566b..ef5003afc4c8f 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -21,7 +21,11 @@ export class MergeEditorModelFactory { public async create( base: ITextModel, input1: ITextModel, + input1Detail: string | undefined, + input1Description: string | undefined, input2: ITextModel, + input2Detail: string | undefined, + input2Description: string | undefined, result: ITextModel, ): Promise { @@ -56,7 +60,11 @@ export class MergeEditorModelFactory { InternalSymbol, base, input1, + input1Detail, + input1Description, input2, + input2Detail, + input2Description, result, changesInput1, changesInput2, @@ -71,10 +79,14 @@ export class MergeEditorModel extends EditorModel { private resultEdits = new ResultEdits([], this.base, this.result, this.editorWorkerService); constructor( - symbol: typeof InternalSymbol, + _symbol: typeof InternalSymbol, readonly base: ITextModel, readonly input1: ITextModel, + readonly input1Detail: string | undefined, + readonly input1Description: string | undefined, readonly input2: ITextModel, + readonly input2Detail: string | undefined, + readonly input2Description: string | undefined, readonly result: ITextModel, private readonly inputOneLinesDiffs: readonly LineDiff[], private readonly inputTwoLinesDiffs: readonly LineDiff[], diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorSerializer.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorSerializer.ts index 2480f0950ba0f..d4e52947bb83d 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorSerializer.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorSerializer.ts @@ -7,7 +7,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { parse } from 'vs/base/common/marshalling'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorSerializer } from 'vs/workbench/common/editor'; -import { MergeEditorInput, MergeEditorInputJSON } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; +import { MergeEditorInput, MergeEditorInputJSON, MergeEditorInputData } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; export class MergeEditorSerializer implements IEditorSerializer { @@ -22,7 +22,13 @@ export class MergeEditorSerializer implements IEditorSerializer { deserialize(instantiationService: IInstantiationService, raw: string): MergeEditorInput | undefined { try { const data = parse(raw); - return instantiationService.createInstance(MergeEditorInput, data.anchestor, data.inputOne, data.inputTwo, data.result); + return instantiationService.createInstance( + MergeEditorInput, + data.anchestor, + new MergeEditorInputData(data.inputOne.uri, data.inputOne.detail, data.inputOne.description), + new MergeEditorInputData(data.inputTwo.uri, data.inputTwo.detail, data.inputTwo.description), + data.result + ); } catch (err) { onUnexpectedError(err); return undefined; From bf7dc6646f063898682b30ab48c42d1c2961f697 Mon Sep 17 00:00:00 2001 From: Yuki Ito Date: Thu, 24 Mar 2022 13:45:23 +0000 Subject: [PATCH 066/942] Use maxTokenizationLineLength in monarch --- .../standalone/browser/standaloneLanguages.ts | 5 +- .../standalone/common/monarch/monarchLexer.ts | 19 ++++++- .../standalone/test/browser/monarch.test.ts | 57 ++++++++++++++++--- 3 files changed, 68 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index f738b97ed24a3..decc60790f513 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -23,6 +23,7 @@ import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneT import { IMarkerData, IMarkerService } from 'vs/platform/markers/common/markers'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { LanguageSelector } from 'vs/editor/common/languageSelector'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; /** * Register information about a new language. @@ -374,7 +375,7 @@ export function registerTokensProviderFactory(languageId: string, factory: Token if (isATokensProvider(result)) { return createTokenizationSupportAdapter(languageId, result); } - return new MonarchTokenizer(StandaloneServices.get(ILanguageService), StandaloneServices.get(IStandaloneThemeService), languageId, compile(languageId, result)); + return new MonarchTokenizer(StandaloneServices.get(ILanguageService), StandaloneServices.get(IStandaloneThemeService), languageId, compile(languageId, result), StandaloneServices.get(IConfigurationService)); } }; return languages.TokenizationRegistry.registerFactory(languageId, adaptedFactory); @@ -405,7 +406,7 @@ export function setTokensProvider(languageId: string, provider: TokensProvider | */ export function setMonarchTokensProvider(languageId: string, languageDef: IMonarchLanguage | Thenable): IDisposable { const create = (languageDef: IMonarchLanguage) => { - return new MonarchTokenizer(StandaloneServices.get(ILanguageService), StandaloneServices.get(IStandaloneThemeService), languageId, compile(languageId, languageDef)); + return new MonarchTokenizer(StandaloneServices.get(ILanguageService), StandaloneServices.get(IStandaloneThemeService), languageId, compile(languageId, languageDef), StandaloneServices.get(IConfigurationService)); }; if (isThenable(languageDef)) { return registerTokensProviderFactory(languageId, { create: () => languageDef }); diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts index bdc490fdb41de..dde538afac904 100644 --- a/src/vs/editor/standalone/common/monarch/monarchLexer.ts +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -10,11 +10,12 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import * as languages from 'vs/editor/common/languages'; -import { NullState } from 'vs/editor/common/languages/nullTokenize'; +import { NullState, nullTokenizeEncoded } from 'vs/editor/common/languages/nullTokenize'; import { TokenTheme } from 'vs/editor/common/languages/supports/tokenization'; import { ILanguageService } from 'vs/editor/common/languages/language'; import * as monarchCommon from 'vs/editor/standalone/common/monarch/monarchCommon'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneTheme'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; const CACHE_STACK_DEPTH = 5; @@ -394,8 +395,9 @@ export class MonarchTokenizer implements languages.ITokenizationSupport { private readonly _embeddedLanguages: { [languageId: string]: boolean }; public embeddedLoaded: Promise; private readonly _tokenizationRegistryListener: IDisposable; + private _maxTokenizationLineLength: number; - constructor(languageService: ILanguageService, standaloneThemeService: IStandaloneThemeService, languageId: string, lexer: monarchCommon.ILexer) { + constructor(languageService: ILanguageService, standaloneThemeService: IStandaloneThemeService, languageId: string, lexer: monarchCommon.ILexer, @IConfigurationService private readonly _configurationService: IConfigurationService) { this._languageService = languageService; this._standaloneThemeService = standaloneThemeService; this._languageId = languageId; @@ -423,6 +425,16 @@ export class MonarchTokenizer implements languages.ITokenizationSupport { emitting = false; } }); + this._maxTokenizationLineLength = this._configurationService.getValue('editor.maxTokenizationLineLength', { + overrideIdentifier: this._languageId + }); + this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('editor.maxTokenizationLineLength')) { + this._maxTokenizationLineLength = this._configurationService.getValue('editor.maxTokenizationLineLength', { + overrideIdentifier: this._languageId + }); + } + }); } public dispose(): void { @@ -473,6 +485,9 @@ export class MonarchTokenizer implements languages.ITokenizationSupport { } public tokenizeEncoded(line: string, hasEOL: boolean, lineState: languages.IState): languages.EncodedTokenizationResult { + if (line.length >= this._maxTokenizationLineLength) { + return nullTokenizeEncoded(this._languageService.languageIdCodec.encodeLanguageId(this._languageId), lineState); + } const tokensCollector = new MonarchModernTokensCollector(this._languageService, this._standaloneThemeService.getColorTheme().tokenTheme); const endLineState = this._tokenize(line, hasEOL, lineState, tokensCollector); return tokensCollector.finalize(endLineState); diff --git a/src/vs/editor/standalone/test/browser/monarch.test.ts b/src/vs/editor/standalone/test/browser/monarch.test.ts index 5e67bcb4cbae1..22487d563aac8 100644 --- a/src/vs/editor/standalone/test/browser/monarch.test.ts +++ b/src/vs/editor/standalone/test/browser/monarch.test.ts @@ -11,11 +11,13 @@ import { compile } from 'vs/editor/standalone/common/monarch/monarchCompile'; import { Token, TokenizationRegistry } from 'vs/editor/common/languages'; import { IMonarchLanguage } from 'vs/editor/standalone/common/monarch/monarchTypes'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { StandaloneConfigurationService } from 'vs/editor/standalone/browser/standaloneServices'; suite('Monarch', () => { - function createMonarchTokenizer(languageService: ILanguageService, languageId: string, language: IMonarchLanguage): MonarchTokenizer { - return new MonarchTokenizer(languageService, null!, languageId, compile(languageId, language)); + function createMonarchTokenizer(languageService: ILanguageService, languageId: string, language: IMonarchLanguage, configurationService: IConfigurationService): MonarchTokenizer { + return new MonarchTokenizer(languageService, null!, languageId, compile(languageId, language), configurationService); } function getTokens(tokenizer: MonarchTokenizer, lines: string[]): Token[][] { @@ -32,6 +34,7 @@ suite('Monarch', () => { test('Ensure @rematch and nextEmbedded can be used together in Monarch grammar', () => { const disposables = new DisposableStore(); const languageService = disposables.add(new LanguageService()); + const configurationService = new StandaloneConfigurationService(); disposables.add(languageService.registerLanguage({ id: 'sql' })); disposables.add(TokenizationRegistry.register('sql', createMonarchTokenizer(languageService, 'sql', { tokenizer: { @@ -39,7 +42,7 @@ suite('Monarch', () => { [/./, 'token'] ] } - }))); + }, configurationService))); const SQL_QUERY_START = '(SELECT|INSERT|UPDATE|DELETE|CREATE|REPLACE|ALTER|WITH)'; const tokenizer = createMonarchTokenizer(languageService, 'test1', { tokenizer: { @@ -63,7 +66,7 @@ suite('Monarch', () => { ], endStringWithSQL: [[/"""/, { token: 'string.quote', next: '@popall', nextEmbedded: '@pop', },]], } - }); + }, configurationService); const lines = [ `mysql_query("""SELECT * FROM table_name WHERE ds = ''""")`, @@ -106,6 +109,7 @@ suite('Monarch', () => { }); test('microsoft/monaco-editor#1235: Empty Line Handling', () => { + const configurationService = new StandaloneConfigurationService(); const languageService = new LanguageService(); const tokenizer = createMonarchTokenizer(languageService, 'test', { tokenizer: { @@ -125,7 +129,7 @@ suite('Monarch', () => { // No possible rule to detect an empty line and @pop? ], }, - }); + }, configurationService); const lines = [ `// This comment \\`, @@ -163,6 +167,7 @@ suite('Monarch', () => { }); test('microsoft/monaco-editor#2265: Exit a state at end of line', () => { + const configurationService = new StandaloneConfigurationService(); const languageService = new LanguageService(); const tokenizer = createMonarchTokenizer(languageService, 'test', { includeLF: true, @@ -179,7 +184,7 @@ suite('Monarch', () => { [/[^\d]+/, ''] ] } - }); + }, configurationService); const lines = [ `PRINT 10 * 20`, @@ -211,6 +216,7 @@ suite('Monarch', () => { }); test('issue #115662: monarchCompile function need an extra option which can control replacement', () => { + const configurationService = new StandaloneConfigurationService(); const languageService = new LanguageService(); const tokenizer1 = createMonarchTokenizer(languageService, 'test', { @@ -230,7 +236,7 @@ suite('Monarch', () => { }, ], }, - }); + }, configurationService); const tokenizer2 = createMonarchTokenizer(languageService, 'test', { ignoreCase: false, @@ -242,7 +248,7 @@ suite('Monarch', () => { }, ], }, - }); + }, configurationService); const lines = [ `@ham` @@ -265,6 +271,7 @@ suite('Monarch', () => { }); test('microsoft/monaco-editor#2424: Allow to target @@', () => { + const configurationService = new StandaloneConfigurationService(); const languageService = new LanguageService(); const tokenizer = createMonarchTokenizer(languageService, 'test', { @@ -277,7 +284,7 @@ suite('Monarch', () => { }, ], }, - }); + }, configurationService); const lines = [ `@@` @@ -292,4 +299,36 @@ suite('Monarch', () => { languageService.dispose(); }); + test('microsoft/monaco-editor#3025: Check maxTokenizationLineLength before tokenizing', () => { + const configurationService = new StandaloneConfigurationService(); + const languageService = new LanguageService(); + + const tokenizer = createMonarchTokenizer(languageService, 'test', { + maxTokenizationLineLength: 4, + tokenizer: { + root: [ + { + regex: /ham/, + action: { token: 'ham' } + }, + ], + }, + }, configurationService); + + const lines = [ + 'ham', // length 3, should be tokenized + 'hamham' // length 6, should NOT be tokenized + ]; + + const actualTokens = getTokens(tokenizer, lines); + assert.deepStrictEqual(actualTokens, [ + [ + new Token(0, 'ham.test', 'test'), + ], [ + new Token(0, '', 'test') + ] + ]); + languageService.dispose(); + }); + }); From d1fcde7fdd1d80195fae5d7a162c42e3c1d233f3 Mon Sep 17 00:00:00 2001 From: Yuki Ito Date: Thu, 14 Apr 2022 17:40:41 +0100 Subject: [PATCH 067/942] fix bad rebase --- src/vs/editor/standalone/common/monarch/monarchLexer.ts | 5 ++++- src/vs/editor/standalone/test/browser/monarch.test.ts | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts index dde538afac904..72988e4212f0b 100644 --- a/src/vs/editor/standalone/common/monarch/monarchLexer.ts +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -10,7 +10,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import * as languages from 'vs/editor/common/languages'; -import { NullState, nullTokenizeEncoded } from 'vs/editor/common/languages/nullTokenize'; +import { NullState, nullTokenizeEncoded, nullTokenize } from 'vs/editor/common/languages/nullTokenize'; import { TokenTheme } from 'vs/editor/common/languages/supports/tokenization'; import { ILanguageService } from 'vs/editor/common/languages/language'; import * as monarchCommon from 'vs/editor/standalone/common/monarch/monarchCommon'; @@ -479,6 +479,9 @@ export class MonarchTokenizer implements languages.ITokenizationSupport { } public tokenize(line: string, hasEOL: boolean, lineState: languages.IState): languages.TokenizationResult { + if (line.length >= this._maxTokenizationLineLength) { + return nullTokenize(this._languageId, lineState); + } const tokensCollector = new MonarchClassicTokensCollector(); const endLineState = this._tokenize(line, hasEOL, lineState, tokensCollector); return tokensCollector.finalize(endLineState); diff --git a/src/vs/editor/standalone/test/browser/monarch.test.ts b/src/vs/editor/standalone/test/browser/monarch.test.ts index 22487d563aac8..da034e57b4ea1 100644 --- a/src/vs/editor/standalone/test/browser/monarch.test.ts +++ b/src/vs/editor/standalone/test/browser/monarch.test.ts @@ -299,12 +299,14 @@ suite('Monarch', () => { languageService.dispose(); }); - test('microsoft/monaco-editor#3025: Check maxTokenizationLineLength before tokenizing', () => { + test('microsoft/monaco-editor#3025: Check maxTokenizationLineLength before tokenizing', async () => { const configurationService = new StandaloneConfigurationService(); const languageService = new LanguageService(); + // Set maxTokenizationLineLength to 4 so that "ham" works but "hamham" would fail + await configurationService.updateValue('editor.maxTokenizationLineLength', 4); + const tokenizer = createMonarchTokenizer(languageService, 'test', { - maxTokenizationLineLength: 4, tokenizer: { root: [ { From 70b7af01e8a639681b63cb32a5cabc5c80428120 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 16 May 2022 21:57:18 +0200 Subject: [PATCH 068/942] wire up policy service --- .../sharedProcess/sharedProcessMain.ts | 8 +- src/vs/code/electron-main/app.ts | 7 ++ src/vs/code/electron-main/main.ts | 20 +++-- src/vs/code/node/cliProcessMain.ts | 9 ++- .../common/configurationService.ts | 8 +- .../configuration/common/configurations.ts | 12 +-- .../test/common/configurationService.test.ts | 27 +++---- .../test/common/policyConfiguration.test.ts | 7 +- .../policy/common/filePolicyService.ts | 2 + src/vs/platform/policy/common/policy.ts | 8 ++ .../policy/node/vscode-policy-watcher.d.ts | 30 ------- .../policy/node/windowsPolicyService.ts | 79 ++++++++++--------- .../test/common/userDataSyncClient.ts | 3 +- .../node/remoteExtensionHostAgentCli.ts | 3 +- src/vs/server/node/serverServices.ts | 3 +- src/vs/workbench/browser/web.main.ts | 3 +- .../electron-sandbox/desktop.main.ts | 20 ++++- .../browser/configurationService.ts | 4 +- .../configurationEditingService.test.ts | 3 +- .../test/browser/configurationService.test.ts | 19 ++--- 20 files changed, 149 insertions(+), 126 deletions(-) delete mode 100644 src/vs/platform/policy/node/vscode-policy-watcher.d.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index aa4c7ad67e7b7..6b07e7c7ddd83 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -100,6 +100,8 @@ import { InspectProfilingService as V8InspectProfilingService } from 'vs/platfor import { IV8InspectProfilingService } from 'vs/platform/profiling/common/profiling'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService'; +import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; +import { IPolicyService } from 'vs/platform/policy/common/policy'; class SharedProcessMain extends Disposable { @@ -180,6 +182,10 @@ class SharedProcessMain extends Disposable { const mainProcessService = new MessagePortMainProcessService(this.server, mainRouter); services.set(IMainProcessService, mainProcessService); + // Policies + const policyService = new PolicyChannelClient(mainProcessService.getChannel('policy')); + services.set(IPolicyService, policyService); + // Environment const environmentService = new SharedProcessEnvironmentService(this.configuration.args, productService); services.set(INativeEnvironmentService, environmentService); @@ -222,7 +228,7 @@ class SharedProcessMain extends Disposable { fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider); // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, environmentService, logService)); + const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, policyService)); services.set(IConfigurationService, configurationService); // Storage (global access only) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 4bc4a432ceb9e..64085aad35617 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -99,6 +99,8 @@ import { IWorkspacesHistoryMainService, WorkspacesHistoryMainService } from 'vs/ import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { IWorkspacesManagementMainService, WorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { CredentialsNativeMainService } from 'vs/platform/credentials/electron-main/credentialsMainService'; +import { IPolicyService } from 'vs/platform/policy/common/policy'; +import { PolicyChannel } from 'vs/platform/policy/common/policyIpc'; /** * The main VS Code application. There will only ever be one instance, @@ -695,6 +697,11 @@ export class CodeApplication extends Disposable { const diagnosticsChannel = ProxyChannel.fromService(accessor.get(IDiagnosticsMainService), { disableMarshalling: true }); this.mainProcessNodeIpcServer.registerChannel('diagnostics', diagnosticsChannel); + // Policies (main & shared process) + const policyChannel = new PolicyChannel(accessor.get(IPolicyService)); + mainProcessElectronServer.registerChannel('policy', policyChannel); + sharedProcessClient.then(client => client.registerChannel('policy', policyChannel)); + // Local Files const diskFileSystemProvider = this.fileService.getProvider(Schemas.file); assertType(diskFileSystemProvider instanceof DiskFileSystemProvider); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 7b95aaad6182c..f31b96ebecece 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -62,6 +62,8 @@ import { IStateMainService } from 'vs/platform/state/electron-main/state'; import { StateMainService } from 'vs/platform/state/electron-main/stateMainService'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IThemeMainService, ThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; +import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; +import { WindowsPolicyService } from 'vs/platform/policy/node/windowsPolicyService'; /** * The main VS Code entry point. @@ -89,13 +91,13 @@ class CodeMain { setUnexpectedErrorHandler(err => console.error(err)); // Create services - const [instantiationService, instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService] = this.createServices(); + const [instantiationService, instanceEnvironment, environmentMainService, policyService, configurationService, stateMainService, bufferLogService, productService] = this.createServices(); try { // Init services try { - await this.initServices(environmentMainService, configurationService, stateMainService); + await this.initServices(environmentMainService, policyService, configurationService, stateMainService); } catch (error) { // Show a dialog for errors that can be resolved by the user @@ -137,7 +139,7 @@ class CodeMain { } } - private createServices(): [IInstantiationService, IProcessEnvironment, IEnvironmentMainService, ConfigurationService, StateMainService, BufferLogService, IProductService] { + private createServices(): [IInstantiationService, IProcessEnvironment, IEnvironmentMainService, IPolicyService, ConfigurationService, StateMainService, BufferLogService, IProductService] { const services = new ServiceCollection(); // Product @@ -166,8 +168,12 @@ class CodeMain { // Logger services.set(ILoggerService, new LoggerService(logService, fileService)); + // Policy + const policyService = isWindows ? new WindowsPolicyService(productService) : new NullPolicyService(); + services.set(IPolicyService, policyService); + // Configuration - const configurationService = new ConfigurationService(environmentMainService.settingsResource, fileService, environmentMainService, logService); + const configurationService = new ConfigurationService(environmentMainService.settingsResource, fileService, policyService); services.set(IConfigurationService, configurationService); // Lifecycle @@ -192,7 +198,7 @@ class CodeMain { // Protocol services.set(IProtocolMainService, new SyncDescriptor(ProtocolMainService)); - return [new InstantiationService(services, true), instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService]; + return [new InstantiationService(services, true), instanceEnvironment, environmentMainService, policyService, configurationService, stateMainService, bufferLogService, productService]; } private patchEnvironment(environmentMainService: IEnvironmentMainService): IProcessEnvironment { @@ -212,7 +218,7 @@ class CodeMain { return instanceEnvironment; } - private initServices(environmentMainService: IEnvironmentMainService, configurationService: ConfigurationService, stateMainService: StateMainService): Promise { + private initServices(environmentMainService: IEnvironmentMainService, policyService: IPolicyService, configurationService: ConfigurationService, stateMainService: StateMainService): Promise { return Promises.settled([ // Environment service (paths) @@ -226,6 +232,8 @@ class CodeMain { environmentMainService.backupHome ].map(path => path ? FSPromises.mkdir(path, { recursive: true }) : undefined)), + policyService.initialize(), + // Configuration service configurationService.initialize(), diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 6ae96fc2047a3..2217a4b70b16d 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -11,6 +11,7 @@ import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/err import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isAbsolute, join } from 'vs/base/common/path'; +import { isWindows } from 'vs/base/common/platform'; import { cwd } from 'vs/base/common/process'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; @@ -39,6 +40,8 @@ import { ILocalizationsService } from 'vs/platform/localizations/common/localiza import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; import { ConsoleLogger, getLogLevel, ILogger, ILogService, LogLevel, MultiplexLogService } from 'vs/platform/log/common/log'; import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; +import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; +import { WindowsPolicyService } from 'vs/platform/policy/node/windowsPolicyService'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRequestService } from 'vs/platform/request/common/request'; @@ -128,8 +131,12 @@ class CliMain extends Disposable { const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService)); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + // Policy + const policyService = isWindows ? new WindowsPolicyService(productService) : new NullPolicyService(); + services.set(IPolicyService, policyService); + // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, environmentService, logService)); + const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, policyService)); services.set(IConfigurationService, configurationService); // Init config diff --git a/src/vs/platform/configuration/common/configurationService.ts b/src/vs/platform/configuration/common/configurationService.ts index b3ff50a6d0cd8..3d0422d38c1d7 100644 --- a/src/vs/platform/configuration/common/configurationService.ts +++ b/src/vs/platform/configuration/common/configurationService.ts @@ -11,9 +11,8 @@ import { URI } from 'vs/base/common/uri'; import { ConfigurationTarget, IConfigurationChange, IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; import { Configuration, ConfigurationChangeEvent, ConfigurationModel, UserSettings } from 'vs/platform/configuration/common/configurationModels'; import { DefaultConfiguration, PolicyConfiguration } from 'vs/platform/configuration/common/configurations'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; -import { ILogService } from 'vs/platform/log/common/log'; +import { IPolicyService } from 'vs/platform/policy/common/policy'; export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable { @@ -31,12 +30,11 @@ export class ConfigurationService extends Disposable implements IConfigurationSe constructor( private readonly settingsResource: URI, fileService: IFileService, - environmentService: IEnvironmentService, - logService: ILogService, + policyService: IPolicyService, ) { super(); this.defaultConfiguration = this._register(new DefaultConfiguration()); - this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, fileService, environmentService, logService)); + this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, policyService)); this.userConfiguration = this._register(new UserSettings(this.settingsResource, undefined, extUriBiasedIgnorePathCase, fileService)); this.configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, new ConfigurationModel()); diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index dad918f94e105..dc2fd2e60799b 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -11,11 +11,8 @@ import { equals } from 'vs/base/common/objects'; import { addToValueTree, IOverrides, toValuesTree } from 'vs/platform/configuration/common/configuration'; import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { Extensions, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IFileService } from 'vs/platform/files/common/files'; -import { ILogService } from 'vs/platform/log/common/log'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; -import { IPolicyService, NullPolicyService, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { IPolicyService, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; import { Registry } from 'vs/platform/registry/common/platform'; export class DefaultConfiguration extends Disposable { @@ -85,19 +82,14 @@ export class PolicyConfiguration extends Disposable { private readonly _onDidChangeConfiguration = this._register(new Emitter()); readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event; - private readonly policyService: IPolicyService; - private _configurationModel = new ConfigurationModel(); get configurationModel() { return this._configurationModel; } constructor( private readonly defaultConfiguration: DefaultConfiguration, - fileService: IFileService, - environmentService: IEnvironmentService, - logService: ILogService + @IPolicyService private readonly policyService: IPolicyService ) { super(); - this.policyService = environmentService.policyFile ? new FilePolicyService(environmentService.policyFile, fileService, logService) : new NullPolicyService(); } async initialize(): Promise { diff --git a/src/vs/platform/configuration/test/common/configurationService.test.ts b/src/vs/platform/configuration/test/common/configurationService.test.ts index 04635ed013c16..d49af5f41f69a 100644 --- a/src/vs/platform/configuration/test/common/configurationService.test.ts +++ b/src/vs/platform/configuration/test/common/configurationService.test.ts @@ -12,19 +12,17 @@ import { URI } from 'vs/base/common/uri'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { NullLogService } from 'vs/platform/log/common/log'; +import { NullPolicyService } from 'vs/platform/policy/common/policy'; import { Registry } from 'vs/platform/registry/common/platform'; suite('ConfigurationService', () => { let fileService: IFileService; - let environmentService: IEnvironmentService; let settingsResource: URI; const disposables: DisposableStore = new DisposableStore(); @@ -33,14 +31,13 @@ suite('ConfigurationService', () => { const diskFileSystemProvider = disposables.add(new InMemoryFileSystemProvider()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); settingsResource = URI.file('settings.json'); - environmentService = new TestInstantiationService().mock(IEnvironmentService) as IEnvironmentService; }); teardown(() => disposables.clear()); test('simple', async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); await testObject.initialize(); const config = testObject.getValue<{ foo: string; @@ -53,7 +50,7 @@ suite('ConfigurationService', () => { test('config gets flattened', async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); await testObject.initialize(); const config = testObject.getValue<{ testworkbench: { @@ -72,7 +69,7 @@ suite('ConfigurationService', () => { test('error case does not explode', async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString(',,,,')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); await testObject.initialize(); const config = testObject.getValue<{ foo: string; @@ -82,7 +79,7 @@ suite('ConfigurationService', () => { }); test('missing file does not explode', async () => { - const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, environmentService, new NullLogService())); + const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, new NullPolicyService())); await testObject.initialize(); const config = testObject.getValue<{ foo: string }>(); @@ -91,7 +88,7 @@ suite('ConfigurationService', () => { }); test('trigger configuration change event when file does not exist', async () => { - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); await testObject.initialize(); return new Promise((c, e) => { disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(() => { @@ -104,7 +101,7 @@ suite('ConfigurationService', () => { }); test('trigger configuration change event when file exists', async () => { - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); await testObject.initialize(); @@ -120,7 +117,7 @@ suite('ConfigurationService', () => { test('reloadConfiguration', async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); await testObject.initialize(); let config = testObject.getValue<{ foo: string; @@ -159,7 +156,7 @@ suite('ConfigurationService', () => { } }); - let testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, environmentService, new NullLogService())); + let testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, new NullPolicyService())); await testObject.initialize(); let setting = testObject.getValue(); @@ -167,7 +164,7 @@ suite('ConfigurationService', () => { assert.strictEqual(setting.configuration.service.testSetting, 'isSet'); await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); - testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); + testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); setting = testObject.getValue(); @@ -195,7 +192,7 @@ suite('ConfigurationService', () => { } }); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); testObject.initialize(); let res = testObject.inspect('something.missing'); @@ -230,7 +227,7 @@ suite('ConfigurationService', () => { } }); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, environmentService, new NullLogService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); testObject.initialize(); let res = testObject.inspect('lookup.service.testNullSetting'); diff --git a/src/vs/platform/configuration/test/common/policyConfiguration.test.ts b/src/vs/platform/configuration/test/common/policyConfiguration.test.ts index 44a8c34894a2e..784334ffa7cba 100644 --- a/src/vs/platform/configuration/test/common/policyConfiguration.test.ts +++ b/src/vs/platform/configuration/test/common/policyConfiguration.test.ts @@ -8,7 +8,6 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { DefaultConfiguration, PolicyConfiguration } from 'vs/platform/configuration/common/configurations'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; @@ -17,11 +16,14 @@ import { Extensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platf import { Registry } from 'vs/platform/registry/common/platform'; import { VSBuffer } from 'vs/base/common/buffer'; import { deepClone } from 'vs/base/common/objects'; +import { IPolicyService } from 'vs/platform/policy/common/policy'; +import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; suite('PolicyConfiguration', () => { let testObject: PolicyConfiguration; let fileService: IFileService; + let policyService: IPolicyService; const policyFile = URI.file('policyFile').with({ scheme: 'vscode-tests' }); const disposables = new DisposableStore(); const policyConfigurationNode: IConfigurationNode = { @@ -62,7 +64,8 @@ suite('PolicyConfiguration', () => { fileService = disposables.add(new FileService(new NullLogService())); const diskFileSystemProvider = disposables.add(new InMemoryFileSystemProvider()); fileService.registerProvider(policyFile.scheme, diskFileSystemProvider); - testObject = disposables.add(new PolicyConfiguration(defaultConfiguration, fileService, { policyFile } as IEnvironmentService, new NullLogService())); + policyService = new FilePolicyService(policyFile, fileService, new NullLogService()); + testObject = disposables.add(new PolicyConfiguration(defaultConfiguration, policyService)); }); teardown(() => disposables.clear()); diff --git a/src/vs/platform/policy/common/filePolicyService.ts b/src/vs/platform/policy/common/filePolicyService.ts index 44d3105dad3f0..c93a15b365f06 100644 --- a/src/vs/platform/policy/common/filePolicyService.ts +++ b/src/vs/platform/policy/common/filePolicyService.ts @@ -27,6 +27,8 @@ function keysDiff(a: Map, b: Map): string[] { export class FilePolicyService extends Disposable implements IPolicyService { + readonly _serviceBrand: undefined; + private policies: Policies = new Map(); private readonly _onDidChange = new Emitter(); diff --git a/src/vs/platform/policy/common/policy.ts b/src/vs/platform/policy/common/policy.ts index 4febe66904fe3..3d7af47c262ad 100644 --- a/src/vs/platform/policy/common/policy.ts +++ b/src/vs/platform/policy/common/policy.ts @@ -4,18 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export type PolicyName = string; export type PolicyValue = string | boolean | number; export type Policies = Map; +export const IPolicyService = createDecorator('policy'); + export interface IPolicyService { + readonly _serviceBrand: undefined; + readonly onDidChange: Event; initialize(): Promise; getPolicyValue(name: PolicyName): PolicyValue | undefined; } export class NullPolicyService implements IPolicyService { + readonly _serviceBrand: undefined; readonly onDidChange = Event.None; async initialize() { } getPolicyValue() { return undefined; } @@ -23,6 +29,8 @@ export class NullPolicyService implements IPolicyService { export class MultiPolicyService implements IPolicyService { + readonly _serviceBrand: undefined; + readonly onDidChange: Event; constructor(private policyServices: readonly IPolicyService[]) { diff --git a/src/vs/platform/policy/node/vscode-policy-watcher.d.ts b/src/vs/platform/policy/node/vscode-policy-watcher.d.ts deleted file mode 100644 index c305f178822e7..0000000000000 --- a/src/vs/platform/policy/node/vscode-policy-watcher.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode-policy-watcher' { - interface Watcher { - dispose(): void; - } - - interface Policies { - [policyName: string]: { - type: string - }; - } - - interface PolicyValues { - [policyName: string]: string | number | boolean; - } - - function createWatcher( - productName: string, - policies: Policies, - onDidChange: (update: PolicyValues) => void - ): Watcher; - - namespace createWatcher { } - - export = createWatcher; -} diff --git a/src/vs/platform/policy/node/windowsPolicyService.ts b/src/vs/platform/policy/node/windowsPolicyService.ts index 64e7b8bb7d612..77550a47c3b0d 100644 --- a/src/vs/platform/policy/node/windowsPolicyService.ts +++ b/src/vs/platform/policy/node/windowsPolicyService.ts @@ -9,12 +9,15 @@ import { IPolicyService, Policies, PolicyName, PolicyValue } from 'vs/platform/p import { IProductService } from 'vs/platform/product/common/productService'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; -import * as createWatcher from 'vscode-policy-watcher'; +import { createWatcher } from 'vscode-policy-watcher'; import { IStringDictionary } from 'vs/base/common/collections'; export class WindowsPolicyService extends Disposable implements IPolicyService { + readonly _serviceBrand: undefined; + private policies: Policies = new Map(); + private init: Promise | undefined; private readonly _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; @@ -26,51 +29,51 @@ export class WindowsPolicyService extends Disposable implements IPolicyService { } initialize(): Promise { - return new Promise(c => { - if (!this.productService.win32RegValueName) { - return; - } - - const policies: IStringDictionary<{ type: string }> = {}; - const configRegistry = Registry.as(Extensions.Configuration); - - for (const configuration of configRegistry.getConfigurations()) { - if (configuration.properties) { - for (const key in configuration.properties) { - const config = configuration.properties[key]; - const policy = config.policy; - - if (policy) { - if (config.type !== 'string' && config.type !== 'number') { - console.warn(`Policy ${policy.name} has unsupported type ${config.type}`); - continue; - } + if (!this.init) { + this.init = new Promise(c => { + if (!this.productService.win32RegValueName) { + return; + } + + const policies: IStringDictionary<{ type: 'string' | 'number' }> = {}; + const configRegistry = Registry.as(Extensions.Configuration); + + for (const configuration of configRegistry.getConfigurations()) { + if (configuration.properties) { + for (const key in configuration.properties) { + const config = configuration.properties[key]; + const policy = config.policy; + + if (policy) { + if (config.type !== 'string' && config.type !== 'number') { + console.warn(`Policy ${policy.name} has unsupported type ${config.type}`); + continue; + } - policies[policy.name] = { type: config.type }; + policies[policy.name] = { type: config.type }; + } } } } - } - let first = true; + let first = true; - this._register(createWatcher(this.productService.win32RegValueName, policies, update => () => { - for (const key in update) { - this.policies.set(key, update[key]); - } + this._register(createWatcher(this.productService.win32RegValueName, policies, update => () => { + for (const key in update) { + this.policies.set(key, update[key]!); + } - if (first) { - first = false; - c(); - } else { - this._onDidChange.fire(Object.keys(update)); - } - })); - }); - } + if (first) { + first = false; + c(); + } else { + this._onDidChange.fire(Object.keys(update)); + } + })); + }); + } - async refresh(): Promise { - // NOOP + return this.init; } getPolicyValue(name: PolicyName): PolicyValue | undefined { diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 5d3ba1f21bdcd..2d888a524c993 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -41,6 +41,7 @@ import { IUserDataSyncMachinesService, UserDataSyncMachinesService } from 'vs/pl import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSyncEnablementService'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncStoreManagementService, UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { NullPolicyService } from 'vs/platform/policy/common/policy'; export class UserDataSyncClient extends Disposable { @@ -86,7 +87,7 @@ export class UserDataSyncClient extends Disposable { this.instantiationService.stub(IStorageService, this._register(new InMemoryStorageService())); - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, environmentService, logService)); + const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, new NullPolicyService())); await configurationService.initialize(); this.instantiationService.stub(IConfigurationService, configurationService); this.instantiationService.stub(IUriIdentityService, this.instantiationService.createInstance(UriIdentityService)); diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index 170fbafa06a45..bfd5928609d6b 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -42,6 +42,7 @@ import { buildHelpMessage, buildVersionMessage, OptionDescriptions } from 'vs/pl import { isWindows } from 'vs/base/common/platform'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService'; +import { NullPolicyService } from 'vs/platform/policy/common/policy'; class CliMain extends Disposable { @@ -91,7 +92,7 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, this._register(new DiskFileSystemProvider(logService))); // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, environmentService, logService)); + const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, new NullPolicyService())); await configurationService.initialize(); services.set(IConfigurationService, configurationService); diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index a443d3bc84c31..c3ae3e6642314 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -70,6 +70,7 @@ import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from 'vs/workbench/services/remote/co import { ExtensionHostStatusService, IExtensionHostStatusService } from 'vs/server/node/extensionHostStatusService'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService'; +import { NullPolicyService } from 'vs/platform/policy/common/policy'; const eventPrefix = 'monacoworkbench'; @@ -107,7 +108,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken services.set(IFileService, fileService); fileService.registerProvider(Schemas.file, disposables.add(new DiskFileSystemProvider(logService))); - const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService, environmentService, logService); + const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService, new NullPolicyService()); services.set(IConfigurationService, configurationService); const extensionHostStatusService = new ExtensionHostStatusService(); diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index b8dd198abc8b2..b8a397edac4d0 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -73,6 +73,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLogChannel'; import { dirname, joinPath } from 'vs/base/common/resources'; +import { NullPolicyService } from 'vs/platform/policy/common/policy'; export class BrowserMain extends Disposable { @@ -440,7 +441,7 @@ export class BrowserMain extends Disposable { private async createWorkspaceService(payload: IAnyWorkspaceIdentifier, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData, Schemas.tmp] /* Cache all non native resources */, environmentService, fileService); - const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, fileService, remoteAgentService, uriIdentityService, logService); + const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, fileService, remoteAgentService, uriIdentityService, logService, new NullPolicyService()); try { await workspaceService.initialize(payload); diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 98b95174864da..42ada97cea0d1 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -50,6 +50,8 @@ import { isCI, isMacintosh } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/electron-sandbox/diskFileSystemProvider'; import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; +import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; +import { IPolicyService } from 'vs/platform/policy/common/policy'; export class DesktopMain extends Disposable { @@ -155,6 +157,10 @@ export class DesktopMain extends Disposable { const mainProcessService = this._register(new ElectronIPCMainProcessService(this.configuration.windowId)); serviceCollection.set(IMainProcessService, mainProcessService); + // Policies + const policyService = new PolicyChannelClient(mainProcessService.getChannel('policy')); + serviceCollection.set(IPolicyService, policyService); + // Product const productService: IProductService = { _serviceBrand: undefined, ...product }; serviceCollection.set(IProductService, productService); @@ -247,7 +253,7 @@ export class DesktopMain extends Disposable { const payload = this.resolveWorkspaceInitializationPayload(environmentService); const [configurationService, storageService] = await Promise.all([ - this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, uriIdentityService, logService).then(service => { + this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, uriIdentityService, logService, policyService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -325,9 +331,17 @@ export class DesktopMain extends Disposable { return workspaceInitializationPayload; } - private async createWorkspaceService(payload: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { + private async createWorkspaceService( + payload: IAnyWorkspaceIdentifier, + environmentService: INativeWorkbenchEnvironmentService, + fileService: FileService, + remoteAgentService: IRemoteAgentService, + uriIdentityService: IUriIdentityService, + logService: ILogService, + policyService: IPolicyService + ): Promise { const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData] /* Cache all non native resources */, environmentService, fileService); - const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache }, environmentService, fileService, remoteAgentService, uriIdentityService, logService); + const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache }, environmentService, fileService, remoteAgentService, uriIdentityService, logService, policyService); try { await workspaceService.initialize(payload); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 0db722acbc186..b31cf92ae7c0c 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -40,6 +40,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { isUndefined } from 'vs/base/common/types'; import { localize } from 'vs/nls'; +import { IPolicyService } from 'vs/platform/policy/common/policy'; class Workspace extends BaseWorkspace { initialized: boolean = false; @@ -104,6 +105,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService, + policyService: IPolicyService ) { super(); @@ -112,7 +114,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat this.initRemoteUserConfigurationBarrier = new Barrier(); this.completeWorkspaceBarrier = new Barrier(); this.defaultConfiguration = this._register(new DefaultConfiguration(configurationCache, environmentService)); - this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, fileService, environmentService, logService)); + this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, policyService)); this.configurationCache = configurationCache; this.fileService = fileService; this.uriIdentityService = uriIdentityService; diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts index 2fba810064f63..16e6ddde07be8 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts @@ -40,6 +40,7 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteA import { getSingleFolderWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { hash } from 'vs/base/common/hash'; +import { NullPolicyService } from 'vs/platform/policy/common/policy'; const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' }); @@ -107,7 +108,7 @@ suite('ConfigurationEditingService', () => { disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService)))); instantiationService.stub(IFileService, fileService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); await workspaceService.initialize({ id: hash(workspaceFolder.toString()).toString(16), uri: workspaceFolder diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 7f980fd1d1d45..794400f725bec 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -44,6 +44,7 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteA import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService'; import { hash } from 'vs/base/common/hash'; import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; +import { NullPolicyService } from 'vs/platform/policy/common/policy'; function convertToWorkspacePayload(folder: URI): ISingleFolderWorkspaceIdentifier { return { @@ -77,7 +78,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); await (testObject).initialize(convertToWorkspacePayload(folder)); }); @@ -117,7 +118,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); await (testObject).initialize(convertToWorkspacePayload(folder)); const actual = testObject.getWorkspaceFolder(joinPath(folder, 'a')); @@ -137,7 +138,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); await (testObject).initialize(convertToWorkspacePayload(folder)); @@ -184,7 +185,7 @@ suite('WorkspaceContextService - Workspace', () => { const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null)); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -242,7 +243,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); @@ -485,7 +486,7 @@ suite('WorkspaceService - Initialization', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -743,7 +744,7 @@ suite('WorkspaceConfigurationService - Folder', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -1440,7 +1441,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, workspaceService); @@ -2101,7 +2102,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve(), needsCaching: () => false }; - testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); instantiationService.stub(IEnvironmentService, environmentService); From 9cfa78035f4faa2661b3faa233c3080c045e1c73 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 16 May 2022 13:00:51 -0700 Subject: [PATCH 069/942] remove enum --- src/vs/platform/terminal/common/terminal.ts | 9 --------- .../terminal/common/xterm/shellIntegrationAddon.ts | 6 +++--- .../contrib/terminal/browser/terminalInstance.ts | 4 ++-- .../contrib/terminal/browser/terminalProcessManager.ts | 4 ++-- 4 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index dda984c4b5d31..ec3d59f59d7d4 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -241,15 +241,6 @@ export interface IFixedTerminalDimensions { */ rows?: number; } - - -export const enum ShellIntegrationTelemetry { - ActivationTimeout = 'terminal/shellIntegrationActivationTimeout', - FailureProcessExit = 'terminal/shellIntegrationFailureProcessExit', - FailureCustomArgs = 'terminal/shellIntegrationActivationFailureCustomArgs', - Success = 'terminal/shellIntegrationActivationSucceeded' -} - export interface IPtyHostController { readonly onPtyHostExit?: Event; readonly onPtyHostStart?: Event; diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 0b809d33db49d..0d48530b61153 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IShellIntegration, ShellIntegrationTelemetry } from 'vs/platform/terminal/common/terminal'; +import { IShellIntegration } from 'vs/platform/terminal/common/terminal'; import { Disposable } from 'vs/base/common/lifecycle'; import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; import { CommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; @@ -144,7 +144,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private _handleVSCodeSequence(data: string): boolean { const didHandle = this._doHandleVSCodeSequence(data); if (!this._hasUpdatedTelemetry && didHandle) { - this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.Success); + this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivationSucceeded'); this._hasUpdatedTelemetry = true; clearTimeout(this._activationTimeout); } @@ -229,7 +229,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private async _ensureCapabilitiesOrAddFailureTelemetry(): Promise { this._activationTimeout = setTimeout(() => { if (!this.capabilities.get(TerminalCapability.CommandDetection) && !this.capabilities.get(TerminalCapability.CwdDetection)) { - this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.ActivationTimeout); + this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivationTimeout'); this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); } this._hasUpdatedTelemetry = true; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index ce50ff14aad0e..1a677dc5f2764 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -42,7 +42,7 @@ import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notif import { IProductService } from 'vs/platform/product/common/productService'; import { IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IProcessDataEvent, IProcessPropertyMap, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, ProcessPropertyType, ShellIntegrationTelemetry, TerminalIcon, TerminalLocation, TerminalSettingId, TerminalShellType, TitleEventSource, WindowsShellType } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IProcessPropertyMap, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, ProcessPropertyType, TerminalIcon, TerminalLocation, TerminalSettingId, TerminalShellType, TitleEventSource, WindowsShellType } from 'vs/platform/terminal/common/terminal'; import { escapeNonWindowsPath } from 'vs/platform/terminal/common/terminalEnvironment'; import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; @@ -1634,7 +1634,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } if (failedShellIntegrationInjection) { - this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.FailureProcessExit); + this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationFailureProcessExit'); } // First onExit to consumers, this can happen after the terminal has already been disposed. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 8ad3c42cadd17..7ae7dc3c4fc93 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -21,7 +21,7 @@ import { withNullAsUndefined } from 'vs/base/common/types'; import { EnvironmentVariableInfoChangesActive, EnvironmentVariableInfoStale } from 'vs/workbench/contrib/terminal/browser/environmentVariableInfo'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IEnvironmentVariableInfo, IEnvironmentVariableService, IMergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; -import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants, ITerminalDimensions, IProcessReadyEvent, IProcessProperty, ProcessPropertyType, IProcessPropertyMap, ITerminalProcessOptions, TerminalSettingId, ShellIntegrationTelemetry } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IShellLaunchConfig, ITerminalChildProcess, ITerminalEnvironment, ITerminalLaunchError, FlowControlConstants, ITerminalDimensions, IProcessReadyEvent, IProcessProperty, ProcessPropertyType, IProcessPropertyMap, ITerminalProcessOptions, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder'; import { localize } from 'vs/nls'; import { formatMessageForTerminal } from 'vs/workbench/contrib/terminal/common/terminalStrings'; @@ -335,7 +335,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce this._hasChildProcesses = value; break; case ProcessPropertyType.FailedShellIntegrationActivation: - this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>(ShellIntegrationTelemetry.FailureCustomArgs); + this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivationFailureCustomArgs'); break; } this._onDidChangeProperty.fire({ type, value }); From 0af4de3e4139fafb9e8baca6696279180b8e5499 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 16 May 2022 22:22:37 +0200 Subject: [PATCH 070/942] works! --- .../platform/policy/common/filePolicyService.ts | 8 ++++---- src/vs/platform/policy/common/policy.ts | 3 +-- src/vs/platform/policy/common/policyIpc.ts | 15 ++++++++------- .../platform/policy/node/windowsPolicyService.ts | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/vs/platform/policy/common/filePolicyService.ts b/src/vs/platform/policy/common/filePolicyService.ts index c93a15b365f06..1f563b4fab1d9 100644 --- a/src/vs/platform/policy/common/filePolicyService.ts +++ b/src/vs/platform/policy/common/filePolicyService.ts @@ -11,7 +11,7 @@ import { isObject } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; -import { IPolicyService, Policies, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { IPolicyService, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; function keysDiff(a: Map, b: Map): string[] { const result: string[] = []; @@ -29,7 +29,7 @@ export class FilePolicyService extends Disposable implements IPolicyService { readonly _serviceBrand: undefined; - private policies: Policies = new Map(); + private policies = new Map(); private readonly _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; @@ -56,8 +56,8 @@ export class FilePolicyService extends Disposable implements IPolicyService { await this.doRefresh(); } - private async read(): Promise { - const policies: Policies = new Map(); + private async read(): Promise> { + const policies = new Map(); try { const content = await this.fileService.readFile(this.file); diff --git a/src/vs/platform/policy/common/policy.ts b/src/vs/platform/policy/common/policy.ts index 3d7af47c262ad..5071a94f3ed5b 100644 --- a/src/vs/platform/policy/common/policy.ts +++ b/src/vs/platform/policy/common/policy.ts @@ -7,8 +7,7 @@ import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export type PolicyName = string; -export type PolicyValue = string | boolean | number; -export type Policies = Map; +export type PolicyValue = string | boolean; export const IPolicyService = createDecorator('policy'); diff --git a/src/vs/platform/policy/common/policyIpc.ts b/src/vs/platform/policy/common/policyIpc.ts index 00a0913f69306..b3efcfa79d2e1 100644 --- a/src/vs/platform/policy/common/policyIpc.ts +++ b/src/vs/platform/policy/common/policyIpc.ts @@ -6,7 +6,9 @@ import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IPolicyService, Policies, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { IPolicyService, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; + +type Policies = { [name: PolicyName]: PolicyValue | undefined }; export class PolicyChannel implements IServerChannel { @@ -18,10 +20,7 @@ export class PolicyChannel implements IServerChannel { switch (event) { case 'onDidChange': return Event.map( this.service.onDidChange, - names => new Map( - names - .map(name => [name, this.service.getPolicyValue(name)]) - .filter(pair => pair[1] !== undefined) as [PolicyName, PolicyValue][]), + names => names.reduce((r, name) => ({ ...r, [name]: this.service.getPolicyValue(name) }), {}), this.disposables ); } @@ -46,14 +45,16 @@ export class PolicyChannelClient implements IPolicyService { declare readonly _serviceBrand: undefined; - private policies: Policies = new Map(); + private policies = new Map(); private readonly _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; constructor(private readonly channel: IChannel) { this.channel.listen('onDidChange')(policies => { - for (const [name, value] of policies) { + for (const name in policies) { + const value = policies[name]; + if (value === undefined) { this.policies.delete(name); } else { diff --git a/src/vs/platform/policy/node/windowsPolicyService.ts b/src/vs/platform/policy/node/windowsPolicyService.ts index 77550a47c3b0d..6a324539ed416 100644 --- a/src/vs/platform/policy/node/windowsPolicyService.ts +++ b/src/vs/platform/policy/node/windowsPolicyService.ts @@ -5,7 +5,7 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IPolicyService, Policies, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { IPolicyService, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; import { IProductService } from 'vs/platform/product/common/productService'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; @@ -16,7 +16,7 @@ export class WindowsPolicyService extends Disposable implements IPolicyService { readonly _serviceBrand: undefined; - private policies: Policies = new Map(); + private readonly policies = new Map(); private init: Promise | undefined; private readonly _onDidChange = new Emitter(); @@ -58,7 +58,7 @@ export class WindowsPolicyService extends Disposable implements IPolicyService { let first = true; - this._register(createWatcher(this.productService.win32RegValueName, policies, update => () => { + this._register(createWatcher(this.productService.win32RegValueName, policies, update => { for (const key in update) { this.policies.set(key, update[key]!); } From fe9f84127a9730f48ca4444abcaa01b27e01b99e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 16 May 2022 22:35:23 +0200 Subject: [PATCH 071/942] figure out proper init --- src/vs/platform/policy/common/filePolicyService.ts | 3 ++- src/vs/platform/policy/common/policy.ts | 7 ++++--- src/vs/platform/policy/common/policyIpc.ts | 12 ++++++++++-- src/vs/platform/policy/node/windowsPolicyService.ts | 7 ++++--- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/vs/platform/policy/common/filePolicyService.ts b/src/vs/platform/policy/common/filePolicyService.ts index 96974697594dc..f46b55cb70fa4 100644 --- a/src/vs/platform/policy/common/filePolicyService.ts +++ b/src/vs/platform/policy/common/filePolicyService.ts @@ -48,8 +48,9 @@ export class FilePolicyService extends Disposable implements IPolicyService { this._register(onDidChangePolicyFile(() => this.throttledDelayer.trigger(() => this.refresh()))); } - async initialize(): Promise { + async initialize(): Promise<{ [name: PolicyName]: PolicyValue }> { await this.refresh(); + return Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {}); } private async read(): Promise> { diff --git a/src/vs/platform/policy/common/policy.ts b/src/vs/platform/policy/common/policy.ts index 5071a94f3ed5b..6446f06e9acf8 100644 --- a/src/vs/platform/policy/common/policy.ts +++ b/src/vs/platform/policy/common/policy.ts @@ -15,14 +15,14 @@ export interface IPolicyService { readonly _serviceBrand: undefined; readonly onDidChange: Event; - initialize(): Promise; + initialize(): Promise<{ [name: PolicyName]: PolicyValue }>; getPolicyValue(name: PolicyName): PolicyValue | undefined; } export class NullPolicyService implements IPolicyService { readonly _serviceBrand: undefined; readonly onDidChange = Event.None; - async initialize() { } + async initialize() { return {}; } getPolicyValue() { return undefined; } } @@ -37,7 +37,8 @@ export class MultiPolicyService implements IPolicyService { } async initialize() { - await Promise.all(this.policyServices.map(p => p.initialize())); + const result = await Promise.all(this.policyServices.map(p => p.initialize())); + return result.reduce((r, o) => ({ ...r, ...o }), {}); } getPolicyValue(name: PolicyName) { diff --git a/src/vs/platform/policy/common/policyIpc.ts b/src/vs/platform/policy/common/policyIpc.ts index b3efcfa79d2e1..c163321d08582 100644 --- a/src/vs/platform/policy/common/policyIpc.ts +++ b/src/vs/platform/policy/common/policyIpc.ts @@ -61,11 +61,19 @@ export class PolicyChannelClient implements IPolicyService { this.policies.set(name, value); } } + + this._onDidChange.fire(Object.keys(policies)); }); } - initialize(): Promise { - return this.channel.call('initialize'); + async initialize(): Promise<{ [name: PolicyName]: PolicyValue }> { + const result = await this.channel.call<{ [name: PolicyName]: PolicyValue }>('initialize'); + + for (const name in result) { + this.policies.set(name, result[name]); + } + + return result; } getPolicyValue(name: PolicyName): PolicyValue | undefined { diff --git a/src/vs/platform/policy/node/windowsPolicyService.ts b/src/vs/platform/policy/node/windowsPolicyService.ts index 6a324539ed416..7e943f1c5ff99 100644 --- a/src/vs/platform/policy/node/windowsPolicyService.ts +++ b/src/vs/platform/policy/node/windowsPolicyService.ts @@ -11,13 +11,14 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { createWatcher } from 'vscode-policy-watcher'; import { IStringDictionary } from 'vs/base/common/collections'; +import { Iterable } from 'vs/base/common/iterator'; export class WindowsPolicyService extends Disposable implements IPolicyService { readonly _serviceBrand: undefined; private readonly policies = new Map(); - private init: Promise | undefined; + private init: Promise<{ [name: PolicyName]: PolicyValue }> | undefined; private readonly _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; @@ -28,7 +29,7 @@ export class WindowsPolicyService extends Disposable implements IPolicyService { super(); } - initialize(): Promise { + initialize(): Promise<{ [name: PolicyName]: PolicyValue }> { if (!this.init) { this.init = new Promise(c => { if (!this.productService.win32RegValueName) { @@ -65,7 +66,7 @@ export class WindowsPolicyService extends Disposable implements IPolicyService { if (first) { first = false; - c(); + c(Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {})); } else { this._onDidChange.fire(Object.keys(update)); } From 07f313f44aaee8c550a3b8f24047a26552118ef6 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 16 May 2022 13:47:32 -0700 Subject: [PATCH 072/942] fix compile error --- src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 0d48530b61153..c02ea09dbeef2 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -125,7 +125,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private _terminal?: Terminal; readonly capabilities = new TerminalCapabilityStore(); private _hasUpdatedTelemetry: boolean = false; - private _activationTimeout!: NodeJS.Timeout; + private _activationTimeout!: any; constructor( @ILogService private readonly _logService: ILogService, From 2c0fa7052dcab8a40b611617782bc24014bdf452 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 16 May 2022 15:29:11 -0700 Subject: [PATCH 073/942] Update src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- .../platform/terminal/common/xterm/shellIntegrationAddon.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index c02ea09dbeef2..8af901f1680c2 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -146,7 +146,10 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati if (!this._hasUpdatedTelemetry && didHandle) { this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivationSucceeded'); this._hasUpdatedTelemetry = true; - clearTimeout(this._activationTimeout); + if (this._activationTimeout !== undefined) { + clearTimeout(this._activationTimeout); + this._activationTimeout = undefined; + } } return didHandle; } From 063486aafd924efa02eaae26eb7394e317f9e092 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 16 May 2022 15:29:29 -0700 Subject: [PATCH 074/942] revert more changes --- src/vs/workbench/contrib/terminal/browser/terminal.ts | 4 ++-- .../contrib/terminal/browser/terminalInstance.ts | 2 +- .../contrib/terminal/browser/xterm/xtermTerminal.ts | 10 ++++------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index a2b290be7b07e..6d1494b10d3e9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -858,9 +858,9 @@ export interface IXtermTerminal { readonly commandTracker: ICommandTracker; /** - * Reports the status of shell integration and fires events relating to it when enabled. + * Reports the status of shell integration and fires events relating to it. */ - readonly shellIntegration: IShellIntegration | undefined; + readonly shellIntegration: IShellIntegration; readonly onDidChangeSelection: Event; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 1a677dc5f2764..f845f8ddc335a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -725,7 +725,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._areLinksReady = true; this._onLinksReady.fire(this); }); - this._processManager.onRestoreCommands(e => this.xterm?.shellIntegration?.deserialize(e)); + this._processManager.onRestoreCommands(e => this.xterm?.shellIntegration.deserialize(e)); this._loadTypeAheadAddon(xterm); diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 7f6faec3f2453..3704797e24e30 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -618,12 +618,10 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { this._decorationAddon.dispose(); this._decorationAddon = undefined; } - } else { - this._shellIntegrationAddon?.dispose(); - if (this._decorationAddon) { - this._decorationAddon.dispose(); - this._decorationAddon = undefined; - } + } + if (this._decorationAddon) { + this._decorationAddon.dispose(); + this._decorationAddon = undefined; } } } From 77aebb7f5c630c1ff6aab04a78ceb5cb15529d33 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 16 May 2022 15:35:18 -0700 Subject: [PATCH 075/942] Add bot comment on issues mentioning old vscode version (#149669) --- .github/workflows/on-open.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index 98a739f8c7f1b..3c6617101e72e 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -31,6 +31,7 @@ jobs: appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} labelColor: "006b75" labelDescription: Issues found in a recent release of VS Code + oldVersionMessage: "Thanks for creating this issue! It looks like you may be using an old version of VS Code, the latest stable release is {currentVersion}. Please try upgrading to the latest version and checking whether this issue remains.\n\nHappy Coding!" days: 5 - name: Run Clipboard Labeler From fc31613e8a788e08b627340cce0ffdb079811172 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 16 May 2022 15:35:27 -0700 Subject: [PATCH 076/942] clean up --- src/vs/platform/terminal/common/terminal.ts | 1 + .../terminal/common/xterm/shellIntegrationAddon.ts | 8 ++++---- src/vs/platform/terminal/node/ptyService.ts | 2 +- .../contrib/terminal/browser/xterm/xtermTerminal.ts | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index ec3d59f59d7d4..91b3244bd3fe6 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -241,6 +241,7 @@ export interface IFixedTerminalDimensions { */ rows?: number; } + export interface IPtyHostController { readonly onPtyHostExit?: Event; readonly onPtyHostStart?: Event; diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 8af901f1680c2..c28607fc175e2 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -125,11 +125,11 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private _terminal?: Terminal; readonly capabilities = new TerminalCapabilityStore(); private _hasUpdatedTelemetry: boolean = false; - private _activationTimeout!: any; + private _activationTimeout: number | undefined; constructor( - @ILogService private readonly _logService: ILogService, - private readonly _telemetryService?: ITelemetryService + private readonly _telemetryService: ITelemetryService | undefined, + @ILogService private readonly _logService: ILogService ) { super(); } @@ -230,7 +230,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati } private async _ensureCapabilitiesOrAddFailureTelemetry(): Promise { - this._activationTimeout = setTimeout(() => { + this._activationTimeout = window.setTimeout(() => { if (!this.capabilities.get(TerminalCapability.CommandDetection) && !this.capabilities.get(TerminalCapability.CwdDetection)) { this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivationTimeout'); this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 0b4301c23e6e9..a3191a3230c11 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -742,7 +742,7 @@ class XtermSerializer implements ITerminalSerializer { this._xterm.writeln(reviveBuffer); } this.setUnicodeVersion(unicodeVersion); - this._shellIntegrationAddon = new ShellIntegrationAddon(logService); + this._shellIntegrationAddon = new ShellIntegrationAddon(undefined, logService); this._xterm.loadAddon(this._shellIntegrationAddon); } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 3704797e24e30..cf6e1918dce6f 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -175,7 +175,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { this._updateUnicodeVersion(); this._commandNavigationAddon = this._instantiationService.createInstance(CommandNavigationAddon, _capabilities); this.raw.loadAddon(this._commandNavigationAddon); - this._shellIntegrationAddon = new ShellIntegrationAddon(this._logService, this._telemetryService); + this._shellIntegrationAddon = new ShellIntegrationAddon(this._telemetryService, this._logService); this.raw.loadAddon(this._shellIntegrationAddon); this._updateShellIntegrationAddons(); } From dc85061f320571faf371aa685becb240868d137c Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 16 May 2022 16:08:39 -0700 Subject: [PATCH 077/942] Update src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- .../workbench/contrib/terminal/browser/xterm/xtermTerminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index cf6e1918dce6f..509db6cc5a605 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -175,7 +175,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { this._updateUnicodeVersion(); this._commandNavigationAddon = this._instantiationService.createInstance(CommandNavigationAddon, _capabilities); this.raw.loadAddon(this._commandNavigationAddon); - this._shellIntegrationAddon = new ShellIntegrationAddon(this._telemetryService, this._logService); + this._shellIntegrationAddon = this._instantiationService.createInstance(ShellIntegrationAddon, this._telemetryService); this.raw.loadAddon(this._shellIntegrationAddon); this._updateShellIntegrationAddons(); } From d71f6ec0d9de40bc322e2de76263b5fa8069c0d2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 16 May 2022 17:30:39 -0700 Subject: [PATCH 078/942] Update markdown diagnostics when linked files change (#149672) For #146303 This PR updates the markdown diagnostic reporter to watch linked to files. If one of these linked to files is created or deleted, we recompute the diagnostics for all markdown files that linked to it --- .../src/languageFeatures/diagnostics.ts | 138 +++++++++++++++--- .../src/test/diagnostic.test.ts | 18 ++- 2 files changed, 131 insertions(+), 25 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index fd11cce6531de..f53a83d670e51 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -12,7 +12,7 @@ import { Disposable } from '../util/dispose'; import { isMarkdownFile } from '../util/file'; import { Limiter } from '../util/limiter'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; -import { LinkDefinitionSet, MdLink, MdLinkProvider, MdLinkSource } from './documentLinkProvider'; +import { InternalHref, LinkDefinitionSet, MdLink, MdLinkProvider, MdLinkSource } from './documentLinkProvider'; import { tryFindMdDocumentForLink } from './references'; const localize = nls.loadMessageBundle(); @@ -118,6 +118,94 @@ class InflightDiagnosticRequests { } } +class LinkWatcher extends Disposable { + + private readonly _onDidChangeLinkedToFile = this._register(new vscode.EventEmitter>); + /** + * Event fired with a list of document uri when one of the links in the document changes + */ + public readonly onDidChangeLinkedToFile = this._onDidChangeLinkedToFile.event; + + private readonly _watchers = new Map; + }>(); + + override dispose() { + super.dispose(); + + for (const entry of this._watchers.values()) { + entry.watcher.dispose(); + } + this._watchers.clear(); + } + + /** + * Set the known links in a markdown document, adding and removing file watchers as needed + */ + updateLinksForDocument(document: vscode.Uri, links: readonly MdLink[]) { + const linkedToResource = new Set( + links + .filter(link => link.href.kind === 'internal') + .map(link => (link.href as InternalHref).path)); + + // First decrement watcher counter for previous document state + for (const entry of this._watchers.values()) { + entry.documents.delete(document.toString()); + } + + // Then create/update watchers for new document state + for (const path of linkedToResource) { + let entry = this._watchers.get(path.toString()); + if (!entry) { + entry = { + watcher: this.startWatching(path), + documents: new Map(), + }; + this._watchers.set(path.toString(), entry); + } + + entry.documents.set(document.toString(), document); + } + + // Finally clean up watchers for links that are no longer are referenced anywhere + for (const [key, value] of this._watchers) { + if (value.documents.size === 0) { + value.watcher.dispose(); + this._watchers.delete(key); + } + } + } + + deleteDocument(resource: vscode.Uri) { + this.updateLinksForDocument(resource, []); + } + + private startWatching(path: vscode.Uri): vscode.Disposable { + const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(path, '*'), false, true, false); + const handler = (resource: vscode.Uri) => this.onLinkedResourceChanged(resource); + return vscode.Disposable.from( + watcher, + watcher.onDidDelete(handler), + watcher.onDidCreate(handler), + ); + } + + private onLinkedResourceChanged(resource: vscode.Uri) { + const entry = this._watchers.get(resource.toString()); + if (entry) { + this._onDidChangeLinkedToFile.fire(entry.documents.values()); + } + } +} + export class DiagnosticManager extends Disposable { private readonly collection: vscode.DiagnosticCollection; @@ -126,6 +214,8 @@ export class DiagnosticManager extends Disposable { private readonly pendingDiagnostics = new Set(); private readonly inFlightDiagnostics = this._register(new InflightDiagnosticRequests()); + private readonly linkWatcher = this._register(new LinkWatcher()); + constructor( private readonly computer: DiagnosticComputer, private readonly configuration: DiagnosticConfiguration, @@ -148,10 +238,20 @@ export class DiagnosticManager extends Disposable { this.triggerDiagnostics(e.document); })); - this._register(vscode.workspace.onDidCloseTextDocument(doc => { - this.pendingDiagnostics.delete(doc.uri); - this.inFlightDiagnostics.cancel(doc.uri); - this.collection.delete(doc.uri); + this._register(vscode.workspace.onDidCloseTextDocument(({ uri }) => { + this.pendingDiagnostics.delete(uri); + this.inFlightDiagnostics.cancel(uri); + this.linkWatcher.deleteDocument(uri); + this.collection.delete(uri); + })); + + this._register(this.linkWatcher.onDidChangeLinkedToFile(changedDocuments => { + for (const resource of changedDocuments) { + const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === resource.toString()); + if (doc) { + this.triggerDiagnostics(doc); + } + } })); this.rebuild(); @@ -162,12 +262,12 @@ export class DiagnosticManager extends Disposable { this.pendingDiagnostics.clear(); } - public async getDiagnostics(doc: SkinnyTextDocument, token: vscode.CancellationToken): Promise { + public async recomputeDiagnosticState(doc: SkinnyTextDocument, token: vscode.CancellationToken): Promise<{ diagnostics: readonly vscode.Diagnostic[]; links: readonly MdLink[]; config: DiagnosticOptions }> { const config = this.configuration.getOptions(doc.uri); if (!config.enabled) { - return []; + return { diagnostics: [], links: [], config }; } - return this.computer.getDiagnostics(doc, config, token); + return { ...await this.computer.getDiagnostics(doc, config, token), config }; } private async recomputePendingDiagnostics(): Promise { @@ -178,8 +278,9 @@ export class DiagnosticManager extends Disposable { const doc = vscode.workspace.textDocuments.find(doc => doc.uri.fsPath === resource.fsPath); if (doc) { this.inFlightDiagnostics.trigger(doc.uri, async (token) => { - const diagnostics = await this.getDiagnostics(doc, token); - this.collection.set(doc.uri, diagnostics); + const state = await this.recomputeDiagnosticState(doc, token); + this.linkWatcher.updateLinksForDocument(doc.uri, state.config.enabled && state.config.validateFilePaths ? state.links : []); + this.collection.set(doc.uri, state.diagnostics); }); } } @@ -269,17 +370,20 @@ export class DiagnosticComputer { private readonly linkProvider: MdLinkProvider, ) { } - public async getDiagnostics(doc: SkinnyTextDocument, options: DiagnosticOptions, token: vscode.CancellationToken): Promise { + public async getDiagnostics(doc: SkinnyTextDocument, options: DiagnosticOptions, token: vscode.CancellationToken): Promise<{ readonly diagnostics: vscode.Diagnostic[]; readonly links: MdLink[] }> { const links = await this.linkProvider.getAllLinks(doc, token); if (token.isCancellationRequested) { - return []; + return { links, diagnostics: [] }; } - return (await Promise.all([ - this.validateFileLinks(doc, options, links, token), - Array.from(this.validateReferenceLinks(options, links)), - this.validateOwnHeaderLinks(doc, options, links, token), - ])).flat(); + return { + links, + diagnostics: (await Promise.all([ + this.validateFileLinks(doc, options, links, token), + Array.from(this.validateReferenceLinks(options, links)), + this.validateOwnHeaderLinks(doc, options, links, token), + ])).flat() + }; } private async validateOwnHeaderLinks(doc: SkinnyTextDocument, options: DiagnosticOptions, links: readonly MdLink[], token: vscode.CancellationToken): Promise { diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index ce6357c65a65f..a9f54978b7d9c 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -16,16 +16,18 @@ import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { assertRangeEqual, joinLines, workspacePath } from './util'; -function getComputedDiagnostics(doc: InMemoryDocument, workspaceContents: MdWorkspaceContents) { +async function getComputedDiagnostics(doc: InMemoryDocument, workspaceContents: MdWorkspaceContents): Promise { const engine = createNewMarkdownEngine(); const linkProvider = new MdLinkProvider(engine); const computer = new DiagnosticComputer(engine, workspaceContents, linkProvider); - return computer.getDiagnostics(doc, { - enabled: true, - validateFilePaths: DiagnosticLevel.warning, - validateOwnHeaders: DiagnosticLevel.warning, - validateReferences: DiagnosticLevel.warning, - }, noopToken); + return ( + await computer.getDiagnostics(doc, { + enabled: true, + validateFilePaths: DiagnosticLevel.warning, + validateOwnHeaders: DiagnosticLevel.warning, + validateReferences: DiagnosticLevel.warning, + }, noopToken) + ).diagnostics; } function createDiagnosticsManager(workspaceContents: MdWorkspaceContents, configuration = new MemoryDiagnosticConfiguration()) { @@ -155,7 +157,7 @@ suite('markdown: Diagnostics', () => { )); const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(false)); - const diagnostics = await manager.getDiagnostics(doc1, noopToken); + const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); assert.deepStrictEqual(diagnostics.length, 0); }); From c2b064538b5016371522f3e4e4a56d23924ec8ce Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 16 May 2022 17:35:16 -0700 Subject: [PATCH 079/942] Don't detect links in markdown code blocks (#149680) Fixes #149678 --- .../languageFeatures/documentLinkProvider.ts | 83 ++++++++++--------- .../src/test/documentLinkProvider.test.ts | 43 ++++++++++ 2 files changed, 86 insertions(+), 40 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts index 56195d7a03cc7..2a239a0449193 100644 --- a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts @@ -187,36 +187,39 @@ const definitionPattern = /^([\t ]*\[(?!\^)((?:\\\]|[^\]])+)\]:\s*)([^<]\S*|<[^> const inlineCodePattern = /(?:^|[^`])(`+)(?:.+?|.*?(?:(?:\r?\n).+?)*?)(?:\r?\n)?\1(?:$|[^`])/gm; -interface CodeInDocument { - /** - * code blocks and fences each represented by [line_start,line_end). - */ - readonly multiline: ReadonlyArray<[number, number]>; - - /** - * inline code spans each represented by {@link vscode.Range}. - */ - readonly inline: readonly vscode.Range[]; -} +class NoLinkRanges { + public static async compute(document: SkinnyTextDocument, engine: MarkdownEngine): Promise { + const tokens = await engine.parse(document); + const multiline = tokens.filter(t => (t.type === 'code_block' || t.type === 'fence' || t.type === 'html_block') && !!t.map).map(t => t.map) as [number, number][]; -async function findCode(document: SkinnyTextDocument, engine: MarkdownEngine): Promise { - const tokens = await engine.parse(document); - const multiline = tokens.filter(t => (t.type === 'code_block' || t.type === 'fence') && !!t.map).map(t => t.map) as [number, number][]; + const text = document.getText(); + const inline = [...text.matchAll(inlineCodePattern)].map(match => { + const start = match.index || 0; + return new vscode.Range(document.positionAt(start), document.positionAt(start + match[0].length)); + }); - const text = document.getText(); - const inline = [...text.matchAll(inlineCodePattern)].map(match => { - const start = match.index || 0; - return new vscode.Range(document.positionAt(start), document.positionAt(start + match[0].length)); - }); + return new NoLinkRanges(multiline, inline); + } - return { multiline, inline }; -} + private constructor( + /** + * code blocks and fences each represented by [line_start,line_end). + */ + public readonly multiline: ReadonlyArray<[number, number]>, -function isLinkInsideCode(code: CodeInDocument, linkHrefRange: vscode.Range) { - return code.multiline.some(interval => linkHrefRange.start.line >= interval[0] && linkHrefRange.start.line < interval[1]) || - code.inline.some(position => position.intersection(linkHrefRange)); + /** + * Inline code spans where links should not be detected + */ + public readonly inline: readonly vscode.Range[] + ) { } + + contains(range: vscode.Range): boolean { + return this.multiline.some(interval => range.start.line >= interval[0] && range.start.line < interval[1]) || + this.inline.some(position => position.intersection(range)); + } } + export class MdLinkProvider implements vscode.DocumentLinkProvider { constructor( @@ -262,35 +265,35 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider { } public async getAllLinks(document: SkinnyTextDocument, token: vscode.CancellationToken): Promise { - const codeInDocument = await findCode(document, this.engine); + const noLinkRanges = await NoLinkRanges.compute(document, this.engine); if (token.isCancellationRequested) { return []; } return Array.from([ - ...this.getInlineLinks(document, codeInDocument), - ...this.getReferenceLinks(document, codeInDocument), - ...this.getLinkDefinitions2(document, codeInDocument), - ...this.getAutoLinks(document, codeInDocument), + ...this.getInlineLinks(document, noLinkRanges), + ...this.getReferenceLinks(document, noLinkRanges), + ...this.getLinkDefinitions2(document, noLinkRanges), + ...this.getAutoLinks(document, noLinkRanges), ]); } - private *getInlineLinks(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable { + private *getInlineLinks(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { const text = document.getText(); for (const match of text.matchAll(linkPattern)) { const matchImageData = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index); - if (matchImageData && !isLinkInsideCode(codeInDocument, matchImageData.source.hrefRange)) { + if (matchImageData && !noLinkRanges.contains(matchImageData.source.hrefRange)) { yield matchImageData; } const matchLinkData = extractDocumentLink(document, match[1].length, match[5], match.index); - if (matchLinkData && !isLinkInsideCode(codeInDocument, matchLinkData.source.hrefRange)) { + if (matchLinkData && !noLinkRanges.contains(matchLinkData.source.hrefRange)) { yield matchLinkData; } } } - private *getAutoLinks(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable { + private *getAutoLinks(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { const text = document.getText(); for (const match of text.matchAll(autoLinkPattern)) { @@ -301,7 +304,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider { const linkStart = document.positionAt(offset); const linkEnd = document.positionAt(offset + link.length); const hrefRange = new vscode.Range(linkStart, linkEnd); - if (isLinkInsideCode(codeInDocument, hrefRange)) { + if (noLinkRanges.contains(hrefRange)) { continue; } yield { @@ -318,7 +321,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider { } } - private *getReferenceLinks(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable { + private *getReferenceLinks(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { const text = document.getText(); for (const match of text.matchAll(referenceLinkPattern)) { let linkStart: vscode.Position; @@ -339,7 +342,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider { } const hrefRange = new vscode.Range(linkStart, linkEnd); - if (isLinkInsideCode(codeInDocument, hrefRange)) { + if (noLinkRanges.contains(hrefRange)) { continue; } @@ -360,11 +363,11 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider { } public async getLinkDefinitions(document: SkinnyTextDocument): Promise> { - const codeInDocument = await findCode(document, this.engine); - return this.getLinkDefinitions2(document, codeInDocument); + const noLinkRanges = await NoLinkRanges.compute(document, this.engine); + return this.getLinkDefinitions2(document, noLinkRanges); } - private *getLinkDefinitions2(document: SkinnyTextDocument, codeInDocument: CodeInDocument): Iterable { + private *getLinkDefinitions2(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { const text = document.getText(); for (const match of text.matchAll(definitionPattern)) { const pre = match[1]; @@ -388,7 +391,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider { text = link; } const hrefRange = new vscode.Range(linkStart, linkEnd); - if (isLinkInsideCode(codeInDocument, hrefRange)) { + if (noLinkRanges.contains(hrefRange)) { continue; } const target = parseLink(document, text); diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index 1df40657d74e4..1f9960d08a1a3 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -270,4 +270,47 @@ suite('markdown.DocumentLinkProvider', () => { const link = links[0]; assertRangeEqual(link.range, new vscode.Range(0, 5, 0, 23)); }); + + test('Should not detect links inside html comment blocks', async () => { + const links = await getLinksForFile(joinLines( + ``, + ``, + ``, + ``, + ``, + ``, + ``, + ``, + ``, + )); + assert.strictEqual(links.length, 0); + }); + + test.skip('Should not detect links inside inline html comments', async () => { + // See #149678 + const links = await getLinksForFile(joinLines( + `text text`, + `text text`, + `text text`, + ``, + `text text`, + ``, + `text text`, + ``, + `text text`, + )); + assert.strictEqual(links.length, 0); + }); }); From 5f3e9c120a4407de3e55465588ce788618526eb0 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Mon, 16 May 2022 18:40:33 -0700 Subject: [PATCH 080/942] Update custom menu styles (#149187) * Initial updates * Add border radius * Address PR feedback * Fix typo * Update shadow blur * Update LR padding and use description foreground for shortcuts * Typo * Fix separator padding/margin * fix jumpy items in hc themes * Fix shadow and border radius * Use opacity for keybinding for better color blend * Update min width and container padding T/B * Revert actionbar margin and remove unnecessary menu css file * Ensure menus respect 0 horizontal margin rule * set bg/fg color on menu container * better fix for jumpy menu items * use outline instead of border * clean up dead css in style.css fix opacity for separators in menus * bring back vertical action bar margins * Remove old CSS import Co-authored-by: SteVen Batten --- extensions/theme-defaults/themes/dark_vs.json | 2 +- src/vs/base/browser/ui/menu/menu.ts | 58 ++++++++++--------- .../browser/contextMenuHandler.css | 9 --- .../contextview/browser/contextMenuHandler.ts | 1 - src/vs/platform/theme/common/colorRegistry.ts | 2 +- src/vs/workbench/browser/media/style.css | 44 -------------- 6 files changed, 33 insertions(+), 83 deletions(-) delete mode 100644 src/vs/platform/contextview/browser/contextMenuHandler.css diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index 768a6b3038cb1..8072b0bdd6e1e 100644 --- a/extensions/theme-defaults/themes/dark_vs.json +++ b/extensions/theme-defaults/themes/dark_vs.json @@ -12,7 +12,7 @@ "activityBarBadge.background": "#007ACC", "sideBarTitle.foreground": "#BBBBBB", "input.placeholderForeground": "#A6A6A6", - "menu.background": "#252526", + "menu.background": "#303031", "menu.foreground": "#CCCCCC", "statusBarItem.remoteForeground": "#FFF", "statusBarItem.remoteBackground": "#16825D", diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index e3528de50650d..24feda55daa53 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -287,11 +287,13 @@ export class Menu extends ActionBar { const fgColor = style.foregroundColor ? `${style.foregroundColor}` : ''; const bgColor = style.backgroundColor ? `${style.backgroundColor}` : ''; const border = style.borderColor ? `1px solid ${style.borderColor}` : ''; - const shadow = style.shadowColor ? `0 2px 4px ${style.shadowColor}` : ''; + const borderRadius = '5px'; + const shadow = style.shadowColor ? `0 2px 8px ${style.shadowColor}` : ''; - container.style.border = border; - this.domNode.style.color = fgColor; - this.domNode.style.backgroundColor = bgColor; + container.style.outline = border; + container.style.borderRadius = borderRadius; + container.style.color = fgColor; + container.style.backgroundColor = bgColor; container.style.boxShadow = shadow; if (this.viewItems) { @@ -691,20 +693,19 @@ class BaseMenuActionViewItem extends BaseActionViewItem { const isSelected = this.element && this.element.classList.contains('focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : undefined; - const border = isSelected && this.menuStyle.selectionBorderColor ? `thin solid ${this.menuStyle.selectionBorderColor}` : ''; + const outline = isSelected && this.menuStyle.selectionBorderColor ? `1px solid ${this.menuStyle.selectionBorderColor}` : ''; + const outlineOffset = isSelected && this.menuStyle.selectionBorderColor ? `-1px` : ''; if (this.item) { this.item.style.color = fgColor ? fgColor.toString() : ''; this.item.style.backgroundColor = bgColor ? bgColor.toString() : ''; + this.item.style.outline = outline; + this.item.style.outlineOffset = outlineOffset; } if (this.check) { this.check.style.color = fgColor ? fgColor.toString() : ''; } - - if (this.container) { - this.container.style.border = border; - } } style(style: IMenuStyles): void { @@ -1012,7 +1013,8 @@ function getMenuWidgetCSS(style: IMenuStyles, isForShadowDom: boolean): string { let result = /* css */` .monaco-menu { font-size: 13px; - + border-radius: 5px; + min-width: 160px; } ${formatRule(Codicon.menuSelection)} @@ -1087,10 +1089,9 @@ ${formatRule(Codicon.menuSubmenu)} .monaco-menu .monaco-action-bar.vertical .action-label.separator { display: block; - border-bottom: 1px solid #bbb; + border-bottom: 1px solid var(--vscode-menu-separatorBackground); padding-top: 1px; - margin-left: .8em; - margin-right: .8em; + padding: 30px; } .monaco-menu .secondary-actions .monaco-action-bar .action-label { @@ -1136,6 +1137,11 @@ ${formatRule(Codicon.menuSubmenu)} position: relative; } +.monaco-menu .monaco-action-bar.vertical .action-menu-item:hover .keybinding, +.monaco-menu .monaco-action-bar.vertical .action-menu-item:focus .keybinding { + opacity: unset; +} + .monaco-menu .monaco-action-bar.vertical .action-label { flex: 1 1 auto; text-decoration: none; @@ -1191,12 +1197,9 @@ ${formatRule(Codicon.menuSubmenu)} } .monaco-menu .monaco-action-bar.vertical .action-label.separator { - padding: 0.5em 0 0 0; - margin-bottom: 0.5em; width: 100%; height: 0px !important; - margin-left: .8em !important; - margin-right: .8em !important; + opacity: 1; } .monaco-menu .monaco-action-bar.vertical .action-label.separator.text { @@ -1238,17 +1241,15 @@ ${formatRule(Codicon.menuSubmenu)} outline: 0; } -.monaco-menu .monaco-action-bar.vertical .action-item { - border: thin solid transparent; /* prevents jumping behaviour on hover or focus */ -} - - -/* High Contrast Theming */ +.hc-black .context-view.monaco-menu-container, +.hc-light .context-view.monaco-menu-container, :host-context(.hc-black) .context-view.monaco-menu-container, :host-context(.hc-light) .context-view.monaco-menu-container { box-shadow: none; } +.hc-black .monaco-menu .monaco-action-bar.vertical .action-item.focused, +.hc-light .monaco-menu .monaco-action-bar.vertical .action-item.focused, :host-context(.hc-black) .monaco-menu .monaco-action-bar.vertical .action-item.focused, :host-context(.hc-light) .monaco-menu .monaco-action-bar.vertical .action-item.focused { background: none; @@ -1257,11 +1258,11 @@ ${formatRule(Codicon.menuSubmenu)} /* Vertical Action Bar Styles */ .monaco-menu .monaco-action-bar.vertical { - padding: .5em 0; + padding: .6em 0; } .monaco-menu .monaco-action-bar.vertical .action-menu-item { - height: 1.8em; + height: 2em; } .monaco-menu .monaco-action-bar.vertical .action-label:not(.separator), @@ -1277,10 +1278,12 @@ ${formatRule(Codicon.menuSubmenu)} .monaco-menu .monaco-action-bar.vertical .action-label.separator { font-size: inherit; - padding: 0.2em 0 0 0; - margin-bottom: 0.2em; + margin: 5px 0 !important; + padding: 0; + border-radius: 0; } +.linux .monaco-menu .monaco-action-bar.vertical .action-label.separator, :host-context(.linux) .monaco-menu .monaco-action-bar.vertical .action-label.separator { margin-left: 0; margin-right: 0; @@ -1291,6 +1294,7 @@ ${formatRule(Codicon.menuSubmenu)} padding: 0 1.8em; } +.linux .monaco-menu .monaco-action-bar.vertical .submenu-indicator { :host-context(.linux) .monaco-menu .monaco-action-bar.vertical .submenu-indicator { height: 100%; mask-size: 10px 10px; diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.css b/src/vs/platform/contextview/browser/contextMenuHandler.css deleted file mode 100644 index 51a9e40092389..0000000000000 --- a/src/vs/platform/contextview/browser/contextMenuHandler.css +++ /dev/null @@ -1,9 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.context-view .monaco-menu { - min-width: 130px; -} - diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index a0f444f666c81..6ad90b3af6193 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -10,7 +10,6 @@ import { Menu } from 'vs/base/browser/ui/menu/menu'; import { ActionRunner, IRunEvent, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; import { isCancellationError } from 'vs/base/common/errors'; import { combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import 'vs/css!./contextMenuHandler'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 6e163e27d1682..4696b6036e6a7 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -470,7 +470,7 @@ export const menuBackground = registerColor('menu.background', { dark: selectBac export const menuSelectionForeground = registerColor('menu.selectionForeground', { dark: listActiveSelectionForeground, light: listActiveSelectionForeground, hcDark: listActiveSelectionForeground, hcLight: listActiveSelectionForeground }, nls.localize('menuSelectionForeground', "Foreground color of the selected menu item in menus.")); export const menuSelectionBackground = registerColor('menu.selectionBackground', { dark: listActiveSelectionBackground, light: listActiveSelectionBackground, hcDark: listActiveSelectionBackground, hcLight: listActiveSelectionBackground }, nls.localize('menuSelectionBackground', "Background color of the selected menu item in menus.")); export const menuSelectionBorder = registerColor('menu.selectionBorder', { dark: null, light: null, hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('menuSelectionBorder', "Border color of the selected menu item in menus.")); -export const menuSeparatorBackground = registerColor('menu.separatorBackground', { dark: '#BBBBBB', light: '#888888', hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('menuSeparatorBackground', "Color of a separator menu item in menus.")); +export const menuSeparatorBackground = registerColor('menu.separatorBackground', { dark: '#606060', light: '#D4D4D4', hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('menuSeparatorBackground', "Color of a separator menu item in menus.")); /** * Toolbar colors diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css index 0f2382f01a4b4..3d61bbcb25586 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -135,50 +135,6 @@ body.web { -webkit-app-region: no-drag; } -.monaco-workbench .monaco-menu .monaco-action-bar.vertical { - padding: .5em 0; -} - -.monaco-workbench .monaco-menu .monaco-action-bar.vertical .action-menu-item { - height: 1.8em; -} - -.monaco-workbench .monaco-menu .monaco-action-bar.vertical .action-label:not(.separator), -.monaco-workbench .monaco-menu .monaco-action-bar.vertical .keybinding { - font-size: inherit; - padding: 0 2em; -} - -.monaco-workbench .monaco-menu .monaco-action-bar.vertical .menu-item-check { - font-size: inherit; - width: 2em; -} - -.monaco-workbench .monaco-menu .monaco-action-bar.vertical .action-label.separator { - font-size: inherit; - padding: 0.2em 0 0 0; - margin-bottom: 0.2em; -} - -.monaco-workbench.linux .monaco-menu .monaco-action-bar.vertical .action-label.separator { - margin-left: 0; - margin-right: 0; -} - -.monaco-workbench .monaco-menu .monaco-action-bar.vertical .submenu-indicator { - font-size: 60%; - padding: 0 1.8em; -} - -.monaco-workbench.linux .monaco-menu .monaco-action-bar.vertical .submenu-indicator { - height: 100%; - mask-size: 10px 10px; - -webkit-mask-size: 10px 10px; -} - -.monaco-workbench .monaco-menu .action-item { - cursor: default; -} .monaco-workbench .codicon[class*='codicon-'] { font-size: 16px; /* sets font-size for codicons in workbench (https://github.com/microsoft/vscode/issues/98495) */ From abb896879f4bb1d3e26008026308f41986743796 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Mon, 16 May 2022 14:05:05 -0700 Subject: [PATCH 081/942] Add hasPolicy tag --- .../preferences/browser/settingsEditor2.ts | 11 ++-- .../preferences/browser/settingsSearchMenu.ts | 8 ++- .../preferences/browser/settingsTree.ts | 56 ++++++++++++------- .../preferences/browser/settingsTreeModels.ts | 13 ++++- .../contrib/preferences/common/preferences.ts | 1 + 5 files changed, 60 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index d2d2c18350340..4bbcafeb7c2d5 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -43,7 +43,7 @@ import { commonlyUsedData, tocData } from 'vs/workbench/contrib/preferences/brow import { AbstractSettingRenderer, HeightChangeParams, ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveConfiguredUntrustedSettings, createTocTreeForExtensionSettings, resolveSettingsTree, SettingsTree, SettingTreeRenderers } from 'vs/workbench/contrib/preferences/browser/settingsTree'; import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, ENABLE_LANGUAGE_FILTER, EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, ID_SETTING_TAG, IPreferencesSearchService, ISearchProvider, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SUGGEST_FILTERS, WORKSPACE_TRUST_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, ENABLE_LANGUAGE_FILTER, EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, ID_SETTING_TAG, IPreferencesSearchService, ISearchProvider, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG, POLICY_SETTING_TAG, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SUGGEST_FILTERS, WORKSPACE_TRUST_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; import { settingsHeaderBorder, settingsSashBorder, settingsTextInputBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IOpenSettingsOptions, IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEditorOptions, SettingMatchType, SettingValueType, validateSettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; @@ -126,6 +126,7 @@ export class SettingsEditor2 extends EditorPane { `@${FEATURE_SETTING_TAG}remote`, `@${FEATURE_SETTING_TAG}timeline`, `@${FEATURE_SETTING_TAG}notebook`, + `@${POLICY_SETTING_TAG}` ]; private static shouldSettingUpdateFast(type: SettingValueType | SettingValueType[]): boolean { @@ -866,10 +867,10 @@ export class SettingsEditor2 extends EditorPane { // the element was not found } })); - this._register(this.settingRenderers.onApplyLanguageFilter((lang: string) => { - if (this.searchWidget) { - // Prepend the language filter to the query. - const newQuery = `@${LANGUAGE_SETTING_TAG}${lang} ${this.searchWidget.getValue().trimStart()}`; + this._register(this.settingRenderers.onApplyFilter((filter: string) => { + if (this.searchWidget && !this.searchWidget.getValue().includes(filter)) { + // Prepend the filter to the query. + const newQuery = `${filter} ${this.searchWidget.getValue().trimStart()}`; this.focusSearch(newQuery, false); } })); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts index e39d23eb58e39..9a1c0ec289ca5 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts @@ -10,7 +10,7 @@ import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestCont import { localize } from 'vs/nls'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; -import { EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, GENERAL_TAG_SETTING_TAG, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; +import { EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, GENERAL_TAG_SETTING_TAG, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG, POLICY_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenuActionViewItem { private readonly suggestController: SuggestController | null; @@ -135,6 +135,12 @@ export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenu localize('onlineSettingsSearch', "Online services"), localize('onlineSettingsSearchTooltip', "Show settings for online services"), '@tag:usesOnlineServices' + ), + this.createToggleAction( + 'policySettingsSearch', + localize('policySettingsSearch', "Policy services"), + localize('policySettingsSearchTooltip', "Show settings for policy services"), + `@${POLICY_SETTING_TAG}` ) ]; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 673de4bf5aa4d..2e2ee4e7257e8 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -43,7 +43,7 @@ import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerg import { ITOCEntry } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; import { inspectSetting, ISettingsEditorViewState, settingKeyToDisplayFormat, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; import { ExcludeSettingWidget, ISettingListChangeEvent, IListDataItem, ListSettingWidget, ObjectSettingDropdownWidget, IObjectDataItem, IObjectEnumOption, ObjectValue, IObjectValueSuggester, IObjectKeySuggester, ObjectSettingCheckboxWidget } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; -import { SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; +import { LANGUAGE_SETTING_TAG, POLICY_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ISetting, ISettingsGroup, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { getDefaultIgnoredSettings, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; @@ -740,8 +740,8 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre protected readonly _onDidChangeSettingHeight = this._register(new Emitter()); readonly onDidChangeSettingHeight: Event = this._onDidChangeSettingHeight.event; - protected readonly _onApplyLanguageFilter = this._register(new Emitter()); - readonly onApplyLanguageFilter: Event = this._onApplyLanguageFilter.event; + protected readonly _onApplyFilter = this._register(new Emitter()); + readonly onApplyFilter: Event = this._onApplyFilter.event; private readonly markdownRenderer: MarkdownRenderer; @@ -794,14 +794,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); const toDispose = new DisposableStore(); - - const policyWarningElement = DOM.append(container, $('.setting-item-policy-description')); - const policyIcon = DOM.append(policyWarningElement, $('span.codicon.codicon-lock')); - toDispose.add(attachStylerCallback(this._themeService, { editorInfoForeground }, colors => { - policyIcon.style.setProperty('--organization-policy-icon-color', colors.editorInfoForeground?.toString() || ''); - })); - const element = DOM.append(policyWarningElement, $('span')); - element.textContent = localize('policyLabel', "This setting is managed by your organization."); + const policyWarningElement = this.renderPolicyLabel(container, toDispose); const toolbarContainer = DOM.append(container, $('.setting-toolbar-container')); const toolbar = this.renderSettingToolbar(toolbarContainer); @@ -848,6 +841,33 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre }); } + protected renderPolicyLabel(container: HTMLElement, toDispose: DisposableStore): HTMLElement { + const policyWarningElement = DOM.append(container, $('.setting-item-policy-description')); + const policyIcon = DOM.append(policyWarningElement, $('span.codicon.codicon-lock')); + toDispose.add(attachStylerCallback(this._themeService, { editorInfoForeground }, colors => { + policyIcon.style.setProperty('--organization-policy-icon-color', colors.editorInfoForeground?.toString() || ''); + })); + const element = DOM.append(policyWarningElement, $('span')); + element.textContent = localize('policyLabel', "This setting is managed by your organization."); + const viewPolicyLabel = localize('viewPolicySettings', "View policy settings"); + const linkElement: HTMLAnchorElement = DOM.append(policyWarningElement, $('a')); + linkElement.textContent = viewPolicyLabel; + linkElement.setAttribute('tabindex', '0'); + linkElement.href = '#'; + toDispose.add(DOM.addStandardDisposableListener(linkElement, DOM.EventType.CLICK, (e: MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + this._onApplyFilter.fire(`@${POLICY_SETTING_TAG}`); + })); + toDispose.add(DOM.addStandardDisposableListener(linkElement, DOM.EventType.KEY_DOWN, (e: IKeyboardEvent) => { + if (e.equals(KeyCode.Enter) || e.equals(KeyCode.Space)) { + e.stopPropagation(); + this._onApplyFilter.fire(`@${POLICY_SETTING_TAG}`); + } + })); + return policyWarningElement; + } + protected renderSettingToolbar(container: HTMLElement): ToolBar { const toggleMenuKeybinding = this._keybindingService.lookupKeybinding(SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU); let toggleMenuTitle = localize('settingsContextMenuTitle', "More Actions... "); @@ -1098,7 +1118,7 @@ export class SettingComplexRenderer extends AbstractSettingRenderer implements I template.elementDisposables.add(template.button.onDidClick(() => { if (isLanguageTagSetting) { - this._onApplyLanguageFilter.fire(plainKey); + this._onApplyFilter.fire(`@${LANGUAGE_SETTING_TAG}:${plainKey}`); } else { this._onDidOpenSettings.fire(dataElement.setting.key); } @@ -1836,13 +1856,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre const toolbar = this.renderSettingToolbar(toolbarContainer); toDispose.add(toolbar); - const policyWarningElement = DOM.append(container, $('.setting-item-policy-description')); - const policyIcon = DOM.append(policyWarningElement, $('span.codicon.codicon-lock')); - toDispose.add(attachStylerCallback(this._themeService, { editorInfoForeground }, colors => { - policyIcon.style.setProperty('--organization-policy-icon-color', colors.editorInfoForeground?.toString() || ''); - })); - const element = DOM.append(policyWarningElement, $('span')); - element.textContent = localize('policyLabel', "This setting is managed by your organization."); + const policyWarningElement = this.renderPolicyLabel(container, toDispose); const template: ISettingBoolItemTemplate = { toDispose, @@ -1943,7 +1957,7 @@ export class SettingTreeRenderers { readonly onDidChangeSettingHeight: Event; - readonly onApplyLanguageFilter: Event; + readonly onApplyFilter: Event; readonly allRenderers: ITreeRenderer[]; @@ -1992,7 +2006,7 @@ export class SettingTreeRenderers { this.onDidClickSettingLink = Event.any(...settingRenderers.map(r => r.onDidClickSettingLink)); this.onDidFocusSetting = Event.any(...settingRenderers.map(r => r.onDidFocusSetting)); this.onDidChangeSettingHeight = Event.any(...settingRenderers.map(r => r.onDidChangeSettingHeight)); - this.onApplyLanguageFilter = Event.any(...settingRenderers.map(r => r.onApplyLanguageFilter)); + this.onApplyFilter = Event.any(...settingRenderers.map(r => r.onApplyFilter)); this.allRenderers = [ ...settingRenderers, diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index 6e0c5b34b4334..b2f6eac0cdcbd 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -11,7 +11,7 @@ import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationValue } from 'vs/platform/configuration/common/configuration'; import { SettingsTarget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; import { ITOCEntry, knownAcronyms, knownTermMappings, tocData } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; -import { ENABLE_LANGUAGE_FILTER, MODIFIED_SETTING_TAG, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; +import { ENABLE_LANGUAGE_FILTER, MODIFIED_SETTING_TAG, POLICY_SETTING_TAG, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; import { IExtensionSetting, ISearchResult, ISetting, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { FOLDER_SCOPES, WORKSPACE_SCOPES, REMOTE_MACHINE_SCOPES, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; @@ -238,7 +238,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { this.value = displayValue; this.isConfigured = isConfigured; - if (isConfigured || this.setting.tags || this.tags || this.setting.restricted) { + if (isConfigured || this.setting.tags || this.tags || this.setting.restricted || this.hasPolicyValue) { // Don't create an empty Set for all 1000 settings, only if needed this.tags = new Set(); if (isConfigured) { @@ -252,6 +252,10 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { if (this.setting.restricted) { this.tags.add(REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG); } + + if (this.hasPolicyValue) { + this.tags.add(POLICY_SETTING_TAG); + } } this.overriddenScopeList = overriddenScopeList; @@ -905,6 +909,11 @@ export function parseQuery(query: string): IParsedQuery { return ''; }); + query = query.replace(`@${POLICY_SETTING_TAG}`, () => { + tags.push(POLICY_SETTING_TAG); + return ''; + }); + const extensions: string[] = []; const features: string[] = []; const ids: string[] = []; diff --git a/src/vs/workbench/contrib/preferences/common/preferences.ts b/src/vs/workbench/contrib/preferences/common/preferences.ts index 6138098d36f74..6b87d1ae46b83 100644 --- a/src/vs/workbench/contrib/preferences/common/preferences.ts +++ b/src/vs/workbench/contrib/preferences/common/preferences.ts @@ -78,6 +78,7 @@ export const FEATURE_SETTING_TAG = 'feature:'; export const ID_SETTING_TAG = 'id:'; export const LANGUAGE_SETTING_TAG = 'lang:'; export const GENERAL_TAG_SETTING_TAG = 'tag:'; +export const POLICY_SETTING_TAG = 'hasPolicy'; export const WORKSPACE_TRUST_SETTING_TAG = 'workspaceTrust'; export const REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG = 'requireTrustedWorkspace'; export const KEYBOARD_LAYOUT_OPEN_PICKER = 'workbench.action.openKeyboardLayoutPicker'; From 558b5fbc2716b1c2339a83e95450c1972eb18ddf Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 17 May 2022 08:48:08 +0200 Subject: [PATCH 082/942] fix policy ipc serialization --- src/vs/platform/policy/common/policyIpc.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/policy/common/policyIpc.ts b/src/vs/platform/policy/common/policyIpc.ts index c163321d08582..2fdb1e62cb1ea 100644 --- a/src/vs/platform/policy/common/policyIpc.ts +++ b/src/vs/platform/policy/common/policyIpc.ts @@ -8,8 +8,6 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IPolicyService, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; -type Policies = { [name: PolicyName]: PolicyValue | undefined }; - export class PolicyChannel implements IServerChannel { private readonly disposables = new DisposableStore(); @@ -20,7 +18,7 @@ export class PolicyChannel implements IServerChannel { switch (event) { case 'onDidChange': return Event.map( this.service.onDidChange, - names => names.reduce((r, name) => ({ ...r, [name]: this.service.getPolicyValue(name) }), {}), + names => names.reduce((r, name) => ({ ...r, [name]: this.service.getPolicyValue(name) ?? null }), {}), this.disposables ); } @@ -51,11 +49,11 @@ export class PolicyChannelClient implements IPolicyService { readonly onDidChange: Event = this._onDidChange.event; constructor(private readonly channel: IChannel) { - this.channel.listen('onDidChange')(policies => { + this.channel.listen('onDidChange')(policies => { for (const name in policies) { - const value = policies[name]; + const value = policies[name as keyof typeof policies]; - if (value === undefined) { + if (value === null) { this.policies.delete(name); } else { this.policies.set(name, value); From a1090d51c50e6e266e28b69f00dac615e380bc67 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 17 May 2022 08:55:41 +0200 Subject: [PATCH 083/942] make sure policy init always returns the right values --- src/vs/platform/policy/node/windowsPolicyService.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/policy/node/windowsPolicyService.ts b/src/vs/platform/policy/node/windowsPolicyService.ts index 7e943f1c5ff99..9b9cba059a924 100644 --- a/src/vs/platform/policy/node/windowsPolicyService.ts +++ b/src/vs/platform/policy/node/windowsPolicyService.ts @@ -18,7 +18,7 @@ export class WindowsPolicyService extends Disposable implements IPolicyService { readonly _serviceBrand: undefined; private readonly policies = new Map(); - private init: Promise<{ [name: PolicyName]: PolicyValue }> | undefined; + private init: Promise | undefined; private readonly _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; @@ -29,7 +29,7 @@ export class WindowsPolicyService extends Disposable implements IPolicyService { super(); } - initialize(): Promise<{ [name: PolicyName]: PolicyValue }> { + async initialize(): Promise<{ [name: PolicyName]: PolicyValue }> { if (!this.init) { this.init = new Promise(c => { if (!this.productService.win32RegValueName) { @@ -66,7 +66,7 @@ export class WindowsPolicyService extends Disposable implements IPolicyService { if (first) { first = false; - c(Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {})); + c(); } else { this._onDidChange.fire(Object.keys(update)); } @@ -74,7 +74,8 @@ export class WindowsPolicyService extends Disposable implements IPolicyService { }); } - return this.init; + await this.init; + return Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {}); } getPolicyValue(name: PolicyName): PolicyValue | undefined { From 2f19fc5c580b56c4f1e40215432df4c682b3d8a5 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 17 May 2022 09:00:45 +0200 Subject: [PATCH 084/942] delete policy removals --- src/vs/platform/policy/node/windowsPolicyService.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/policy/node/windowsPolicyService.ts b/src/vs/platform/policy/node/windowsPolicyService.ts index 9b9cba059a924..36a083dd2d794 100644 --- a/src/vs/platform/policy/node/windowsPolicyService.ts +++ b/src/vs/platform/policy/node/windowsPolicyService.ts @@ -61,7 +61,13 @@ export class WindowsPolicyService extends Disposable implements IPolicyService { this._register(createWatcher(this.productService.win32RegValueName, policies, update => { for (const key in update) { - this.policies.set(key, update[key]!); + const value = update[key] as any; + + if (value === undefined) { + this.policies.delete(key); + } else { + this.policies.set(key, value); + } } if (first) { From 2d1940e1ab04c5b679409f434272c2752d7ce1f5 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 17 May 2022 09:33:32 +0200 Subject: [PATCH 085/942] wip: registerPolicyDefinitions --- src/vs/code/electron-main/main.ts | 4 +- src/vs/code/node/cliProcessMain.ts | 2 +- .../configuration/common/configurations.ts | 32 +++++++++- .../policy/common/filePolicyService.ts | 6 +- src/vs/platform/policy/common/policy.ts | 34 ++--------- src/vs/platform/policy/common/policyIpc.ts | 11 ++-- .../policy/node/windowsPolicyService.ts | 60 +++++++------------ 7 files changed, 66 insertions(+), 83 deletions(-) diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index f31b96ebecece..16a7a727c1575 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -169,7 +169,7 @@ class CodeMain { services.set(ILoggerService, new LoggerService(logService, fileService)); // Policy - const policyService = isWindows ? new WindowsPolicyService(productService) : new NullPolicyService(); + const policyService = isWindows && productService.win32RegValueName ? new WindowsPolicyService(productService.win32RegValueName) : new NullPolicyService(); services.set(IPolicyService, policyService); // Configuration @@ -232,8 +232,6 @@ class CodeMain { environmentMainService.backupHome ].map(path => path ? FSPromises.mkdir(path, { recursive: true }) : undefined)), - policyService.initialize(), - // Configuration service configurationService.initialize(), diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 2217a4b70b16d..4ea5092c119c4 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -132,7 +132,7 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, diskFileSystemProvider); // Policy - const policyService = isWindows ? new WindowsPolicyService(productService) : new NullPolicyService(); + const policyService = isWindows && productService.win32RegValueName ? new WindowsPolicyService(productService.win32RegValueName) : new NullPolicyService(); services.set(IPolicyService, policyService); // Configuration diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index 1834735dccf47..aed37d85e4785 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -11,7 +11,7 @@ import { equals } from 'vs/base/common/objects'; import { addToValueTree, IOverrides, toValuesTree } from 'vs/platform/configuration/common/configuration'; import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { Extensions, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; -import { IPolicyService, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; import { Registry } from 'vs/platform/registry/common/platform'; export class DefaultConfiguration extends Disposable { @@ -91,10 +91,38 @@ export class PolicyConfiguration extends Disposable { super(); } + // TODO@sandy: make nice + private getPolicyDefinitions(): IStringDictionary { + const result: IStringDictionary = {}; + const configRegistry = Registry.as(Extensions.Configuration); + + for (const configuration of configRegistry.getConfigurations()) { + if (configuration.properties) { + for (const key in configuration.properties) { + const config = configuration.properties[key]; + const policy = config.policy; + + if (policy) { + if (config.type !== 'string' && config.type !== 'number') { + console.warn(`Policy ${policy.name} has unsupported type ${config.type}`); + continue; + } + + result[policy.name] = { type: config.type }; + } + } + } + } + + return result; + } + async initialize(): Promise { - await this.policyService.initialize(); + // TODO@sandy: go through registry + await this.policyService.registerPolicyDefinitions(this.getPolicyDefinitions()); this.update(this.defaultConfiguration.configurationModel.keys, false); this._register(this.policyService.onDidChange(policyNames => this.onDidChangePolicies(policyNames))); + // TODO@sandy: also make sure that policy configurations that are registered after initialize() also call registerPolicyDefinitions() this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties }) => this.update(properties, true))); return this._configurationModel; } diff --git a/src/vs/platform/policy/common/filePolicyService.ts b/src/vs/platform/policy/common/filePolicyService.ts index f46b55cb70fa4..f3786b547e81e 100644 --- a/src/vs/platform/policy/common/filePolicyService.ts +++ b/src/vs/platform/policy/common/filePolicyService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ThrottledDelayer } from 'vs/base/common/async'; +import { IStringDictionary } from 'vs/base/common/collections'; import { Emitter, Event } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -11,7 +12,7 @@ import { isObject } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; -import { IPolicyService, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; function keysDiff(a: Map, b: Map): string[] { const result: string[] = []; @@ -48,7 +49,8 @@ export class FilePolicyService extends Disposable implements IPolicyService { this._register(onDidChangePolicyFile(() => this.throttledDelayer.trigger(() => this.refresh()))); } - async initialize(): Promise<{ [name: PolicyName]: PolicyValue }> { + // TODO@sandeep respect only registered policy definitions + async registerPolicyDefinitions(policies: IStringDictionary): Promise> { await this.refresh(); return Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {}); } diff --git a/src/vs/platform/policy/common/policy.ts b/src/vs/platform/policy/common/policy.ts index 6446f06e9acf8..21ef3302bb09d 100644 --- a/src/vs/platform/policy/common/policy.ts +++ b/src/vs/platform/policy/common/policy.ts @@ -3,11 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IStringDictionary } from 'vs/base/common/collections'; import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export type PolicyName = string; export type PolicyValue = string | boolean; +export type PolicyDefinition = { type: 'string' | 'number' }; export const IPolicyService = createDecorator('policy'); @@ -15,41 +17,13 @@ export interface IPolicyService { readonly _serviceBrand: undefined; readonly onDidChange: Event; - initialize(): Promise<{ [name: PolicyName]: PolicyValue }>; + registerPolicyDefinitions(policies: IStringDictionary): Promise>; getPolicyValue(name: PolicyName): PolicyValue | undefined; } export class NullPolicyService implements IPolicyService { readonly _serviceBrand: undefined; readonly onDidChange = Event.None; - async initialize() { return {}; } + async registerPolicyDefinitions() { return {}; } getPolicyValue() { return undefined; } } - -export class MultiPolicyService implements IPolicyService { - - readonly _serviceBrand: undefined; - - readonly onDidChange: Event; - - constructor(private policyServices: readonly IPolicyService[]) { - this.onDidChange = Event.any(...policyServices.map(p => p.onDidChange)); - } - - async initialize() { - const result = await Promise.all(this.policyServices.map(p => p.initialize())); - return result.reduce((r, o) => ({ ...r, ...o }), {}); - } - - getPolicyValue(name: PolicyName) { - for (const policyService of this.policyServices) { - const result = policyService.getPolicyValue(name); - - if (typeof result !== 'undefined') { - return result; - } - } - - return undefined; - } -} diff --git a/src/vs/platform/policy/common/policyIpc.ts b/src/vs/platform/policy/common/policyIpc.ts index 2fdb1e62cb1ea..177b1d51ae69e 100644 --- a/src/vs/platform/policy/common/policyIpc.ts +++ b/src/vs/platform/policy/common/policyIpc.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IStringDictionary } from 'vs/base/common/collections'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IPolicyService, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; export class PolicyChannel implements IServerChannel { @@ -26,9 +27,9 @@ export class PolicyChannel implements IServerChannel { throw new Error(`Event not found: ${event}`); } - call(_: unknown, command: string): Promise { + call(_: unknown, command: string, arg?: any): Promise { switch (command) { - case 'initialize': return this.service.initialize(); + case 'initialize': return this.service.registerPolicyDefinitions(arg as IStringDictionary); } throw new Error(`Call not found: ${command}`); @@ -64,8 +65,8 @@ export class PolicyChannelClient implements IPolicyService { }); } - async initialize(): Promise<{ [name: PolicyName]: PolicyValue }> { - const result = await this.channel.call<{ [name: PolicyName]: PolicyValue }>('initialize'); + async registerPolicyDefinitions(policies: IStringDictionary): Promise> { + const result = await this.channel.call<{ [name: PolicyName]: PolicyValue }>('initialize', policies); for (const name in result) { this.policies.set(name, result[name]); diff --git a/src/vs/platform/policy/node/windowsPolicyService.ts b/src/vs/platform/policy/node/windowsPolicyService.ts index 36a083dd2d794..23439893dc857 100644 --- a/src/vs/platform/policy/node/windowsPolicyService.ts +++ b/src/vs/platform/policy/node/windowsPolicyService.ts @@ -3,13 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IPolicyService, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { createWatcher } from 'vscode-policy-watcher'; +import { IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { createWatcher, Watcher } from 'vscode-policy-watcher'; import { IStringDictionary } from 'vs/base/common/collections'; import { Iterable } from 'vs/base/common/iterator'; @@ -18,48 +15,21 @@ export class WindowsPolicyService extends Disposable implements IPolicyService { readonly _serviceBrand: undefined; private readonly policies = new Map(); - private init: Promise | undefined; + private init: Promise | undefined; private readonly _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; - constructor( - @IProductService private readonly productService: IProductService - ) { + constructor(private readonly productName: string) { super(); } - async initialize(): Promise<{ [name: PolicyName]: PolicyValue }> { + async registerPolicyDefinitions(policies: IStringDictionary): Promise> { if (!this.init) { this.init = new Promise(c => { - if (!this.productService.win32RegValueName) { - return; - } - - const policies: IStringDictionary<{ type: 'string' | 'number' }> = {}; - const configRegistry = Registry.as(Extensions.Configuration); - - for (const configuration of configRegistry.getConfigurations()) { - if (configuration.properties) { - for (const key in configuration.properties) { - const config = configuration.properties[key]; - const policy = config.policy; - - if (policy) { - if (config.type !== 'string' && config.type !== 'number') { - console.warn(`Policy ${policy.name} has unsupported type ${config.type}`); - continue; - } - - policies[policy.name] = { type: config.type }; - } - } - } - } - let first = true; - this._register(createWatcher(this.productService.win32RegValueName, policies, update => { + const watcher = createWatcher(this.productName, policies, update => { for (const key in update) { const value = update[key] as any; @@ -72,15 +42,25 @@ export class WindowsPolicyService extends Disposable implements IPolicyService { if (first) { first = false; - c(); + c(watcher); } else { this._onDidChange.fire(Object.keys(update)); } - })); + }); + + this._register(watcher); }); + + await this.init; + } else { + const watcher = await this.init; + const promise = Event.toPromise(this.onDidChange); + watcher.addPolicies(policies); + await promise; } - await this.init; + // TODO@joao: heavy cleanup + return Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {}); } From 9be07b370b7c0b443ee6227a79edb43300c47ded Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 17 May 2022 00:34:27 -0700 Subject: [PATCH 086/942] Post old version comment as bot rather than "github actions" (#149687) --- .github/workflows/on-open.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index 3c6617101e72e..af70c86caa0cd 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -28,6 +28,7 @@ jobs: uses: ./actions/new-release with: label: new release + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} labelColor: "006b75" labelDescription: Issues found in a recent release of VS Code From 5db383efe60fd80328658a9625d39b25de8ebafe Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 17 May 2022 09:35:35 +0200 Subject: [PATCH 087/942] use FilePolicyService --- src/vs/code/electron-main/main.ts | 3 ++- src/vs/code/node/cliProcessMain.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 16a7a727c1575..029a5c98f37e7 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -64,6 +64,7 @@ import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtil import { IThemeMainService, ThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; import { WindowsPolicyService } from 'vs/platform/policy/node/windowsPolicyService'; +import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; /** * The main VS Code entry point. @@ -169,7 +170,7 @@ class CodeMain { services.set(ILoggerService, new LoggerService(logService, fileService)); // Policy - const policyService = isWindows && productService.win32RegValueName ? new WindowsPolicyService(productService.win32RegValueName) : new NullPolicyService(); + const policyService = isWindows && productService.win32RegValueName ? new WindowsPolicyService(productService.win32RegValueName) : environmentMainService.policyFile ? new FilePolicyService(environmentMainService.policyFile, fileService, logService) : new NullPolicyService(); services.set(IPolicyService, policyService); // Configuration diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 4ea5092c119c4..184289c296eeb 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -40,6 +40,7 @@ import { ILocalizationsService } from 'vs/platform/localizations/common/localiza import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; import { ConsoleLogger, getLogLevel, ILogger, ILogService, LogLevel, MultiplexLogService } from 'vs/platform/log/common/log'; import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; +import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; import { WindowsPolicyService } from 'vs/platform/policy/node/windowsPolicyService'; import product from 'vs/platform/product/common/product'; @@ -132,7 +133,7 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, diskFileSystemProvider); // Policy - const policyService = isWindows && productService.win32RegValueName ? new WindowsPolicyService(productService.win32RegValueName) : new NullPolicyService(); + const policyService = isWindows && productService.win32RegValueName ? new WindowsPolicyService(productService.win32RegValueName) : environmentService.policyFile ? new FilePolicyService(environmentService.policyFile, fileService, logService) : new NullPolicyService(); services.set(IPolicyService, policyService); // Configuration From 83213f7bac0a66e30b9c921d5e8ac37b60f4ddb1 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 17 May 2022 10:42:31 +0200 Subject: [PATCH 088/942] Switch agent pools (#149691) --- build/azure-pipelines/product-build-pr.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/build/azure-pipelines/product-build-pr.yml b/build/azure-pipelines/product-build-pr.yml index db20311683dd0..53fc0fb3c2ec4 100644 --- a/build/azure-pipelines/product-build-pr.yml +++ b/build/azure-pipelines/product-build-pr.yml @@ -31,8 +31,7 @@ stages: - stage: Compile jobs: - job: Compile - pool: - vmImage: ubuntu-18.04 + pool: vscode-1es-vscode-linux-18.04 variables: VSCODE_ARCH: x64 steps: @@ -42,8 +41,7 @@ stages: - stage: LinuxServerDependencies dependsOn: [] - pool: - vmImage: ubuntu-18.04 + pool: vscode-1es-vscode-linux-18.04 jobs: - job: x64 container: centos7-devtoolset8-x64 @@ -58,8 +56,7 @@ stages: - stage: Windows dependsOn: - Compile - pool: - vmImage: windows-2019 + pool: vscode-1es-vscode-windows-2019 jobs: - job: Windows timeoutInMinutes: 120 @@ -74,8 +71,7 @@ stages: dependsOn: - Compile - LinuxServerDependencies - pool: - vmImage: ubuntu-18.04 + pool: vscode-1es-vscode-linux-18.04 jobs: - job: Linuxx64 container: vscode-bionic-x64 From d728de2e8d7eb57ba15b89c4f8b9741df32ab5e4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 17 May 2022 10:51:23 +0200 Subject: [PATCH 089/942] fix wordings --- .../common/abstractExtensionManagementService.ts | 2 +- .../workbench/contrib/extensions/browser/extensionsActions.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index d096eaa5ff732..a73af96a7c894 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -366,7 +366,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl const deprecated = report.deprecated ? report.deprecated[extension.identifier.id.toLowerCase()] : undefined; if (deprecated && !isBoolean(deprecated)) { - throw new ExtensionManagementError(nls.localize('unsupported prerelease extension', "Can't install '{0}' extension because it is deprecated. Instead use '{1}' extension.", extension.identifier.id, deprecated.displayName), ExtensionManagementErrorCode.UnsupportedPreRelease); + throw new ExtensionManagementError(nls.localize('unsupported prerelease extension', "Can't install '{0}' extension because it is deprecated. Use '{1}' extension instead.", extension.identifier.id, deprecated.displayName), ExtensionManagementErrorCode.UnsupportedPreRelease); } if (!await this.canInstall(extension)) { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 06033ae52a722..d3b1573086ed6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -2178,7 +2178,7 @@ export class ExtensionStatusAction extends ExtensionAction { } else { const link = `[${this.extension.deprecated.displayName}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.deprecated.id]))}`)})`; if (this.extension.state !== ExtensionState.Installed) { - this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('unsupported prerelease switch tooltip', "This extension has been deprecated. Instead use {0}.", link)) }, true); + this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('unsupported prerelease switch tooltip', "This extension has been deprecated. Use {0} instead.", link)) }, true); } } return; From 4904c88c9d97f3089f2a8a7dcb69f42efd9c10e7 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 17 May 2022 11:04:38 +0200 Subject: [PATCH 090/942] update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index acc64a5ce02a6..a0d15674aaa08 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.68.0", - "distro": "5e85c2493e771e21081d8f4188922225406ed06b", + "distro": "dec8abea6c1191c66ab4a551172264be30a76e26", "author": { "name": "Microsoft Corporation" }, @@ -229,4 +229,4 @@ "elliptic": "^6.5.3", "nwmatcher": "^1.4.4" } -} +} \ No newline at end of file From 38de430140abc164ca2980fc715c47a343da1b2d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 17 May 2022 12:41:45 +0200 Subject: [PATCH 091/942] Add scope for settings (#149696) --- extensions/git/package.json | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index c3c93123829ea..de341a095ee61 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1852,7 +1852,8 @@ "git.branchPrefix": { "type": "string", "description": "%config.branchPrefix%", - "default": "" + "default": "", + "scope": "resource" }, "git.branchProtection": { "type": "array", @@ -1860,7 +1861,8 @@ "items": { "type": "string" }, - "default": [] + "default": [], + "scope": "resource" }, "git.branchProtectionPrompt": { "type": "string", @@ -1875,7 +1877,8 @@ "%config.branchProtectionPrompt.alwaysCommitToNewBranch%", "%config.branchProtectionPrompt.alwaysPrompt%" ], - "default": "alwaysPrompt" + "default": "alwaysPrompt", + "scope": "resource" }, "git.branchValidationRegex": { "type": "string", @@ -1890,7 +1893,8 @@ "git.branchRandomName.enable": { "type": "boolean", "description": "%config.branchRandomNameEnable%", - "default": false + "default": false, + "scope": "resource" }, "git.branchRandomName.dictionary": { "type": "array", @@ -1901,7 +1905,8 @@ "default": [ "adjectives", "animals" - ] + ], + "scope": "resource" }, "git.confirmSync": { "type": "boolean", From f2380f2c909a795f9352ab1b04e17b473828c721 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 17 May 2022 12:49:52 +0200 Subject: [PATCH 092/942] Engineering - Run PR pipeline for the main branch (#149700) * Add trigger for the main branch * Add trigger for release branches --- build/azure-pipelines/product-build-pr.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/azure-pipelines/product-build-pr.yml b/build/azure-pipelines/product-build-pr.yml index 53fc0fb3c2ec4..21fbe295cc90a 100644 --- a/build/azure-pipelines/product-build-pr.yml +++ b/build/azure-pipelines/product-build-pr.yml @@ -1,4 +1,6 @@ -trigger: none +trigger: + - main + - release/* pr: branches: From 4182f3b2ad6f762f24df8da490bce0e403a0fdce Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 17 May 2022 13:46:32 +0200 Subject: [PATCH 093/942] show `detail` atop of code view --- .../mergeEditor/browser/media/mergeEditor.css | 3 ++- .../mergeEditor/browser/mergeEditor.ts | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css index 2e3929e236c83..0b9caf7faf892 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css +++ b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ .monaco-workbench .merge-editor .code-view > .title { - padding: 0 0 0 10px; + padding: 0 10px; height: 30px; display: flex; align-content: center; + justify-content: space-between; } .monaco-workbench .merge-editor .code-view > .title .monaco-icon-label { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index bb5cb6334e694..3cb4562da9e9f 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -170,9 +170,9 @@ export class MergeEditor extends EditorPane { this._sessionDisposables.clear(); const model = await input.resolve(); - this.input1View.setModel(model.input1, localize('yours', 'Yours'), model.input1Detail); - this.input2View.setModel(model.input2, localize('theirs', 'Theirs',), model.input2Detail); - this.inputResultView.setModel(model.result, localize('result', 'Result',), this._labelService.getUriLabel(model.result.uri, { relative: true })); + this.input1View.setModel(model.input1, localize('yours', 'Yours'), model.input1Detail, model.input1Description); + this.input2View.setModel(model.input2, localize('theirs', 'Theirs',), model.input2Detail, model.input1Description); + this.inputResultView.setModel(model.result, localize('result', 'Result',), this._labelService.getUriLabel(model.result.uri, { relative: true }), undefined); let input1Decorations = new Array(); let input2Decorations = new Array(); @@ -364,9 +364,9 @@ class CodeEditorView implements IView { // preferredHeight?: number | undefined; element: HTMLElement; - private _titleElement: HTMLElement; - private _editorElement: HTMLElement; - public _gutterDiv: HTMLElement; + private readonly _titleElement: HTMLElement; + private readonly _editorElement: HTMLElement; + public readonly _gutterDiv: HTMLElement; minimumWidth: number = 10; maximumWidth: number = Number.MAX_SAFE_INTEGER; @@ -378,7 +378,8 @@ class CodeEditorView implements IView { private readonly _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; - private _title: IconLabel; + private readonly _title: IconLabel; + private readonly _detail: IconLabel; public readonly editor: CodeEditorWidget; // private readonly gutter: EditorGutterWidget; @@ -406,11 +407,13 @@ class CodeEditorView implements IView { ); this._title = new IconLabel(this._titleElement, { supportIcons: true }); + this._detail = new IconLabel(this._titleElement, { supportIcons: true }); } - public setModel(model: ITextModel, title: string, description: string | undefined): void { + public setModel(model: ITextModel, title: string, description: string | undefined, detail: string | undefined): void { this.editor.setModel(model); this._title.setLabel(title, description); + this._detail.setLabel('', detail); } layout(width: number, height: number, top: number, left: number): void { From 7a511786185e49b39b85e71cf8a01006b3e1e4bb Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 17 May 2022 13:46:42 +0200 Subject: [PATCH 094/942] git should open merge editor with detail and description (tag and commit-hash) --- extensions/git/src/commands.ts | 47 ++++++++++++++++++++++++++++- extensions/git/src/git.ts | 9 ++++-- extensions/git/src/repository.ts | 12 ++------ extensions/git/src/test/git.test.ts | 6 ++++ 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 14138c13bc5e7..da619d4feb53c 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -15,7 +15,7 @@ import { Git, Stash } from './git'; import { Model } from './model'; import { Repository, Resource, ResourceGroupType } from './repository'; import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging'; -import { fromGitUri, toGitUri, isGitUri } from './uri'; +import { fromGitUri, toGitUri, isGitUri, toMergeUris } from './uri'; import { grep, isDescendant, pathEquals, relativePath } from './util'; import { LogLevel, OutputChannelLogger } from './log'; import { GitTimelineItem } from './timelineProvider'; @@ -405,6 +405,51 @@ export class CommandCenter { } } + @command('_git.openMergeEditor') + async openMergeEditor(uri: unknown) { + if (!(uri instanceof Uri)) { + return; + } + const repo = this.model.getRepository(uri); + if (!repo) { + return; + } + + + type InputData = { uri: Uri; detail?: string; description?: string }; + const mergeUris = toMergeUris(uri); + let input1: InputData = { uri: mergeUris.ours }; + let input2: InputData = { uri: mergeUris.theirs }; + + try { + const [head, mergeHead] = await Promise.all([repo.getCommit('HEAD'), repo.getCommit('MERGE_HEAD')]); + // ours (current branch and commit) + input1.detail = head.refNames.map(s => s.replace(/^HEAD ->/, '')).join(', '); + input1.description = head.hash.substring(0, 7); + + // theirs + input2.detail = mergeHead.refNames.join(', '); + input2.description = mergeHead.hash.substring(0, 7); + + } catch (error) { + // not so bad, can continue with just uris + console.error('FAILED to read HEAD, MERGE_HEAD commits'); + console.error(error); + } + + const options = { + ancestor: mergeUris.base, + input1, + input2, + output: uri + }; + + await commands.executeCommand( + '_open.mergeEditor', + options + ); + } + async cloneRepository(url?: string, parentPath?: string, options: { recursive?: boolean } = {}): Promise { if (!url || typeof url !== 'string') { url = await pickRemoteSource({ diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 8fdab7f659b87..a511db761a684 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -354,7 +354,7 @@ function sanitizePath(path: string): string { return path.replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`); } -const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%B'; +const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%D%n%B'; export interface ICloneOptions { readonly parentPath: string; @@ -660,6 +660,7 @@ export interface Commit { authorName?: string; authorEmail?: string; commitDate?: Date; + refNames: string[]; } export class GitStatusParser { @@ -790,7 +791,7 @@ export function parseGitmodules(raw: string): Submodule[] { return result; } -const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)/gm; +const commitRegex = /([0-9a-f]{40})\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)(?:\n([^]*?))?(?:\x00)/gm; export function parseGitCommits(data: string): Commit[] { let commits: Commit[] = []; @@ -801,6 +802,7 @@ export function parseGitCommits(data: string): Commit[] { let authorDate; let commitDate; let parents; + let refNames; let message; let match; @@ -810,7 +812,7 @@ export function parseGitCommits(data: string): Commit[] { break; } - [, ref, authorName, authorEmail, authorDate, commitDate, parents, message] = match; + [, ref, authorName, authorEmail, authorDate, commitDate, parents, refNames, message] = match; if (message[message.length - 1] === '\n') { message = message.substr(0, message.length - 1); @@ -825,6 +827,7 @@ export function parseGitCommits(data: string): Commit[] { authorName: ` ${authorName}`.substr(1), authorEmail: ` ${authorEmail}`.substr(1), commitDate: new Date(Number(commitDate) * 1000), + refNames: refNames.split(',').map(s => s.trim()) }); } while (true); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 4a81c8fac64b1..ebe1105ab94e3 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -13,7 +13,7 @@ import { AutoFetcher } from './autofetch'; import { debounce, memoize, throttle } from './decorators'; import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions } from './git'; import { StatusBarCommands } from './statusbar'; -import { toGitUri, toMergeUris } from './uri'; +import { toGitUri } from './uri'; import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util'; import { IFileWatcher, watch } from './watch'; import { LogLevel, OutputChannelLogger } from './log'; @@ -603,16 +603,10 @@ class ResourceCommandResolver { const title = this.getTitle(resource); if (!resource.leftUri && resource.rightUri && resource.type === Status.BOTH_MODIFIED) { - const mergeUris = toMergeUris(resource.rightUri); return { - command: '_open.mergeEditor', + command: '_git.openMergeEditor', title: localize('open.merge', "Open Merge"), - arguments: [{ - ancestor: mergeUris.base, - input1: mergeUris.ours, - input2: mergeUris.theirs, - output: resource.rightUri - }] + arguments: [resource.rightUri] }; } else if (!resource.leftUri) { return { diff --git a/extensions/git/src/test/git.test.ts b/extensions/git/src/test/git.test.ts index c0d0d2003c968..3b225157c3b9b 100644 --- a/extensions/git/src/test/git.test.ts +++ b/extensions/git/src/test/git.test.ts @@ -205,6 +205,7 @@ john.doe@mail.com 1580811030 1580811031 8e5a374372b8393906c7e380dbb09349c5385554 +main,branch This is a commit message.\x00`; assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_SINGLE_PARENT), [{ @@ -215,6 +216,7 @@ This is a commit message.\x00`; authorName: 'John Doe', authorEmail: 'john.doe@mail.com', commitDate: new Date(1580811031000), + refNames: ['main', 'branch'], }]); }); @@ -225,6 +227,7 @@ john.doe@mail.com 1580811030 1580811031 8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217 +main This is a commit message.\x00`; assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_MULTIPLE_PARENTS), [{ @@ -235,6 +238,7 @@ This is a commit message.\x00`; authorName: 'John Doe', authorEmail: 'john.doe@mail.com', commitDate: new Date(1580811031000), + refNames: ['main'], }]); }); @@ -245,6 +249,7 @@ john.doe@mail.com 1580811030 1580811031 +main This is a commit message.\x00`; assert.deepStrictEqual(parseGitCommits(GIT_OUTPUT_NO_PARENTS), [{ @@ -255,6 +260,7 @@ This is a commit message.\x00`; authorName: 'John Doe', authorEmail: 'john.doe@mail.com', commitDate: new Date(1580811031000), + refNames: ['main'], }]); }); }); From caa463191f8565dc85b2fbd90598dd22bf72bd8f Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 17 May 2022 15:20:02 +0200 Subject: [PATCH 095/942] add telemetry event comments and owner (#149070) --- .../html-language-features/server/src/htmlServer.ts | 9 --------- .../json-language-features/client/src/jsonClient.ts | 4 +++- extensions/json-language-features/package.json | 2 +- extensions/json-language-features/yarn.lock | 8 ++++---- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/extensions/html-language-features/server/src/htmlServer.ts b/extensions/html-language-features/server/src/htmlServer.ts index 6713556ae38ce..b3df5fc8e513f 100644 --- a/extensions/html-language-features/server/src/htmlServer.ts +++ b/extensions/html-language-features/server/src/htmlServer.ts @@ -321,15 +321,6 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } const doComplete = mode.doComplete; - if (mode.getId() !== 'html') { - /* __GDPR__ - "html.embbedded.complete" : { - "languageId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - connection.telemetry.logEvent({ key: 'html.embbedded.complete', value: { languageId: mode.getId() } }); - } - const settings = await getDocumentSettings(document, () => doComplete.length > 2); const documentContext = getDocumentContext(document.uri, workspaceFolders); return doComplete(document, textDocumentPosition.position, documentContext, settings); diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index 9bf63477ebe46..5246257adc8ba 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -242,7 +242,9 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua if (runtime.telemetry && uri.authority === 'schema.management.azure.com') { /* __GDPR__ "json.schema" : { - "schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "owner": "aeschli", + "comment": "Measure the use of the Azure resource manager schemas", + "schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The azure schema URL that was requested." } } */ runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath }); diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 25aef750a760a..4941fced2652a 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -147,7 +147,7 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "0.5.0", + "@vscode/extension-telemetry": "0.5.1", "request-light": "^0.5.8", "vscode-languageclient": "^7.0.0", "vscode-nls": "^5.0.0" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 98216da36e6f2..7e56398f06241 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== -"@vscode/extension-telemetry@0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.5.0.tgz#8214171e550393d577fc56326fa986c6800b831b" - integrity sha512-27FsgeVJvC4zVw7Ar3Ub+7vJswDt8RoBFpbgBwf8Xq/B2gaT8G6a+gkw3s2pQmjWGIqyu7TRA8e9rS8/vxv6NQ== +"@vscode/extension-telemetry@0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.5.1.tgz#20150976629663b3d33799a4ad25944a1535f7db" + integrity sha512-cvFq8drxdLRF8KN72WcV4lTEa9GqDiRwy9EbnYuoSCD9Jdk8zHFF49MmACC1qs4R9Ko/C1uMOmeLJmVi8EA0rQ== balanced-match@^1.0.0: version "1.0.0" From 4481afe7d56664fc95376e32c2476eece708ed58 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 17 May 2022 06:52:30 -0700 Subject: [PATCH 096/942] Split up link computer cases unit test Part of #149712 --- .../links/terminalUriLinkDetector.test.ts | 71 ++++++++++--------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalUriLinkDetector.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalUriLinkDetector.test.ts index 949fb57e709ed..448f145fda297 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalUriLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalUriLinkDetector.test.ts @@ -34,39 +34,44 @@ suite('Workbench - TerminalUriLinkDetector', () => { await assertLinkHelper(text, expected, detector, type); } - test('LinkComputer cases', async () => { - await assertLink(TerminalBuiltinLinkType.Url, 'x = "http://foo.bar";', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'x = (http://foo.bar);', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'x = \'http://foo.bar\';', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'x = http://foo.bar ;', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'x = ;', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'x = {http://foo.bar};', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.Url, '(see http://foo.bar)', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.Url, '[see http://foo.bar]', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.Url, '{see http://foo.bar}', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.Url, '', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'http://foo.bar', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.Url, '// Click here to learn more. https://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409', [{ range: [[30, 1], [7, 2]], text: 'https://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409' }]); - await assertLink(TerminalBuiltinLinkType.Url, '// Click here to learn more. https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx', [{ range: [[30, 1], [28, 2]], text: 'https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx' }]); - await assertLink(TerminalBuiltinLinkType.Url, '// https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Scripts/selectNodeVersion.js', [{ range: [[4, 1], [9, 2]], text: 'https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Scripts/selectNodeVersion.js' }]); - await assertLink(TerminalBuiltinLinkType.Url, '', [{ range: [[49, 1], [14, 2]], text: 'https://go.microsoft.com/fwlink/?LinkId=166007' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'For instructions, see https://go.microsoft.com/fwlink/?LinkId=166007.', [{ range: [[23, 1], [68, 1]], text: 'https://go.microsoft.com/fwlink/?LinkId=166007' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'For instructions, see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx.', [{ range: [[23, 1], [21, 2]], text: 'https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'x = "https://en.wikipedia.org/wiki/Zürich";', [{ range: [[6, 1], [41, 1]], text: 'https://en.wikipedia.org/wiki/Zürich' }]); - await assertLink(TerminalBuiltinLinkType.Url, '請參閱 http://go.microsoft.com/fwlink/?LinkId=761051。', [{ range: [[8, 1], [53, 1]], text: 'http://go.microsoft.com/fwlink/?LinkId=761051' }]); - await assertLink(TerminalBuiltinLinkType.Url, '(請參閱 http://go.microsoft.com/fwlink/?LinkId=761051)', [{ range: [[10, 1], [55, 1]], text: 'http://go.microsoft.com/fwlink/?LinkId=761051' }]); - await assertLink(TerminalBuiltinLinkType.LocalFile, 'x = "file:///foo.bar";', [{ range: [[6, 1], [20, 1]], text: 'file:///foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.LocalFile, 'x = "file://c:/foo.bar";', [{ range: [[6, 1], [22, 1]], text: 'file://c:/foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.LocalFile, 'x = "file://shares/foo.bar";', [{ range: [[6, 1], [26, 1]], text: 'file://shares/foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.LocalFile, 'x = "file://shäres/foo.bar";', [{ range: [[6, 1], [26, 1]], text: 'file://shäres/foo.bar' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'Some text, then http://www.bing.com.', [{ range: [[17, 1], [35, 1]], text: 'http://www.bing.com' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'let url = `http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items`;', [{ range: [[12, 1], [78, 1]], text: 'http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items' }]); - await assertLink(TerminalBuiltinLinkType.Url, '7. At this point, ServiceMain has been called. There is no functionality presently in ServiceMain, but you can consult the [MSDN documentation](https://msdn.microsoft.com/en-us/library/windows/desktop/ms687414(v=vs.85).aspx) to add functionality as desired!', [{ range: [[66, 2], [64, 3]], text: 'https://msdn.microsoft.com/en-us/library/windows/desktop/ms687414(v=vs.85).aspx' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'let x = "http://[::1]:5000/connect/token"', [{ range: [[10, 1], [40, 1]], text: 'http://[::1]:5000/connect/token' }]); - await assertLink(TerminalBuiltinLinkType.Url, '2. Navigate to **https://portal.azure.com**', [{ range: [[18, 1], [41, 1]], text: 'https://portal.azure.com' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'POST|https://portal.azure.com|2019-12-05|', [{ range: [[6, 1], [29, 1]], text: 'https://portal.azure.com' }]); - await assertLink(TerminalBuiltinLinkType.Url, 'aa https://foo.bar/[this is foo site] aa', [{ range: [[5, 1], [38, 1]], text: 'https://foo.bar/[this is foo site]' }]); - }); + const linkComputerCases: [TerminalBuiltinLinkType, string, (Pick & { range: [number, number][] })[]][] = [ + [TerminalBuiltinLinkType.Url, 'x = "http://foo.bar";', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]], + [TerminalBuiltinLinkType.Url, 'x = (http://foo.bar);', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]], + [TerminalBuiltinLinkType.Url, 'x = \'http://foo.bar\';', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]], + [TerminalBuiltinLinkType.Url, 'x = http://foo.bar ;', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]], + [TerminalBuiltinLinkType.Url, 'x = ;', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]], + [TerminalBuiltinLinkType.Url, 'x = {http://foo.bar};', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]], + [TerminalBuiltinLinkType.Url, '(see http://foo.bar)', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]], + [TerminalBuiltinLinkType.Url, '[see http://foo.bar]', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]], + [TerminalBuiltinLinkType.Url, '{see http://foo.bar}', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]], + [TerminalBuiltinLinkType.Url, '', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]], + [TerminalBuiltinLinkType.Url, 'http://foo.bar', [{ range: [[6, 1], [19, 1]], text: 'http://foo.bar' }]], + [TerminalBuiltinLinkType.Url, '// Click here to learn more. https://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409', [{ range: [[30, 1], [7, 2]], text: 'https://go.microsoft.com/fwlink/?LinkID=513275&clcid=0x409' }]], + [TerminalBuiltinLinkType.Url, '// Click here to learn more. https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx', [{ range: [[30, 1], [28, 2]], text: 'https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx' }]], + [TerminalBuiltinLinkType.Url, '// https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Scripts/selectNodeVersion.js', [{ range: [[4, 1], [9, 2]], text: 'https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Scripts/selectNodeVersion.js' }]], + [TerminalBuiltinLinkType.Url, '', [{ range: [[49, 1], [14, 2]], text: 'https://go.microsoft.com/fwlink/?LinkId=166007' }]], + [TerminalBuiltinLinkType.Url, 'For instructions, see https://go.microsoft.com/fwlink/?LinkId=166007.', [{ range: [[23, 1], [68, 1]], text: 'https://go.microsoft.com/fwlink/?LinkId=166007' }]], + [TerminalBuiltinLinkType.Url, 'For instructions, see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx.', [{ range: [[23, 1], [21, 2]], text: 'https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx' }]], + [TerminalBuiltinLinkType.Url, 'x = "https://en.wikipedia.org/wiki/Zürich";', [{ range: [[6, 1], [41, 1]], text: 'https://en.wikipedia.org/wiki/Zürich' }]], + [TerminalBuiltinLinkType.Url, '請參閱 http://go.microsoft.com/fwlink/?LinkId=761051。', [{ range: [[8, 1], [53, 1]], text: 'http://go.microsoft.com/fwlink/?LinkId=761051' }]], + [TerminalBuiltinLinkType.Url, '(請參閱 http://go.microsoft.com/fwlink/?LinkId=761051)', [{ range: [[10, 1], [55, 1]], text: 'http://go.microsoft.com/fwlink/?LinkId=761051' }]], + [TerminalBuiltinLinkType.LocalFile, 'x = "file:///foo.bar";', [{ range: [[6, 1], [20, 1]], text: 'file:///foo.bar' }]], + [TerminalBuiltinLinkType.LocalFile, 'x = "file://c:/foo.bar";', [{ range: [[6, 1], [22, 1]], text: 'file://c:/foo.bar' }]], + [TerminalBuiltinLinkType.LocalFile, 'x = "file://shares/foo.bar";', [{ range: [[6, 1], [26, 1]], text: 'file://shares/foo.bar' }]], + [TerminalBuiltinLinkType.LocalFile, 'x = "file://shäres/foo.bar";', [{ range: [[6, 1], [26, 1]], text: 'file://shäres/foo.bar' }]], + [TerminalBuiltinLinkType.Url, 'Some text, then http://www.bing.com.', [{ range: [[17, 1], [35, 1]], text: 'http://www.bing.com' }]], + [TerminalBuiltinLinkType.Url, 'let url = `http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items`;', [{ range: [[12, 1], [78, 1]], text: 'http://***/_api/web/lists/GetByTitle(\'Teambuildingaanvragen\')/items' }]], + [TerminalBuiltinLinkType.Url, '7. At this point, ServiceMain has been called. There is no functionality presently in ServiceMain, but you can consult the [MSDN documentation](https://msdn.microsoft.com/en-us/library/windows/desktop/ms687414(v=vs.85).aspx) to add functionality as desired!', [{ range: [[66, 2], [64, 3]], text: 'https://msdn.microsoft.com/en-us/library/windows/desktop/ms687414(v=vs.85).aspx' }]], + [TerminalBuiltinLinkType.Url, 'let x = "http://[::1]:5000/connect/token"', [{ range: [[10, 1], [40, 1]], text: 'http://[::1]:5000/connect/token' }]], + [TerminalBuiltinLinkType.Url, '2. Navigate to **https://portal.azure.com**', [{ range: [[18, 1], [41, 1]], text: 'https://portal.azure.com' }]], + [TerminalBuiltinLinkType.Url, 'POST|https://portal.azure.com|2019-12-05|', [{ range: [[6, 1], [29, 1]], text: 'https://portal.azure.com' }]], + [TerminalBuiltinLinkType.Url, 'aa https://foo.bar/[this is foo site] aa', [{ range: [[5, 1], [38, 1]], text: 'https://foo.bar/[this is foo site]' }]] + ]; + for (const c of linkComputerCases) { + test('link computer case: `' + c[1] + '`', async () => { + await assertLink(c[0], c[1], c[2]); + }); + } test('should support multiple link results', async () => { await assertLink(TerminalBuiltinLinkType.Url, 'http://foo.bar http://bar.foo', [ From 562c9f6d8b7d1aac3a49aebb6e26fbeabe55dcc0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 17 May 2022 16:01:10 +0200 Subject: [PATCH 097/942] register policy definitions from policy configuration --- .../sharedProcess/sharedProcessMain.ts | 2 +- src/vs/code/electron-main/main.ts | 6 +- src/vs/code/node/cliProcessMain.ts | 2 +- .../common/configurationService.ts | 4 +- .../configuration/common/configurations.ts | 60 +++++++++---------- .../test/common/configurationService.test.ts | 22 +++---- .../test/common/policyConfiguration.test.ts | 2 +- .../policy/common/filePolicyService.ts | 17 +++++- .../test/common/userDataSyncClient.ts | 2 +- .../node/remoteExtensionHostAgentCli.ts | 2 +- src/vs/server/node/serverServices.ts | 2 +- .../browser/configurationService.ts | 2 +- 12 files changed, 68 insertions(+), 55 deletions(-) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 6b07e7c7ddd83..8a2cfbc36dcd1 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -228,7 +228,7 @@ class SharedProcessMain extends Disposable { fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider); // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, policyService)); + const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, policyService, logService)); services.set(IConfigurationService, configurationService); // Storage (global access only) diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 029a5c98f37e7..d1563aebe638d 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -170,11 +170,13 @@ class CodeMain { services.set(ILoggerService, new LoggerService(logService, fileService)); // Policy - const policyService = isWindows && productService.win32RegValueName ? new WindowsPolicyService(productService.win32RegValueName) : environmentMainService.policyFile ? new FilePolicyService(environmentMainService.policyFile, fileService, logService) : new NullPolicyService(); + const policyService = isWindows && productService.win32RegValueName ? new WindowsPolicyService(productService.win32RegValueName) + : environmentMainService.policyFile ? new FilePolicyService(environmentMainService.policyFile, fileService, logService) + : new NullPolicyService(); services.set(IPolicyService, policyService); // Configuration - const configurationService = new ConfigurationService(environmentMainService.settingsResource, fileService, policyService); + const configurationService = new ConfigurationService(environmentMainService.settingsResource, fileService, policyService, logService); services.set(IConfigurationService, configurationService); // Lifecycle diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 184289c296eeb..f0151458057a5 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -137,7 +137,7 @@ class CliMain extends Disposable { services.set(IPolicyService, policyService); // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, policyService)); + const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, policyService, logService)); services.set(IConfigurationService, configurationService); // Init config diff --git a/src/vs/platform/configuration/common/configurationService.ts b/src/vs/platform/configuration/common/configurationService.ts index 3d0422d38c1d7..a1bd9906ee0af 100644 --- a/src/vs/platform/configuration/common/configurationService.ts +++ b/src/vs/platform/configuration/common/configurationService.ts @@ -12,6 +12,7 @@ import { ConfigurationTarget, IConfigurationChange, IConfigurationChangeEvent, I import { Configuration, ConfigurationChangeEvent, ConfigurationModel, UserSettings } from 'vs/platform/configuration/common/configurationModels'; import { DefaultConfiguration, PolicyConfiguration } from 'vs/platform/configuration/common/configurations'; import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; import { IPolicyService } from 'vs/platform/policy/common/policy'; export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable { @@ -31,10 +32,11 @@ export class ConfigurationService extends Disposable implements IConfigurationSe private readonly settingsResource: URI, fileService: IFileService, policyService: IPolicyService, + logService: ILogService, ) { super(); this.defaultConfiguration = this._register(new DefaultConfiguration()); - this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, policyService)); + this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, policyService, logService)); this.userConfiguration = this._register(new UserSettings(this.settingsResource, undefined, extUriBiasedIgnorePathCase, fileService)); this.configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, new ConfigurationModel()); diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index aed37d85e4785..9cd8e84917b46 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -8,9 +8,11 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { equals } from 'vs/base/common/objects'; +import { isEmptyObject } from 'vs/base/common/types'; import { addToValueTree, IOverrides, toValuesTree } from 'vs/platform/configuration/common/configuration'; import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { Extensions, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; +import { ILogService } from 'vs/platform/log/common/log'; import { IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -86,45 +88,41 @@ export class PolicyConfiguration extends Disposable { constructor( private readonly defaultConfiguration: DefaultConfiguration, - @IPolicyService private readonly policyService: IPolicyService + @IPolicyService private readonly policyService: IPolicyService, + @ILogService private readonly logService: ILogService ) { super(); } - // TODO@sandy: make nice - private getPolicyDefinitions(): IStringDictionary { - const result: IStringDictionary = {}; - const configRegistry = Registry.as(Extensions.Configuration); - - for (const configuration of configRegistry.getConfigurations()) { - if (configuration.properties) { - for (const key in configuration.properties) { - const config = configuration.properties[key]; - const policy = config.policy; - - if (policy) { - if (config.type !== 'string' && config.type !== 'number') { - console.warn(`Policy ${policy.name} has unsupported type ${config.type}`); - continue; - } - - result[policy.name] = { type: config.type }; - } + async initialize(): Promise { + await this.registerPolicyDefinitionsAndUpdate(this.defaultConfiguration.configurationModel.keys, false); + this._register(this.policyService.onDidChange(policyNames => this.onDidChangePolicies(policyNames))); + this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties }) => this.registerPolicyDefinitionsAndUpdate(properties, true))); + return this._configurationModel; + } + + private async registerPolicyDefinitionsAndUpdate(properties: string[], trigger: boolean): Promise { + const policyDefinitions: IStringDictionary = {}; + const keys: string[] = []; + const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); + + for (const key of properties) { + const config = configurationProperties[key]; + const policy = config.policy; + if (policy) { + if (config.type !== 'string' && config.type !== 'number') { + this.logService.warn(`Policy ${policy.name} has unsupported type ${config.type}`); + continue; } + keys.push(key); + policyDefinitions[policy.name] = { type: config.type }; } } - return result; - } - - async initialize(): Promise { - // TODO@sandy: go through registry - await this.policyService.registerPolicyDefinitions(this.getPolicyDefinitions()); - this.update(this.defaultConfiguration.configurationModel.keys, false); - this._register(this.policyService.onDidChange(policyNames => this.onDidChangePolicies(policyNames))); - // TODO@sandy: also make sure that policy configurations that are registered after initialize() also call registerPolicyDefinitions() - this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties }) => this.update(properties, true))); - return this._configurationModel; + if (!isEmptyObject(policyDefinitions)) { + await this.policyService.registerPolicyDefinitions(policyDefinitions); + this.update(keys, trigger); + } } private onDidChangePolicies(policyNames: readonly PolicyName[]): void { diff --git a/src/vs/platform/configuration/test/common/configurationService.test.ts b/src/vs/platform/configuration/test/common/configurationService.test.ts index d49af5f41f69a..3b1155bf20aaf 100644 --- a/src/vs/platform/configuration/test/common/configurationService.test.ts +++ b/src/vs/platform/configuration/test/common/configurationService.test.ts @@ -37,7 +37,7 @@ suite('ConfigurationService', () => { test('simple', async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); await testObject.initialize(); const config = testObject.getValue<{ foo: string; @@ -50,7 +50,7 @@ suite('ConfigurationService', () => { test('config gets flattened', async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); await testObject.initialize(); const config = testObject.getValue<{ testworkbench: { @@ -69,7 +69,7 @@ suite('ConfigurationService', () => { test('error case does not explode', async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString(',,,,')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); await testObject.initialize(); const config = testObject.getValue<{ foo: string; @@ -79,7 +79,7 @@ suite('ConfigurationService', () => { }); test('missing file does not explode', async () => { - const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, new NullPolicyService())); + const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, new NullPolicyService(), new NullLogService())); await testObject.initialize(); const config = testObject.getValue<{ foo: string }>(); @@ -88,7 +88,7 @@ suite('ConfigurationService', () => { }); test('trigger configuration change event when file does not exist', async () => { - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); await testObject.initialize(); return new Promise((c, e) => { disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(() => { @@ -101,7 +101,7 @@ suite('ConfigurationService', () => { }); test('trigger configuration change event when file exists', async () => { - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); await testObject.initialize(); @@ -117,7 +117,7 @@ suite('ConfigurationService', () => { test('reloadConfiguration', async () => { await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); await testObject.initialize(); let config = testObject.getValue<{ foo: string; @@ -156,7 +156,7 @@ suite('ConfigurationService', () => { } }); - let testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, new NullPolicyService())); + let testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService, new NullPolicyService(), new NullLogService())); await testObject.initialize(); let setting = testObject.getValue(); @@ -164,7 +164,7 @@ suite('ConfigurationService', () => { assert.strictEqual(setting.configuration.service.testSetting, 'isSet'); await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); - testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); + testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); setting = testObject.getValue(); @@ -192,7 +192,7 @@ suite('ConfigurationService', () => { } }); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); testObject.initialize(); let res = testObject.inspect('something.missing'); @@ -227,7 +227,7 @@ suite('ConfigurationService', () => { } }); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService())); + const testObject = disposables.add(new ConfigurationService(settingsResource, fileService, new NullPolicyService(), new NullLogService())); testObject.initialize(); let res = testObject.inspect('lookup.service.testNullSetting'); diff --git a/src/vs/platform/configuration/test/common/policyConfiguration.test.ts b/src/vs/platform/configuration/test/common/policyConfiguration.test.ts index 784334ffa7cba..116322e570d8b 100644 --- a/src/vs/platform/configuration/test/common/policyConfiguration.test.ts +++ b/src/vs/platform/configuration/test/common/policyConfiguration.test.ts @@ -65,7 +65,7 @@ suite('PolicyConfiguration', () => { const diskFileSystemProvider = disposables.add(new InMemoryFileSystemProvider()); fileService.registerProvider(policyFile.scheme, diskFileSystemProvider); policyService = new FilePolicyService(policyFile, fileService, new NullLogService()); - testObject = disposables.add(new PolicyConfiguration(defaultConfiguration, policyService)); + testObject = disposables.add(new PolicyConfiguration(defaultConfiguration, policyService, new NullLogService())); }); teardown(() => disposables.clear()); diff --git a/src/vs/platform/policy/common/filePolicyService.ts b/src/vs/platform/policy/common/filePolicyService.ts index f3786b547e81e..4dadb9a1716a7 100644 --- a/src/vs/platform/policy/common/filePolicyService.ts +++ b/src/vs/platform/policy/common/filePolicyService.ts @@ -30,6 +30,7 @@ export class FilePolicyService extends Disposable implements IPolicyService { readonly _serviceBrand: undefined; + private readonly policyNames: Set = new Set(); private policies = new Map(); private readonly _onDidChange = new Emitter(); @@ -49,9 +50,17 @@ export class FilePolicyService extends Disposable implements IPolicyService { this._register(onDidChangePolicyFile(() => this.throttledDelayer.trigger(() => this.refresh()))); } - // TODO@sandeep respect only registered policy definitions async registerPolicyDefinitions(policies: IStringDictionary): Promise> { - await this.refresh(); + let hasNewPolicies = false; + for (const key of Object.keys(policies)) { + if (!this.policyNames.has(key)) { + hasNewPolicies = true; + this.policyNames.add(key); + } + } + if (hasNewPolicies) { + await this.refresh(); + } return Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {}); } @@ -67,7 +76,9 @@ export class FilePolicyService extends Disposable implements IPolicyService { } for (const key of Object.keys(raw)) { - policies.set(key, raw[key]); + if (this.policyNames.has(key)) { + policies.set(key, raw[key]); + } } } catch (error) { if ((error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) { diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 2d888a524c993..12abf923f282a 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -87,7 +87,7 @@ export class UserDataSyncClient extends Disposable { this.instantiationService.stub(IStorageService, this._register(new InMemoryStorageService())); - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, new NullPolicyService())); + const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, new NullPolicyService(), logService)); await configurationService.initialize(); this.instantiationService.stub(IConfigurationService, configurationService); this.instantiationService.stub(IUriIdentityService, this.instantiationService.createInstance(UriIdentityService)); diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index bfd5928609d6b..36c01538db41d 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -92,7 +92,7 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, this._register(new DiskFileSystemProvider(logService))); // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, new NullPolicyService())); + const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService, new NullPolicyService(), logService)); await configurationService.initialize(); services.set(IConfigurationService, configurationService); diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index c3ae3e6642314..9885e9a19829d 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -108,7 +108,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken services.set(IFileService, fileService); fileService.registerProvider(Schemas.file, disposables.add(new DiskFileSystemProvider(logService))); - const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService, new NullPolicyService()); + const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService, new NullPolicyService(), logService); services.set(IConfigurationService, configurationService); const extensionHostStatusService = new ExtensionHostStatusService(); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index c4e251483be08..26f97a95502a9 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -114,7 +114,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat this.initRemoteUserConfigurationBarrier = new Barrier(); this.completeWorkspaceBarrier = new Barrier(); this.defaultConfiguration = this._register(new DefaultConfiguration(configurationCache, environmentService)); - this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, policyService)); + this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, policyService, logService)); this.configurationCache = configurationCache; this.fileService = fileService; this.uriIdentityService = uriIdentityService; From 8463cd27b3283fda5eeef2e46889ef958ff9710b Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 17 May 2022 16:06:59 +0200 Subject: [PATCH 098/942] Reduce usages of `editor.deltaDecorations` (#149718) --- .../editor/browser/widget/codeEditorWidget.ts | 4 ++ src/vs/editor/common/editorCommon.ts | 6 ++- .../browser/bracketMatching.ts | 11 ++-- .../colorPicker/browser/colorDetector.ts | 12 ++--- .../browser/colorHoverParticipant.ts | 2 +- .../linkedEditing/browser/linkedEditing.ts | 35 +++++++------ .../browser/unicodeHighlighter.ts | 51 +++++++------------ src/vs/monaco.d.ts | 4 ++ src/vs/workbench/browser/codeeditor.ts | 7 ++- .../browser/breakpointEditorContribution.ts | 25 ++++----- 10 files changed, 79 insertions(+), 78 deletions(-) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 769e04d8b8917..801501376ce64 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -2179,6 +2179,10 @@ class EditorDecorationsCollection implements editorCommon.IEditorDecorationsColl return result; } + public has(decoration: IModelDecoration): boolean { + return this._decorationIds.includes(decoration.id); + } + public clear(): void { if (this._decorationIds.length === 0) { // nothing to do diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 8fbe1627bed54..4b8f8d58fbcf6 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -11,7 +11,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; -import { IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness, IValidEditOperation, IModelDeltaDecoration } from 'vs/editor/common/model'; +import { IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness, IValidEditOperation, IModelDeltaDecoration, IModelDecoration } from 'vs/editor/common/model'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { IDimension } from 'vs/editor/common/core/dimension'; import { IModelDecorationsChangedEvent } from 'vs/editor/common/textModelEvents'; @@ -538,6 +538,10 @@ export interface IEditorDecorationsCollection { * Get all ranges for decorations. */ getRanges(): Range[]; + /** + * Determine if a decoration is in this collection. + */ + has(decoration: IModelDecoration): boolean; /** * Replace all previous decorations with `newDecorations`. */ diff --git a/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts index 37b38180c3a2b..0f917f1b5c60c 100644 --- a/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts @@ -13,7 +13,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IEditorContribution, IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -105,7 +105,7 @@ export class BracketMatchingController extends Disposable implements IEditorCont private _lastBracketsData: BracketsData[]; private _lastVersionId: number; - private _decorations: string[]; + private readonly _decorations: IEditorDecorationsCollection; private readonly _updateBracketsSoon: RunOnceScheduler; private _matchBrackets: 'never' | 'near' | 'always'; @@ -116,7 +116,7 @@ export class BracketMatchingController extends Disposable implements IEditorCont this._editor = editor; this._lastBracketsData = []; this._lastVersionId = 0; - this._decorations = []; + this._decorations = this._editor.createDecorationsCollection(); this._updateBracketsSoon = this._register(new RunOnceScheduler(() => this._updateBrackets(), 50)); this._matchBrackets = this._editor.getOption(EditorOption.matchBrackets); @@ -136,7 +136,6 @@ export class BracketMatchingController extends Disposable implements IEditorCont })); this._register(editor.onDidChangeModel((e) => { this._lastBracketsData = []; - this._decorations = []; this._updateBracketsSoon.schedule(); })); this._register(editor.onDidChangeModelLanguageConfiguration((e) => { @@ -146,7 +145,7 @@ export class BracketMatchingController extends Disposable implements IEditorCont this._register(editor.onDidChangeConfiguration((e) => { if (e.hasChanged(EditorOption.matchBrackets)) { this._matchBrackets = this._editor.getOption(EditorOption.matchBrackets); - this._decorations = this._editor.deltaDecorations(this._decorations, []); + this._decorations.clear(); this._lastBracketsData = []; this._lastVersionId = 0; this._updateBracketsSoon.schedule(); @@ -285,7 +284,7 @@ export class BracketMatchingController extends Disposable implements IEditorCont } } - this._decorations = this._editor.deltaDecorations(this._decorations, newDecorations); + this._decorations.set(newDecorations); } private _recomputeBrackets(): void { diff --git a/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts b/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts index 2a312a7c9b8c2..cf9a2bfcd474b 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts @@ -16,7 +16,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { IModelDeltaDecoration } from 'vs/editor/common/model'; +import { IModelDecoration, IModelDeltaDecoration } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; @@ -41,7 +41,7 @@ export class ColorDetector extends Disposable implements IEditorContribution { private _decorationsIds: string[] = []; private _colorDatas = new Map(); - private _colorDecoratorIds: ReadonlySet = new Set(); + private readonly _colorDecoratorIds = this._editor.createDecorationsCollection(); private _isEnabled: boolean; @@ -215,12 +215,12 @@ export class ColorDetector extends Disposable implements IEditorContribution { }); } - this._colorDecoratorIds = new Set(this._editor.deltaDecorations([...this._colorDecoratorIds], decorations)); + this._colorDecoratorIds.set(decorations); } private removeAllDecorations(): void { this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, []); - this._colorDecoratorIds = new Set(this._editor.deltaDecorations([...this._colorDecoratorIds], [])); + this._colorDecoratorIds.clear(); this._colorDecorationClassRefs.clear(); } @@ -241,8 +241,8 @@ export class ColorDetector extends Disposable implements IEditorContribution { return this._colorDatas.get(decorations[0].id)!; } - isColorDecorationId(decorationId: string): boolean { - return this._colorDecoratorIds.has(decorationId); + isColorDecoration(decoration: IModelDecoration): boolean { + return this._colorDecoratorIds.has(decoration); } } diff --git a/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts b/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts index 406407f5fd454..adb039eecf6c1 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts @@ -70,7 +70,7 @@ export class ColorHoverParticipant implements IEditorHoverParticipant this.updateRanges(), this._debounceDuration ?? this._debounceInformation.get(model)); }; const rangeSyncScheduler = new Delayer(0); - const triggerRangeSync = (decorations: string[]) => { - this._rangeSyncTriggerPromise = rangeSyncScheduler.trigger(() => this._syncRanges(decorations)); + const triggerRangeSync = (token: number) => { + this._rangeSyncTriggerPromise = rangeSyncScheduler.trigger(() => this._syncRanges(token)); }; this._localToDispose.add(this._editor.onDidChangeCursorPosition(() => { triggerRangeUpdate(); @@ -156,9 +158,9 @@ export class LinkedEditingContribution extends Disposable implements IEditorCont this._localToDispose.add(this._editor.onDidChangeModelContent((e) => { if (!this._ignoreChangeEvent) { if (this._currentDecorations.length > 0) { - const referenceRange = model.getDecorationRange(this._currentDecorations[0]); + const referenceRange = this._currentDecorations.getRange(0); if (referenceRange && e.changes.every(c => referenceRange.intersectRanges(c.range))) { - triggerRangeSync(this._currentDecorations); + triggerRangeSync(this._syncRangesToken); return; } } @@ -174,15 +176,15 @@ export class LinkedEditingContribution extends Disposable implements IEditorCont this.updateRanges(); } - private _syncRanges(decorations: string[]): void { + private _syncRanges(token: number): void { // dalayed invocation, make sure we're still on - if (!this._editor.hasModel() || decorations !== this._currentDecorations || decorations.length === 0) { + if (!this._editor.hasModel() || token !== this._syncRangesToken || this._currentDecorations.length === 0) { // nothing to do return; } const model = this._editor.getModel(); - const referenceRange = model.getDecorationRange(decorations[0]); + const referenceRange = this._currentDecorations.getRange(0); if (!referenceRange || referenceRange.startLineNumber !== referenceRange.endLineNumber) { return this.clearRanges(); @@ -198,8 +200,8 @@ export class LinkedEditingContribution extends Disposable implements IEditorCont } let edits: ISingleEditOperation[] = []; - for (let i = 1, len = decorations.length; i < len; i++) { - const mirrorRange = model.getDecorationRange(decorations[i]); + for (let i = 1, len = this._currentDecorations.length; i < len; i++) { + const mirrorRange = this._currentDecorations.getRange(i); if (!mirrorRange) { continue; } @@ -255,7 +257,7 @@ export class LinkedEditingContribution extends Disposable implements IEditorCont public clearRanges(): void { this._visibleContextKey.set(false); - this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, []); + this._currentDecorations.clear(); if (this._currentRequest) { this._currentRequest.cancel(); this._currentRequest = null; @@ -290,8 +292,8 @@ export class LinkedEditingContribution extends Disposable implements IEditorCont if (position.equals(this._currentRequestPosition)) { return; // same position } - if (this._currentDecorations && this._currentDecorations.length > 0) { - const range = model.getDecorationRange(this._currentDecorations[0]); + if (this._currentDecorations.length > 0) { + const range = this._currentDecorations.getRange(0); if (range && range.containsPosition(position)) { return; // just moving inside the existing primary range } @@ -341,7 +343,8 @@ export class LinkedEditingContribution extends Disposable implements IEditorCont const decorations: IModelDeltaDecoration[] = ranges.map(range => ({ range: range, options: LinkedEditingContribution.DECORATION })); this._visibleContextKey.set(true); - this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, decorations); + this._currentDecorations.set(decorations); + this._syncRangesToken++; // cancel any pending syncRanges call } catch (err) { if (!isCancellationError(err)) { onUnexpectedError(err); diff --git a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts index 326e656718051..44b682dea3876 100644 --- a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts +++ b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts @@ -179,9 +179,9 @@ export class UnicodeHighlighter extends Disposable implements IEditorContributio } } - public getDecorationInfo(decorationId: string): UnicodeHighlighterDecorationInfo | null { + public getDecorationInfo(decoration: IModelDecoration): UnicodeHighlighterDecorationInfo | null { if (this._highlighter) { - return this._highlighter.getDecorationInfo(decorationId); + return this._highlighter.getDecorationInfo(decoration); } return null; } @@ -214,7 +214,7 @@ function resolveOptions(trusted: boolean, options: InternalUnicodeHighlightOptio class DocumentUnicodeHighlighter extends Disposable { private readonly _model: ITextModel = this._editor.getModel(); private readonly _updateSoon: RunOnceScheduler; - private _decorationIds = new Set(); + private _decorations = this._editor.createDecorationsCollection(); constructor( private readonly _editor: IActiveCodeEditor, @@ -233,7 +233,7 @@ class DocumentUnicodeHighlighter extends Disposable { } public override dispose() { - this._decorationIds = new Set(this._model.deltaDecorations(Array.from(this._decorationIds), [])); + this._decorations.clear(); super.dispose(); } @@ -243,7 +243,7 @@ class DocumentUnicodeHighlighter extends Disposable { } if (!this._model.mightContainNonBasicASCII()) { - this._decorationIds = new Set(this._editor.deltaDecorations(Array.from(this._decorationIds), [])); + this._decorations.clear(); return; } @@ -271,31 +271,21 @@ class DocumentUnicodeHighlighter extends Disposable { }); } } - this._decorationIds = new Set(this._editor.deltaDecorations( - Array.from(this._decorationIds), - decorations - )); + this._decorations.set(decorations); }); } - public getDecorationInfo(decorationId: string): UnicodeHighlighterDecorationInfo | null { - if (!this._decorationIds.has(decorationId)) { + public getDecorationInfo(decoration: IModelDecoration): UnicodeHighlighterDecorationInfo | null { + if (!this._decorations.has(decoration)) { return null; } const model = this._editor.getModel(); - const range = model.getDecorationRange(decorationId)!; - const decoration = { - range: range, - options: Decorations.instance.getDecorationFromOptions(this._options), - id: decorationId, - ownerId: 0, - }; if ( !isModelDecorationVisible(model, decoration) ) { return null; } - const text = model.getValueInRange(range); + const text = model.getValueInRange(decoration.range); return { reason: computeReason(text, this._options)!, inComment: isModelDecorationInComment(model, decoration), @@ -308,7 +298,7 @@ class ViewportUnicodeHighlighter extends Disposable { private readonly _model: ITextModel = this._editor.getModel(); private readonly _updateSoon: RunOnceScheduler; - private _decorationIds = new Set(); + private readonly _decorations = this._editor.createDecorationsCollection(); constructor( private readonly _editor: IActiveCodeEditor, @@ -336,7 +326,7 @@ class ViewportUnicodeHighlighter extends Disposable { } public override dispose() { - this._decorationIds = new Set(this._model.deltaDecorations(Array.from(this._decorationIds), [])); + this._decorations.clear(); super.dispose(); } @@ -346,7 +336,7 @@ class ViewportUnicodeHighlighter extends Disposable { } if (!this._model.mightContainNonBasicASCII()) { - this._decorationIds = new Set(this._editor.deltaDecorations(Array.from(this._decorationIds), [])); + this._decorations.clear(); return; } @@ -379,22 +369,15 @@ class ViewportUnicodeHighlighter extends Disposable { } this._updateState(totalResult); - this._decorationIds = new Set(this._editor.deltaDecorations(Array.from(this._decorationIds), decorations)); + this._decorations.set(decorations); } - public getDecorationInfo(decorationId: string): UnicodeHighlighterDecorationInfo | null { - if (!this._decorationIds.has(decorationId)) { + public getDecorationInfo(decoration: IModelDecoration): UnicodeHighlighterDecorationInfo | null { + if (!this._decorations.has(decoration)) { return null; } const model = this._editor.getModel(); - const range = model.getDecorationRange(decorationId)!; - const text = model.getValueInRange(range); - const decoration = { - range: range, - options: Decorations.instance.getDecorationFromOptions(this._options), - id: decorationId, - ownerId: 0, - }; + const text = model.getValueInRange(decoration.range); if (!isModelDecorationVisible(model, decoration)) { return null; } @@ -449,7 +432,7 @@ export class UnicodeHighlighterHoverParticipant implements IEditorHoverParticipa let index = 300; for (const d of lineDecorations) { - const highlightInfo = unicodeHighlighter.getDecorationInfo(d.id); + const highlightInfo = unicodeHighlighter.getDecorationInfo(d); if (!highlightInfo) { continue; } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index d96e306b5253a..8390e8adca5bb 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2553,6 +2553,10 @@ declare namespace monaco.editor { * Get all ranges for decorations. */ getRanges(): Range[]; + /** + * Determine if a decoration is in this collection. + */ + has(decoration: IModelDecoration): boolean; /** * Replace all previous decorations with `newDecorations`. */ diff --git a/src/vs/workbench/browser/codeeditor.ts b/src/vs/workbench/browser/codeeditor.ts index bc251d1bda10a..0b23958dfed4e 100644 --- a/src/vs/workbench/browser/codeeditor.ts +++ b/src/vs/workbench/browser/codeeditor.ts @@ -47,8 +47,11 @@ export class RangeHighlightDecorations extends Disposable { } removeHighlightRange() { - if (this.editor?.getModel() && this.rangeHighlightDecorationId) { - this.editor.deltaDecorations([this.rangeHighlightDecorationId], []); + if (this.editor && this.rangeHighlightDecorationId) { + const decorationId = this.rangeHighlightDecorationId; + this.editor.changeDecorations((accessor) => { + accessor.removeDecoration(decorationId); + }); this._onHighlightRemoved.fire(); } diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 4eab6791a7f23..943b41f8eb55c 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -11,7 +11,7 @@ import severity from 'vs/base/common/severity'; import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions'; import { Range } from 'vs/editor/common/core/range'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IContentWidget, IActiveCodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; -import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, ITextModel, OverviewRulerLane, IModelDecorationOverviewRulerOptions } from 'vs/editor/common/model'; +import { IModelDecorationOptions, TrackedRangeStickiness, ITextModel, OverviewRulerLane, IModelDecorationOverviewRulerOptions } from 'vs/editor/common/model'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -158,7 +158,7 @@ async function createCandidateDecorations(model: ITextModel, breakpointDecoratio export class BreakpointEditorContribution implements IBreakpointEditorContribution { - private breakpointHintDecoration: string[] = []; + private breakpointHintDecoration: string | null = null; private breakpointWidget: BreakpointWidget | undefined; private breakpointWidgetVisible: IContextKey; private toDispose: IDisposable[] = []; @@ -443,20 +443,21 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi } private ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber: number): void { - const newDecoration: IModelDeltaDecoration[] = []; - if (showBreakpointHintAtLineNumber !== -1) { - newDecoration.push({ - options: breakpointHelperDecoration, - range: { + this.editor.changeDecorations((accessor) => { + if (this.breakpointHintDecoration) { + accessor.removeDecoration(this.breakpointHintDecoration); + this.breakpointHintDecoration = null; + } + if (showBreakpointHintAtLineNumber !== -1) { + this.breakpointHintDecoration = accessor.addDecoration({ startLineNumber: showBreakpointHintAtLineNumber, startColumn: 1, endLineNumber: showBreakpointHintAtLineNumber, endColumn: 1 - } - }); - } - - this.breakpointHintDecoration = this.editor.deltaDecorations(this.breakpointHintDecoration, newDecoration); + }, breakpointHelperDecoration + ); + } + }); } private async setDecorations(): Promise { From 6ec996a9b784acaed8d5f37bbd80a04b0d26b0c4 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 17 May 2022 16:21:45 +0200 Subject: [PATCH 099/942] Adopt `runWithFakedTimers` to speed up tests (#149712) (#149720) --- .../api/test/browser/extHostTreeViews.test.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/test/browser/extHostTreeViews.test.ts b/src/vs/workbench/api/test/browser/extHostTreeViews.test.ts index 058a03faa0231..48e90584747db 100644 --- a/src/vs/workbench/api/test/browser/extHostTreeViews.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTreeViews.test.ts @@ -19,6 +19,7 @@ import { TreeItemCollapsibleState, ITreeItem, IRevealOptions } from 'vs/workbenc import { NullLogService } from 'vs/platform/log/common/log'; import type { IDisposable } from 'vs/base/common/lifecycle'; import { nullExtensionDescription as extensionsDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; suite('ExtHostTreeView', function () { @@ -250,15 +251,17 @@ suite('ExtHostTreeView', function () { }); async function runWithEventMerging(action: (resolve: () => void) => void) { - await new Promise((resolve) => { - let subscription: IDisposable | undefined = undefined; - subscription = target.onRefresh.event(() => { - subscription!.dispose(); - resolve(); + await runWithFakedTimers({}, async () => { + await new Promise((resolve) => { + let subscription: IDisposable | undefined = undefined; + subscription = target.onRefresh.event(() => { + subscription!.dispose(); + resolve(); + }); + onDidChangeTreeNode.fire(getNode('b')); }); - onDidChangeTreeNode.fire(getNode('b')); + await new Promise(action); }); - await new Promise(action); } test('refresh parent and child node trigger refresh only on parent - scenario 1', async () => { From 3f092b812dbcababef496899f83a52bbdf014e88 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 17 May 2022 16:29:25 +0200 Subject: [PATCH 100/942] fix tests --- .../configuration/common/configurations.ts | 15 +++++-- .../test/common/policyConfiguration.test.ts | 44 +++++++++---------- .../configurationEditingService.test.ts | 4 +- .../test/browser/configurationService.test.ts | 3 +- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index 9cd8e84917b46..1fc471bb739dc 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -108,19 +108,26 @@ export class PolicyConfiguration extends Disposable { for (const key of properties) { const config = configurationProperties[key]; - const policy = config.policy; - if (policy) { + if (!config) { + // Config is removed. So add it to the list if in case it was registered as policy before + keys.push(key); + continue; + } + if (config.policy) { if (config.type !== 'string' && config.type !== 'number') { - this.logService.warn(`Policy ${policy.name} has unsupported type ${config.type}`); + this.logService.warn(`Policy ${config.policy.name} has unsupported type ${config.type}`); continue; } keys.push(key); - policyDefinitions[policy.name] = { type: config.type }; + policyDefinitions[config.policy.name] = { type: config.type }; } } if (!isEmptyObject(policyDefinitions)) { await this.policyService.registerPolicyDefinitions(policyDefinitions); + } + + if (keys.length) { this.update(keys, trigger); } } diff --git a/src/vs/platform/configuration/test/common/policyConfiguration.test.ts b/src/vs/platform/configuration/test/common/policyConfiguration.test.ts index 116322e570d8b..ba2aa5d527ec7 100644 --- a/src/vs/platform/configuration/test/common/policyConfiguration.test.ts +++ b/src/vs/platform/configuration/test/common/policyConfiguration.test.ts @@ -33,16 +33,16 @@ suite('PolicyConfiguration', () => { 'type': 'object', 'properties': { 'policy.settingA': { - 'type': 'boolean', - 'default': true, + 'type': 'string', + 'default': 'defaultValueA', policy: { name: 'PolicySettingA', minimumVersion: '1.0.0', } }, 'policy.settingB': { - 'type': 'boolean', - 'default': true, + 'type': 'string', + 'default': 'defaultValueB', policy: { name: 'PolicySettingB', minimumVersion: '1.0.0', @@ -71,12 +71,12 @@ suite('PolicyConfiguration', () => { teardown(() => disposables.clear()); test('initialize: with policies', async () => { - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false }))); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueA' }))); await testObject.initialize(); const acutal = testObject.configurationModel; - assert.strictEqual(acutal.getValue('policy.settingA'), false); + assert.strictEqual(acutal.getValue('policy.settingA'), 'policyValueA'); assert.strictEqual(acutal.getValue('policy.settingB'), undefined); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); assert.deepStrictEqual(acutal.keys, ['policy.settingA']); @@ -95,44 +95,44 @@ suite('PolicyConfiguration', () => { }); test('initialize: with policies but not registered', async () => { - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false, 'PolicySettingB': false, 'PolicySettingC': false }))); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueA', 'PolicySettingB': 'policyValueB', 'PolicySettingC': 'policyValueC' }))); await testObject.initialize(); const acutal = testObject.configurationModel; - assert.strictEqual(acutal.getValue('policy.settingA'), false); - assert.strictEqual(acutal.getValue('policy.settingB'), false); + assert.strictEqual(acutal.getValue('policy.settingA'), 'policyValueA'); + assert.strictEqual(acutal.getValue('policy.settingB'), 'policyValueB'); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); assert.deepStrictEqual(acutal.keys, ['policy.settingA', 'policy.settingB']); assert.deepStrictEqual(acutal.overrides, []); }); test('change: when policy is added', async () => { - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false }))); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueA' }))); await testObject.initialize(); const promise = Event.toPromise(testObject.onDidChangeConfiguration); - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false, 'PolicySettingB': false, 'PolicySettingC': false }))); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueA', 'PolicySettingB': 'policyValueB', 'PolicySettingC': 'policyValueC' }))); await promise; const acutal = testObject.configurationModel; - assert.strictEqual(acutal.getValue('policy.settingA'), false); - assert.strictEqual(acutal.getValue('policy.settingB'), false); + assert.strictEqual(acutal.getValue('policy.settingA'), 'policyValueA'); + assert.strictEqual(acutal.getValue('policy.settingB'), 'policyValueB'); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); assert.deepStrictEqual(acutal.keys, ['policy.settingA', 'policy.settingB']); assert.deepStrictEqual(acutal.overrides, []); }); test('change: when policy is updated', async () => { - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false }))); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueA' }))); await testObject.initialize(); const promise = Event.toPromise(testObject.onDidChangeConfiguration); - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': true }))); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueAChanged' }))); await promise; const acutal = testObject.configurationModel; - assert.strictEqual(acutal.getValue('policy.settingA'), true); + assert.strictEqual(acutal.getValue('policy.settingA'), 'policyValueAChanged'); assert.strictEqual(acutal.getValue('policy.settingB'), undefined); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); assert.deepStrictEqual(acutal.keys, ['policy.settingA']); @@ -140,7 +140,7 @@ suite('PolicyConfiguration', () => { }); test('change: when policy is removed', async () => { - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false }))); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueA' }))); await testObject.initialize(); const promise = Event.toPromise(testObject.onDidChangeConfiguration); @@ -156,13 +156,13 @@ suite('PolicyConfiguration', () => { }); test('change: when policy setting is registered', async () => { - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingC': false }))); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingC': 'policyValueC' }))); await testObject.initialize(); const promise = Event.toPromise(testObject.onDidChangeConfiguration); policyConfigurationNode.properties!['policy.settingC'] = { - 'type': 'boolean', - 'default': true, + 'type': 'string', + 'default': 'defaultValueC', policy: { name: 'PolicySettingC', minimumVersion: '1.0.0', @@ -172,7 +172,7 @@ suite('PolicyConfiguration', () => { await promise; const acutal = testObject.configurationModel; - assert.strictEqual(acutal.getValue('policy.settingC'), false); + assert.strictEqual(acutal.getValue('policy.settingC'), 'policyValueC'); assert.strictEqual(acutal.getValue('policy.settingA'), undefined); assert.strictEqual(acutal.getValue('policy.settingB'), undefined); assert.strictEqual(acutal.getValue('nonPolicy.setting'), undefined); @@ -181,7 +181,7 @@ suite('PolicyConfiguration', () => { }); test('change: when policy setting is deregistered', async () => { - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': false }))); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueA' }))); await testObject.initialize(); const promise = Event.toPromise(testObject.onDidChangeConfiguration); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts index 47d3e6b870677..727e4328c1f93 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts @@ -41,7 +41,7 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteA import { getSingleFolderWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { hash } from 'vs/base/common/hash'; -import { NullPolicyService } from 'vs/platform/policy/common/policy'; +import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' }); @@ -109,7 +109,7 @@ suite('ConfigurationEditingService', () => { disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService)))); instantiationService.stub(IFileService, fileService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService))); await workspaceService.initialize({ id: hash(workspaceFolder.toString()).toString(16), uri: workspaceFolder diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index b623dcdb5fc22..9cc61d7deca13 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -45,6 +45,7 @@ import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remot import { hash } from 'vs/base/common/hash'; import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; import { NullPolicyService } from 'vs/platform/policy/common/policy'; +import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; function convertToWorkspacePayload(folder: URI): ISingleFolderWorkspaceIdentifier { return { @@ -744,7 +745,7 @@ suite('WorkspaceConfigurationService - Folder', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService))); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); From 30f5b296abe2041e05edc9f31457324302123eab Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 17 May 2022 16:53:50 +0200 Subject: [PATCH 101/942] make `getWordAtText`-config configurable and use lower budget for speedy tests --- src/vs/editor/common/core/wordHelper.ts | 28 +++++++++++++++++-- .../test/browser/extHostDocumentData.test.ts | 25 +++++++++++------ 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/common/core/wordHelper.ts b/src/vs/editor/common/core/wordHelper.ts index 9f98fe1a52409..a4051dc26e8c1 100644 --- a/src/vs/editor/common/core/wordHelper.ts +++ b/src/vs/editor/common/core/wordHelper.ts @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Iterable } from 'vs/base/common/iterator'; +import { toDisposable } from 'vs/base/common/lifecycle'; +import { LinkedList } from 'vs/base/common/linkedList'; + export const USUAL_WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?'; /** @@ -71,13 +75,31 @@ export function ensureValidWordDefinition(wordDefinition?: RegExp | null): RegEx return result; } -const _defaultConfig = { + +export interface IGetWordAtTextConfig { + maxLen: number; + windowSize: number; + timeBudget: number; +} + + +const _defaultConfig = new LinkedList(); +_defaultConfig.unshift({ maxLen: 1000, windowSize: 15, timeBudget: 150 -}; +}); -export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number, config = _defaultConfig): IWordAtPosition | null { +export function setDefaultGetWordAtTextConfig(value: IGetWordAtTextConfig) { + const rm = _defaultConfig.unshift(value); + return toDisposable(rm); +} + +export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number, config?: IGetWordAtTextConfig): IWordAtPosition | null { + + if (!config) { + config = Iterable.first(_defaultConfig)!; + } if (text.length > config.maxLen) { // don't throw strings that long at the regexp diff --git a/src/vs/workbench/api/test/browser/extHostDocumentData.test.ts b/src/vs/workbench/api/test/browser/extHostDocumentData.test.ts index a3cada12b004a..a457ca659ca79 100644 --- a/src/vs/workbench/api/test/browser/extHostDocumentData.test.ts +++ b/src/vs/workbench/api/test/browser/extHostDocumentData.test.ts @@ -12,6 +12,7 @@ import { MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protoc import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel'; import { mock } from 'vs/base/test/common/mock'; import * as perfData from './extHostDocumentData.test.perf-data'; +import { setDefaultGetWordAtTextConfig } from 'vs/editor/common/core/wordHelper'; suite('ExtHostDocumentData', () => { @@ -316,14 +317,22 @@ suite('ExtHostDocumentData', () => { perfData._$_$_expensive ], '\n', 1, 'text', false); - let range = data.document.getWordRangeAtPosition(new Position(0, 1_177_170), regex)!; - assert.strictEqual(range, undefined); - - const pos = new Position(0, 1177170); - range = data.document.getWordRangeAtPosition(pos)!; - assert.ok(range); - assert.ok(range.contains(pos)); - assert.strictEqual(data.document.getText(range), 'TaskDefinition'); + // this test only ensures that we eventually give and timeout (when searching "funny" words and long lines) + // for the sake of speedy tests we lower the timeBudget here + const config = setDefaultGetWordAtTextConfig({ maxLen: 1000, windowSize: 15, timeBudget: 30 }); + try { + let range = data.document.getWordRangeAtPosition(new Position(0, 1_177_170), regex)!; + assert.strictEqual(range, undefined); + + const pos = new Position(0, 1177170); + range = data.document.getWordRangeAtPosition(pos)!; + assert.ok(range); + assert.ok(range.contains(pos)); + assert.strictEqual(data.document.getText(range), 'TaskDefinition'); + + } finally { + config.dispose(); + } }); test('Rename popup sometimes populates with text on the left side omitted #96013', function () { From 94a16a85aae7c2c9ad94cbd9df03d058e565b17b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 17 May 2022 17:01:21 +0200 Subject: [PATCH 102/942] - faster unit tests use faketimer - little refactoring --- .../configuration/common/configurations.ts | 10 +++----- .../test/common/policyConfiguration.test.ts | 25 ++++++++++++------- .../configurationEditingService.test.ts | 9 ++++--- .../test/browser/configurationService.test.ts | 9 ++++--- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index 1fc471bb739dc..6099234ef8eef 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -95,13 +95,13 @@ export class PolicyConfiguration extends Disposable { } async initialize(): Promise { - await this.registerPolicyDefinitionsAndUpdate(this.defaultConfiguration.configurationModel.keys, false); + this.update(await this.registerPolicyDefinitions(this.defaultConfiguration.configurationModel.keys), false); this._register(this.policyService.onDidChange(policyNames => this.onDidChangePolicies(policyNames))); - this._register(this.defaultConfiguration.onDidChangeConfiguration(({ properties }) => this.registerPolicyDefinitionsAndUpdate(properties, true))); + this._register(this.defaultConfiguration.onDidChangeConfiguration(async ({ properties }) => this.update(await this.registerPolicyDefinitions(properties), true))); return this._configurationModel; } - private async registerPolicyDefinitionsAndUpdate(properties: string[], trigger: boolean): Promise { + private async registerPolicyDefinitions(properties: string[]): Promise { const policyDefinitions: IStringDictionary = {}; const keys: string[] = []; const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); @@ -127,9 +127,7 @@ export class PolicyConfiguration extends Disposable { await this.policyService.registerPolicyDefinitions(policyDefinitions); } - if (keys.length) { - this.update(keys, trigger); - } + return keys; } private onDidChangePolicies(policyNames: readonly PolicyName[]): void { diff --git a/src/vs/platform/configuration/test/common/policyConfiguration.test.ts b/src/vs/platform/configuration/test/common/policyConfiguration.test.ts index ba2aa5d527ec7..d5b31e5516d29 100644 --- a/src/vs/platform/configuration/test/common/policyConfiguration.test.ts +++ b/src/vs/platform/configuration/test/common/policyConfiguration.test.ts @@ -18,6 +18,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { deepClone } from 'vs/base/common/objects'; import { IPolicyService } from 'vs/platform/policy/common/policy'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; suite('PolicyConfiguration', () => { @@ -111,9 +112,11 @@ suite('PolicyConfiguration', () => { await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueA' }))); await testObject.initialize(); - const promise = Event.toPromise(testObject.onDidChangeConfiguration); - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueA', 'PolicySettingB': 'policyValueB', 'PolicySettingC': 'policyValueC' }))); - await promise; + await runWithFakedTimers({ useFakeTimers: true }, async () => { + const promise = Event.toPromise(testObject.onDidChangeConfiguration); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueA', 'PolicySettingB': 'policyValueB', 'PolicySettingC': 'policyValueC' }))); + await promise; + }); const acutal = testObject.configurationModel; assert.strictEqual(acutal.getValue('policy.settingA'), 'policyValueA'); @@ -127,9 +130,11 @@ suite('PolicyConfiguration', () => { await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueA' }))); await testObject.initialize(); - const promise = Event.toPromise(testObject.onDidChangeConfiguration); - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueAChanged' }))); - await promise; + await runWithFakedTimers({ useFakeTimers: true }, async () => { + const promise = Event.toPromise(testObject.onDidChangeConfiguration); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueAChanged' }))); + await promise; + }); const acutal = testObject.configurationModel; assert.strictEqual(acutal.getValue('policy.settingA'), 'policyValueAChanged'); @@ -143,9 +148,11 @@ suite('PolicyConfiguration', () => { await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({ 'PolicySettingA': 'policyValueA' }))); await testObject.initialize(); - const promise = Event.toPromise(testObject.onDidChangeConfiguration); - await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({}))); - await promise; + await runWithFakedTimers({ useFakeTimers: true }, async () => { + const promise = Event.toPromise(testObject.onDidChangeConfiguration); + await fileService.writeFile(policyFile, VSBuffer.fromString(JSON.stringify({}))); + await promise; + }); const acutal = testObject.configurationModel; assert.strictEqual(acutal.getValue('policy.settingA'), undefined); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts index 727e4328c1f93..995442cd8b8eb 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts @@ -42,6 +42,7 @@ import { getSingleFolderWorkspaceIdentifier } from 'vs/workbench/services/worksp import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { hash } from 'vs/base/common/hash'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' }); @@ -197,9 +198,11 @@ suite('ConfigurationEditingService', () => { }); test('errors cases - ERROR_POLICY_CONFIGURATION', async () => { - const promise = Event.toPromise(instantiationService.get(IConfigurationService).onDidChangeConfiguration); - await fileService.writeFile(environmentService.policyFile!, VSBuffer.fromString('{ "configurationEditing.service.policySetting": "policyValue" }')); - await promise; + await runWithFakedTimers({ useFakeTimers: true }, async () => { + const promise = Event.toPromise(instantiationService.get(IConfigurationService).onDidChangeConfiguration); + await fileService.writeFile(environmentService.policyFile!, VSBuffer.fromString('{ "configurationEditing.service.policySetting": "policyValue" }')); + await promise; + }); try { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.policySetting', value: 'value' }, { donotNotifyError: true }); } catch (error) { diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 9cc61d7deca13..d36be57c24764 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -46,6 +46,7 @@ import { hash } from 'vs/base/common/hash'; import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; import { NullPolicyService } from 'vs/platform/policy/common/policy'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; function convertToWorkspacePayload(folder: URI): ISingleFolderWorkspaceIdentifier { return { @@ -968,9 +969,11 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('policy value override all', async () => { - const promise = Event.toPromise(testObject.onDidChangeConfiguration); - await fileService.writeFile(environmentService.policyFile!, VSBuffer.fromString('{ "configurationService.folder.policySetting": "policyValue" }')); - const result = await promise; + const result = await runWithFakedTimers({ useFakeTimers: true }, async () => { + const promise = Event.toPromise(testObject.onDidChangeConfiguration); + await fileService.writeFile(environmentService.policyFile!, VSBuffer.fromString('{ "configurationService.folder.policySetting": "policyValue" }')); + return promise; + }); assert.deepStrictEqual(result.affectedKeys, ['configurationService.folder.policySetting']); assert.strictEqual(testObject.getValue('configurationService.folder.policySetting'), 'policyValue'); assert.strictEqual(testObject.inspect('configurationService.folder.policySetting').policyValue, 'policyValue'); From 9acfe28a460b76a353a3ee577ae63c0160fef1ac Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 17 May 2022 17:23:23 +0200 Subject: [PATCH 103/942] Adopt `runWithFakedTimers` to speed up tests (for #149712) (#149724) --- .../common/userDataAutoSyncService.test.ts | 657 +++++++++--------- 1 file changed, 343 insertions(+), 314 deletions(-) diff --git a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts index 6657810f7d6f2..134fb2e963b9b 100644 --- a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts @@ -8,6 +8,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { UserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; @@ -31,401 +32,429 @@ suite('UserDataAutoSyncService', () => { teardown(() => disposableStore.clear()); test('test auto sync with sync resource change triggers sync', async () => { - // Setup the client - const target = new UserDataSyncTestServer(); - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); + await runWithFakedTimers({}, async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); - // Sync once and reset requests - await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); - target.reset(); + // Sync once and reset requests + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); + target.reset(); - const testObject: UserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); + const testObject: UserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); - // Trigger auto sync with settings change - await testObject.triggerSync([SyncResource.Settings], false, false); + // Trigger auto sync with settings change + await testObject.triggerSync([SyncResource.Settings], false, false); - // Filter out machine requests - const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); + // Filter out machine requests + const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); - // Make sure only one manifest request is made - assert.deepStrictEqual(actual, [{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }]); + // Make sure only one manifest request is made + assert.deepStrictEqual(actual, [{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }]); + }); }); test('test auto sync with sync resource change triggers sync for every change', async () => { - // Setup the client - const target = new UserDataSyncTestServer(); - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); + await runWithFakedTimers({}, async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); - // Sync once and reset requests - await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); - target.reset(); + // Sync once and reset requests + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); + target.reset(); - const testObject: UserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); + const testObject: UserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); - // Trigger auto sync with settings change multiple times - for (let counter = 0; counter < 2; counter++) { - await testObject.triggerSync([SyncResource.Settings], false, false); - } + // Trigger auto sync with settings change multiple times + for (let counter = 0; counter < 2; counter++) { + await testObject.triggerSync([SyncResource.Settings], false, false); + } - // Filter out machine requests - const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); + // Filter out machine requests + const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); - assert.deepStrictEqual(actual, [ - { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } } - ]); + assert.deepStrictEqual(actual, [ + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } } + ]); + }); }); test('test auto sync with non sync resource change triggers sync', async () => { - // Setup the client - const target = new UserDataSyncTestServer(); - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); + await runWithFakedTimers({}, async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); - // Sync once and reset requests - await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); - target.reset(); + // Sync once and reset requests + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); + target.reset(); - const testObject: UserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); + const testObject: UserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); - // Trigger auto sync with window focus once - await testObject.triggerSync(['windowFocus'], true, false); + // Trigger auto sync with window focus once + await testObject.triggerSync(['windowFocus'], true, false); - // Filter out machine requests - const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); + // Filter out machine requests + const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); - // Make sure only one manifest request is made - assert.deepStrictEqual(actual, [{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }]); + // Make sure only one manifest request is made + assert.deepStrictEqual(actual, [{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }]); + }); }); test('test auto sync with non sync resource change does not trigger continuous syncs', async () => { - // Setup the client - const target = new UserDataSyncTestServer(); - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); + await runWithFakedTimers({}, async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); - // Sync once and reset requests - await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); - target.reset(); + // Sync once and reset requests + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); + target.reset(); - const testObject: UserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); + const testObject: UserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); - // Trigger auto sync with window focus multiple times - for (let counter = 0; counter < 2; counter++) { - await testObject.triggerSync(['windowFocus'], true, false); - } + // Trigger auto sync with window focus multiple times + for (let counter = 0; counter < 2; counter++) { + await testObject.triggerSync(['windowFocus'], true, false); + } - // Filter out machine requests - const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); + // Filter out machine requests + const actual = target.requests.filter(request => !request.url.startsWith(`${target.url}/v1/resource/machines`)); - // Make sure only one manifest request is made - assert.deepStrictEqual(actual, [{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }]); + // Make sure only one manifest request is made + assert.deepStrictEqual(actual, [{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }]); + }); }); test('test first auto sync requests', async () => { - // Setup the client - const target = new UserDataSyncTestServer(); - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); - const testObject: TestUserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); - - await testObject.sync(); - - assert.deepStrictEqual(target.requests, [ - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, - // Machines - { type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: {} }, - // Settings - { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, - { type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } }, - // Keybindings - { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, - { type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '0' } }, - // Snippets - { type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: {} }, - { type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '0' } }, - // Tasks - { type: 'GET', url: `${target.url}/v1/resource/tasks/latest`, headers: {} }, - { type: 'POST', url: `${target.url}/v1/resource/tasks`, headers: { 'If-Match': '0' } }, - // Global state - { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, - { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } }, - // Extensions - { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, - // Machines - { type: 'POST', url: `${target.url}/v1/resource/machines`, headers: { 'If-Match': '0' } } - ]); + await runWithFakedTimers({}, async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject: TestUserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); + await testObject.sync(); + + assert.deepStrictEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Machines + { type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: {} }, + // Settings + { type: 'GET', url: `${target.url}/v1/resource/settings/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '0' } }, + // Keybindings + { type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '0' } }, + // Snippets + { type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '0' } }, + // Tasks + { type: 'GET', url: `${target.url}/v1/resource/tasks/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/tasks`, headers: { 'If-Match': '0' } }, + // Global state + { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } }, + // Extensions + { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, + // Machines + { type: 'POST', url: `${target.url}/v1/resource/machines`, headers: { 'If-Match': '0' } } + ]); + }); }); test('test further auto sync requests without changes', async () => { - // Setup the client - const target = new UserDataSyncTestServer(); - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); - const testObject: TestUserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); - - // Sync once and reset requests - await testObject.sync(); - target.reset(); - - await testObject.sync(); + await runWithFakedTimers({}, async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject: TestUserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); + + // Sync once and reset requests + await testObject.sync(); + target.reset(); - assert.deepStrictEqual(target.requests, [ - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } } - ]); + await testObject.sync(); + assert.deepStrictEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } } + ]); + }); }); test('test further auto sync requests with changes', async () => { - // Setup the client - const target = new UserDataSyncTestServer(); - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); - const testObject: TestUserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); - - // Sync once and reset requests - await testObject.sync(); - target.reset(); - - // Do changes in the client - const fileService = client.instantiationService.get(IFileService); - const environmentService = client.instantiationService.get(IEnvironmentService); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); - await fileService.writeFile(joinPath(environmentService.snippetsHome, 'html.json'), VSBuffer.fromString(`{}`)); - await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); - await testObject.sync(); - - assert.deepStrictEqual(target.requests, [ - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } }, - // Settings - { type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } }, - // Keybindings - { type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '1' } }, - // Snippets - { type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '1' } }, - // Global state - { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '1' } }, - ]); + await runWithFakedTimers({}, async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject: TestUserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); + + // Sync once and reset requests + await testObject.sync(); + target.reset(); + + // Do changes in the client + const fileService = client.instantiationService.get(IFileService); + const environmentService = client.instantiationService.get(IEnvironmentService); + await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); + await fileService.writeFile(joinPath(environmentService.snippetsHome, 'html.json'), VSBuffer.fromString(`{}`)); + await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); + await testObject.sync(); + assert.deepStrictEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } }, + // Settings + { type: 'POST', url: `${target.url}/v1/resource/settings`, headers: { 'If-Match': '1' } }, + // Keybindings + { type: 'POST', url: `${target.url}/v1/resource/keybindings`, headers: { 'If-Match': '1' } }, + // Snippets + { type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '1' } }, + // Global state + { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '1' } }, + ]); + }); }); test('test auto sync send execution id header', async () => { - // Setup the client - const target = new UserDataSyncTestServer(); - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); - const testObject: TestUserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); - - // Sync once and reset requests - await testObject.sync(); - target.reset(); - - await testObject.sync(); - - for (const request of target.requestsWithAllHeaders) { - const hasExecutionIdHeader = request.headers && request.headers['X-Execution-Id'] && request.headers['X-Execution-Id'].length > 0; - if (request.url.startsWith(`${target.url}/v1/resource/machines`)) { - assert.ok(!hasExecutionIdHeader, `Should not have execution header: ${request.url}`); - } else { - assert.ok(hasExecutionIdHeader, `Should have execution header: ${request.url}`); - } - } + await runWithFakedTimers({}, async () => { + // Setup the client + const target = new UserDataSyncTestServer(); + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + const testObject: TestUserDataAutoSyncService = disposableStore.add(client.instantiationService.createInstance(TestUserDataAutoSyncService)); + + // Sync once and reset requests + await testObject.sync(); + target.reset(); + await testObject.sync(); + + for (const request of target.requestsWithAllHeaders) { + const hasExecutionIdHeader = request.headers && request.headers['X-Execution-Id'] && request.headers['X-Execution-Id'].length > 0; + if (request.url.startsWith(`${target.url}/v1/resource/machines`)) { + assert.ok(!hasExecutionIdHeader, `Should not have execution header: ${request.url}`); + } else { + assert.ok(hasExecutionIdHeader, `Should have execution header: ${request.url}`); + } + } + }); }); test('test delete on one client throws turned off error on other client while syncing', async () => { - const target = new UserDataSyncTestServer(); - - // Set up and sync from the client - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); - await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); - - // Set up and sync from the test client - const testClient = disposableStore.add(new UserDataSyncClient(target)); - await testClient.setUp(); - const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); - await testObject.sync(); - - // Reset from the first client - await client.instantiationService.get(IUserDataSyncService).reset(); - - // Sync from the test client - target.reset(); - - const errorPromise = Event.toPromise(testObject.onError); - await testObject.sync(); - - const e = await errorPromise; - assert.ok(e instanceof UserDataAutoSyncError); - assert.deepStrictEqual((e).code, UserDataSyncErrorCode.TurnedOff); - assert.deepStrictEqual(target.requests, [ - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } }, - // Machine - { type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: { 'If-None-Match': '1' } }, - ]); + await runWithFakedTimers({}, async () => { + const target = new UserDataSyncTestServer(); + + // Set up and sync from the client + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); + await testObject.sync(); + + // Reset from the first client + await client.instantiationService.get(IUserDataSyncService).reset(); + + // Sync from the test client + target.reset(); + + const errorPromise = Event.toPromise(testObject.onError); + await testObject.sync(); + + const e = await errorPromise; + assert.ok(e instanceof UserDataAutoSyncError); + assert.deepStrictEqual((e).code, UserDataSyncErrorCode.TurnedOff); + assert.deepStrictEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } }, + // Machine + { type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: { 'If-None-Match': '1' } }, + ]); + }); }); test('test disabling the machine turns off sync', async () => { - const target = new UserDataSyncTestServer(); - - // Set up and sync from the test client - const testClient = disposableStore.add(new UserDataSyncClient(target)); - await testClient.setUp(); - const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); - await testObject.sync(); - - // Disable current machine - const userDataSyncMachinesService = testClient.instantiationService.get(IUserDataSyncMachinesService); - const machines = await userDataSyncMachinesService.getMachines(); - const currentMachine = machines.find(m => m.isCurrent)!; - await userDataSyncMachinesService.setEnablements([[currentMachine.id, false]]); - - target.reset(); - - const errorPromise = Event.toPromise(testObject.onError); - await testObject.sync(); - - const e = await errorPromise; - assert.ok(e instanceof UserDataAutoSyncError); - assert.deepStrictEqual((e).code, UserDataSyncErrorCode.TurnedOff); - assert.deepStrictEqual(target.requests, [ - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } }, - // Machine - { type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: { 'If-None-Match': '2' } }, - { type: 'POST', url: `${target.url}/v1/resource/machines`, headers: { 'If-Match': '2' } }, - ]); + await runWithFakedTimers({}, async () => { + const target = new UserDataSyncTestServer(); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); + await testObject.sync(); + + // Disable current machine + const userDataSyncMachinesService = testClient.instantiationService.get(IUserDataSyncMachinesService); + const machines = await userDataSyncMachinesService.getMachines(); + const currentMachine = machines.find(m => m.isCurrent)!; + await userDataSyncMachinesService.setEnablements([[currentMachine.id, false]]); + + target.reset(); + + const errorPromise = Event.toPromise(testObject.onError); + await testObject.sync(); + + const e = await errorPromise; + assert.ok(e instanceof UserDataAutoSyncError); + assert.deepStrictEqual((e).code, UserDataSyncErrorCode.TurnedOff); + assert.deepStrictEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } }, + // Machine + { type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: { 'If-None-Match': '2' } }, + { type: 'POST', url: `${target.url}/v1/resource/machines`, headers: { 'If-Match': '2' } }, + ]); + }); }); test('test removing the machine adds machine back', async () => { - const target = new UserDataSyncTestServer(); - - // Set up and sync from the test client - const testClient = disposableStore.add(new UserDataSyncClient(target)); - await testClient.setUp(); - const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); - await testObject.sync(); - - // Remove current machine - await testClient.instantiationService.get(IUserDataSyncMachinesService).removeCurrentMachine(); - - target.reset(); - - await testObject.sync(); - assert.deepStrictEqual(target.requests, [ - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } }, - // Machine - { type: 'POST', url: `${target.url}/v1/resource/machines`, headers: { 'If-Match': '2' } }, - ]); + await runWithFakedTimers({}, async () => { + const target = new UserDataSyncTestServer(); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); + await testObject.sync(); + + // Remove current machine + await testClient.instantiationService.get(IUserDataSyncMachinesService).removeCurrentMachine(); + + target.reset(); + + await testObject.sync(); + assert.deepStrictEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } }, + // Machine + { type: 'POST', url: `${target.url}/v1/resource/machines`, headers: { 'If-Match': '2' } }, + ]); + }); }); test('test creating new session from one client throws session expired error on another client while syncing', async () => { - const target = new UserDataSyncTestServer(); - - // Set up and sync from the client - const client = disposableStore.add(new UserDataSyncClient(target)); - await client.setUp(); - await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); - - // Set up and sync from the test client - const testClient = disposableStore.add(new UserDataSyncClient(target)); - await testClient.setUp(); - const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); - await testObject.sync(); - - // Reset from the first client - await client.instantiationService.get(IUserDataSyncService).reset(); - - // Sync again from the first client to create new session - await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); - - // Sync from the test client - target.reset(); - - const errorPromise = Event.toPromise(testObject.onError); - await testObject.sync(); - - const e = await errorPromise; - assert.ok(e instanceof UserDataAutoSyncError); - assert.deepStrictEqual((e).code, UserDataSyncErrorCode.SessionExpired); - assert.deepStrictEqual(target.requests, [ - // Manifest - { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } }, - // Machine - { type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: { 'If-None-Match': '1' } }, - ]); + await runWithFakedTimers({}, async () => { + const target = new UserDataSyncTestServer(); + + // Set up and sync from the client + const client = disposableStore.add(new UserDataSyncClient(target)); + await client.setUp(); + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); + + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); + await testObject.sync(); + + // Reset from the first client + await client.instantiationService.get(IUserDataSyncService).reset(); + + // Sync again from the first client to create new session + await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); + + // Sync from the test client + target.reset(); + + const errorPromise = Event.toPromise(testObject.onError); + await testObject.sync(); + + const e = await errorPromise; + assert.ok(e instanceof UserDataAutoSyncError); + assert.deepStrictEqual((e).code, UserDataSyncErrorCode.SessionExpired); + assert.deepStrictEqual(target.requests, [ + // Manifest + { type: 'GET', url: `${target.url}/v1/manifest`, headers: { 'If-None-Match': '1' } }, + // Machine + { type: 'GET', url: `${target.url}/v1/resource/machines/latest`, headers: { 'If-None-Match': '1' } }, + ]); + }); }); test('test rate limit on server', async () => { - const target = new UserDataSyncTestServer(5); + await runWithFakedTimers({}, async () => { + const target = new UserDataSyncTestServer(5); - // Set up and sync from the test client - const testClient = disposableStore.add(new UserDataSyncClient(target)); - await testClient.setUp(); - const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); - const errorPromise = Event.toPromise(testObject.onError); - while (target.requests.length < 5) { - await testObject.sync(); - } + const errorPromise = Event.toPromise(testObject.onError); + while (target.requests.length < 5) { + await testObject.sync(); + } - const e = await errorPromise; - assert.ok(e instanceof UserDataSyncStoreError); - assert.deepStrictEqual((e).code, UserDataSyncErrorCode.TooManyRequests); + const e = await errorPromise; + assert.ok(e instanceof UserDataSyncStoreError); + assert.deepStrictEqual((e).code, UserDataSyncErrorCode.TooManyRequests); + }); }); test('test auto sync is suspended when server donot accepts requests', async () => { - const target = new UserDataSyncTestServer(5, 1); + await runWithFakedTimers({}, async () => { + const target = new UserDataSyncTestServer(5, 1); - // Set up and sync from the test client - const testClient = disposableStore.add(new UserDataSyncClient(target)); - await testClient.setUp(); - const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); - while (target.requests.length < 5) { - await testObject.sync(); - } + while (target.requests.length < 5) { + await testObject.sync(); + } - target.reset(); - await testObject.sync(); + target.reset(); + await testObject.sync(); - assert.deepStrictEqual(target.requests, []); + assert.deepStrictEqual(target.requests, []); + }); }); test('test cache control header with no cache is sent when triggered with disable cache option', async () => { - const target = new UserDataSyncTestServer(5, 1); + await runWithFakedTimers({}, async () => { + const target = new UserDataSyncTestServer(5, 1); - // Set up and sync from the test client - const testClient = disposableStore.add(new UserDataSyncClient(target)); - await testClient.setUp(); - const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); - await testObject.triggerSync(['some reason'], true, true); - assert.strictEqual(target.requestsWithAllHeaders[0].headers!['Cache-Control'], 'no-cache'); + await testObject.triggerSync(['some reason'], true, true); + assert.strictEqual(target.requestsWithAllHeaders[0].headers!['Cache-Control'], 'no-cache'); + }); }); test('test cache control header is not sent when triggered without disable cache option', async () => { - const target = new UserDataSyncTestServer(5, 1); + await runWithFakedTimers({}, async () => { + const target = new UserDataSyncTestServer(5, 1); - // Set up and sync from the test client - const testClient = disposableStore.add(new UserDataSyncClient(target)); - await testClient.setUp(); - const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); + // Set up and sync from the test client + const testClient = disposableStore.add(new UserDataSyncClient(target)); + await testClient.setUp(); + const testObject: TestUserDataAutoSyncService = disposableStore.add(testClient.instantiationService.createInstance(TestUserDataAutoSyncService)); - await testObject.triggerSync(['some reason'], true, false); - assert.strictEqual(target.requestsWithAllHeaders[0].headers!['Cache-Control'], undefined); + await testObject.triggerSync(['some reason'], true, false); + assert.strictEqual(target.requestsWithAllHeaders[0].headers!['Cache-Control'], undefined); + }); }); }); From f427aed437f70906cd0eb7d56ca362307b817923 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 17 May 2022 08:26:16 -0700 Subject: [PATCH 104/942] add additional telemetry for connection latency and duration (#149398) * server: add telemetry for initial connection time * feat: measure approximate rtt to the remote extension host * fixup! remove unused property * fixup! address pr comments --- .../common/remoteTelemetryChannel.ts | 4 ++ .../remote/common/remote.contribution.ts | 44 +++++++++++++++++++ .../common/abstractRemoteAgentService.ts | 34 +++++++++++++- .../common/remoteAgentEnvironmentChannel.ts | 4 ++ .../remote/common/remoteAgentService.ts | 7 +++ .../test/browser/workbenchTestServices.ts | 1 + 6 files changed, 92 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/telemetry/common/remoteTelemetryChannel.ts b/src/vs/platform/telemetry/common/remoteTelemetryChannel.ts index 66bd712a808b3..97fb3ec49892a 100644 --- a/src/vs/platform/telemetry/common/remoteTelemetryChannel.ts +++ b/src/vs/platform/telemetry/common/remoteTelemetryChannel.ts @@ -45,6 +45,10 @@ export class ServerTelemetryChannel extends Disposable implements IServerChannel return Promise.resolve(); } + + case 'ping': { + return; + } } // Command we cannot handle so we throw an error throw new Error(`IPC Command ${command} not found`); diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index eb62a41277f24..a2d8b9671aeee 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -31,6 +31,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadServiceChannel } from 'vs/platform/download/common/downloadIpc'; +import { timeout } from 'vs/base/common/async'; export class LabelContribution implements IWorkbenchContribution { constructor( @@ -165,6 +166,9 @@ class RemoteInvalidWorkspaceDetector extends Disposable implements IWorkbenchCon } } +const EXT_HOST_LATENCY_SAMPLES = 5; +const EXT_HOST_LATENCY_DELAY = 2_000; + class InitialRemoteConnectionHealthContribution implements IWorkbenchContribution { constructor( @@ -185,17 +189,22 @@ class InitialRemoteConnectionHealthContribution implements IWorkbenchContributio owner: 'alexdima'; comment: 'The initial connection succeeded'; web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + connectionTimeMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time, in ms, until connected'; isMeasurement: true }; remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; }; type RemoteConnectionSuccessEvent = { web: boolean; + connectionTimeMs: number | undefined; remoteName: string | undefined; }; this._telemetryService.publicLog2('remoteConnectionSuccess', { web: isWeb, + connectionTimeMs: await this._remoteAgentService.getConnection()?.getInitialConnectionTimeMs(), remoteName: getRemoteName(this._environmentService.remoteAuthority) }); + await this._measureExtHostLatency(); + } catch (err) { type RemoteConnectionFailureClassification = { @@ -203,21 +212,56 @@ class InitialRemoteConnectionHealthContribution implements IWorkbenchContributio comment: 'The initial connection failed'; web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + connectionTimeMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Time, in ms, until connection failure'; isMeasurement: true }; message: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; }; type RemoteConnectionFailureEvent = { web: boolean; remoteName: string | undefined; + connectionTimeMs: number | undefined; message: string; }; this._telemetryService.publicLog2('remoteConnectionFailure', { web: isWeb, + connectionTimeMs: await this._remoteAgentService.getConnection()?.getInitialConnectionTimeMs(), remoteName: getRemoteName(this._environmentService.remoteAuthority), message: err ? err.message : '' }); } } + + private async _measureExtHostLatency() { + // Get the minimum latency, since latency spikes could be caused by a busy extension host. + let bestLatency = Infinity; + for (let i = 0; i < EXT_HOST_LATENCY_SAMPLES; i++) { + const rtt = await this._remoteAgentService.getRoundTripTime(); + if (rtt === undefined) { + return; + } + bestLatency = Math.min(bestLatency, rtt / 2); + await timeout(EXT_HOST_LATENCY_DELAY); + } + + type RemoteConnectionFailureClassification = { + owner: 'connor4312'; + comment: 'The latency to the remote extension host'; + web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether this is running on web' }; + remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Anonymized remote name' }; + latencyMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Latency to the remote, in milliseconds'; isMeasurement: true }; + }; + type RemoteConnectionFailureEvent = { + web: boolean; + remoteName: string | undefined; + latencyMs: number; + }; + + this._telemetryService.publicLog2('remoteConnectionFailure', { + web: isWeb, + remoteName: getRemoteName(this._environmentService.remoteAuthority), + latencyMs: bestLatency + }); + } } const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); diff --git a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts index 3268cc373820a..0eba0de6b0072 100644 --- a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts @@ -7,7 +7,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IChannel, IServerChannel, getDelayedChannel, IPCLogger } from 'vs/base/parts/ipc/common/ipc'; import { Client } from 'vs/base/parts/ipc/common/ipc.net'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { connectRemoteAgentManagement, IConnectionOptions, ISocketFactory, PersistentConnectionEvent } from 'vs/platform/remote/common/remoteAgentConnection'; +import { connectRemoteAgentManagement, IConnectionOptions, ISocketFactory, ManagementPersistentConnection, PersistentConnectionEvent } from 'vs/platform/remote/common/remoteAgentConnection'; import { IExtensionHostExitInfo, IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { RemoteAgentConnectionContext, IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; @@ -125,6 +125,17 @@ export abstract class AbstractRemoteAgentService extends Disposable implements I ); } + getRoundTripTime(): Promise { + return this._withTelemetryChannel( + async channel => { + const start = Date.now(); + await RemoteExtensionEnvironmentChannelClient.ping(channel); + return Date.now() - start; + }, + undefined + ); + } + private _withChannel(callback: (channel: IChannel, connection: IRemoteAgentConnection) => Promise, fallback: R): Promise { const connection = this.getConnection(); if (!connection) { @@ -153,6 +164,8 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon readonly remoteAuthority: string; private _connection: Promise> | null; + private _initialConnectionMs: number | undefined; + constructor( remoteAuthority: string, private readonly _commit: string | undefined, @@ -181,6 +194,16 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon this._getOrCreateConnection().then(client => client.registerChannel(channelName, channel)); } + async getInitialConnectionTimeMs() { + try { + await this._getOrCreateConnection(); + } catch { + // ignored -- time is measured even if connection fails + } + + return this._initialConnectionMs!; + } + private _getOrCreateConnection(): Promise> { if (!this._connection) { this._connection = this._createConnection(); @@ -209,7 +232,14 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon logService: this._logService, ipcLogger: false ? new IPCLogger(`Local \u2192 Remote`, `Remote \u2192 Local`) : null }; - const connection = this._register(await connectRemoteAgentManagement(options, this.remoteAuthority, `renderer`)); + let connection: ManagementPersistentConnection; + let start = Date.now(); + try { + connection = this._register(await connectRemoteAgentManagement(options, this.remoteAuthority, `renderer`)); + } finally { + this._initialConnectionMs = Date.now() - start; + } + connection.protocol.onDidDispose(() => { connection.dispose(); }); diff --git a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts index 5d33f408514d0..35bfae00a4d00 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts @@ -138,4 +138,8 @@ export class RemoteExtensionEnvironmentChannelClient { static flushTelemetry(channel: IChannel): Promise { return channel.call('flushTelemetry'); } + + static async ping(channel: IChannel): Promise { + await channel.call('ping'); + } } diff --git a/src/vs/workbench/services/remote/common/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts index b7508ffc4a742..6a899e927be65 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts @@ -36,6 +36,12 @@ export interface IRemoteAgentService { */ getExtensionHostExitInfo(reconnectionToken: string): Promise; + /** + * Gets the round trip time from the remote extension host. Note that this + * may be delayed if the extension host is busy. + */ + getRoundTripTime(): Promise; + whenExtensionsReady(): Promise; /** * Scan remote extensions. @@ -65,4 +71,5 @@ export interface IRemoteAgentConnection { getChannel(channelName: string): T; withChannel(channelName: string, callback: (channel: T) => Promise): Promise; registerChannel>(channelName: string, channel: T): void; + getInitialConnectionTimeMs(): Promise; } diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 9a1025debd886..5bd962101c995 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -1920,4 +1920,5 @@ export class TestRemoteAgentService implements IRemoteAgentService { async updateTelemetryLevel(telemetryLevel: TelemetryLevel): Promise { } async logTelemetry(eventName: string, data?: ITelemetryData): Promise { } async flushTelemetry(): Promise { } + async getRoundTripTime(): Promise { return undefined; } } From 99a76d1569e13b6a1344d888321f5aeecf127ab6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 17 May 2022 17:34:46 +0200 Subject: [PATCH 105/942] use fake timer (#149725) --- .../test/browser/indexedDBFileService.test.ts | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/vs/platform/files/test/browser/indexedDBFileService.test.ts b/src/vs/platform/files/test/browser/indexedDBFileService.test.ts index 3387e3b989bf2..370a9458263c2 100644 --- a/src/vs/platform/files/test/browser/indexedDBFileService.test.ts +++ b/src/vs/platform/files/test/browser/indexedDBFileService.test.ts @@ -11,6 +11,7 @@ import { Schemas } from 'vs/base/common/network'; import { basename, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { flakySuite } from 'vs/base/test/common/testUtils'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { IndexedDBFileSystemProvider } from 'vs/platform/files/browser/indexedDBFileSystemProvider'; import { FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FileSystemProviderError, FileSystemProviderErrorCode, FileType, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; @@ -272,22 +273,24 @@ flakySuite('IndexedDBFileSystemProvider', function () { }); test('createFile (mixed parallel/sequential)', async () => { - const single1 = makeBatchTester(1, 'single1'); - const single2 = makeBatchTester(1, 'single2'); - - const batch1 = makeBatchTester(20, 'batch1'); - const batch2 = makeBatchTester(20, 'batch2'); - - single1.create(); - batch1.create(); - await Promise.all([single1.assertContentsCorrect(), batch1.assertContentsCorrect()]); - single2.create(); - batch2.create(); - await Promise.all([single2.assertContentsCorrect(), batch2.assertContentsCorrect()]); - await Promise.all([single1.assertContentsCorrect(), batch1.assertContentsCorrect()]); - - await (Promise.all([single1.delete(), single2.delete(), batch1.delete(), batch2.delete()])); - await (Promise.all([single1.assertContentsEmpty(), single2.assertContentsEmpty(), batch1.assertContentsEmpty(), batch2.assertContentsEmpty()])); + runWithFakedTimers({ useFakeTimers: true }, async () => { + const single1 = makeBatchTester(1, 'single1'); + const single2 = makeBatchTester(1, 'single2'); + + const batch1 = makeBatchTester(20, 'batch1'); + const batch2 = makeBatchTester(20, 'batch2'); + + single1.create(); + batch1.create(); + await Promise.all([single1.assertContentsCorrect(), batch1.assertContentsCorrect()]); + single2.create(); + batch2.create(); + await Promise.all([single2.assertContentsCorrect(), batch2.assertContentsCorrect()]); + await Promise.all([single1.assertContentsCorrect(), batch1.assertContentsCorrect()]); + + await (Promise.all([single1.delete(), single2.delete(), batch1.delete(), batch2.delete()])); + await (Promise.all([single1.assertContentsEmpty(), single2.assertContentsEmpty(), batch1.assertContentsEmpty(), batch2.assertContentsEmpty()])); + }); }); test('rename not existing resource', async () => { From 4b3becfae1014b49f24262c382fa72fbba164107 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 17 May 2022 17:44:52 +0200 Subject: [PATCH 106/942] revert the change (#149726) * revert the change * remove import --- .../test/browser/indexedDBFileService.test.ts | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/vs/platform/files/test/browser/indexedDBFileService.test.ts b/src/vs/platform/files/test/browser/indexedDBFileService.test.ts index 370a9458263c2..3387e3b989bf2 100644 --- a/src/vs/platform/files/test/browser/indexedDBFileService.test.ts +++ b/src/vs/platform/files/test/browser/indexedDBFileService.test.ts @@ -11,7 +11,6 @@ import { Schemas } from 'vs/base/common/network'; import { basename, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { flakySuite } from 'vs/base/test/common/testUtils'; -import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { IndexedDBFileSystemProvider } from 'vs/platform/files/browser/indexedDBFileSystemProvider'; import { FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FileSystemProviderError, FileSystemProviderErrorCode, FileType, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; @@ -273,24 +272,22 @@ flakySuite('IndexedDBFileSystemProvider', function () { }); test('createFile (mixed parallel/sequential)', async () => { - runWithFakedTimers({ useFakeTimers: true }, async () => { - const single1 = makeBatchTester(1, 'single1'); - const single2 = makeBatchTester(1, 'single2'); - - const batch1 = makeBatchTester(20, 'batch1'); - const batch2 = makeBatchTester(20, 'batch2'); - - single1.create(); - batch1.create(); - await Promise.all([single1.assertContentsCorrect(), batch1.assertContentsCorrect()]); - single2.create(); - batch2.create(); - await Promise.all([single2.assertContentsCorrect(), batch2.assertContentsCorrect()]); - await Promise.all([single1.assertContentsCorrect(), batch1.assertContentsCorrect()]); - - await (Promise.all([single1.delete(), single2.delete(), batch1.delete(), batch2.delete()])); - await (Promise.all([single1.assertContentsEmpty(), single2.assertContentsEmpty(), batch1.assertContentsEmpty(), batch2.assertContentsEmpty()])); - }); + const single1 = makeBatchTester(1, 'single1'); + const single2 = makeBatchTester(1, 'single2'); + + const batch1 = makeBatchTester(20, 'batch1'); + const batch2 = makeBatchTester(20, 'batch2'); + + single1.create(); + batch1.create(); + await Promise.all([single1.assertContentsCorrect(), batch1.assertContentsCorrect()]); + single2.create(); + batch2.create(); + await Promise.all([single2.assertContentsCorrect(), batch2.assertContentsCorrect()]); + await Promise.all([single1.assertContentsCorrect(), batch1.assertContentsCorrect()]); + + await (Promise.all([single1.delete(), single2.delete(), batch1.delete(), batch2.delete()])); + await (Promise.all([single1.assertContentsEmpty(), single2.assertContentsEmpty(), batch1.assertContentsEmpty(), batch2.assertContentsEmpty()])); }); test('rename not existing resource', async () => { From 2c97c8064722c44a10cf548990d3457864b6c89f Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 17 May 2022 17:52:20 +0200 Subject: [PATCH 107/942] Adopt `setTimeout0()` which doesn't suffer from the 4ms artificial delay that browsers set when the nesting level is > 5. (#149723) --- src/vs/base/test/common/timeTravelScheduler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/test/common/timeTravelScheduler.ts b/src/vs/base/test/common/timeTravelScheduler.ts index 9081b7c5632cc..470d93ac86486 100644 --- a/src/vs/base/test/common/timeTravelScheduler.ts +++ b/src/vs/base/test/common/timeTravelScheduler.ts @@ -5,6 +5,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { setTimeout0 } from 'vs/base/common/platform'; interface PriorityQueue { length: number; @@ -178,7 +179,7 @@ export class AsyncSchedulerProcessor extends Disposable { if (this.useSetImmediate) { originalGlobalValues.setImmediate(() => this.process()); } else { - originalGlobalValues.setTimeout(() => this.process()); + setTimeout0(() => this.process()); } }); } From 9ae563f53bd406c28a2415d2df9f335a5ced8729 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 17 May 2022 09:22:29 -0700 Subject: [PATCH 108/942] Pick up latest Marked.js release (#149729) Fixes #142003 --- src/vs/base/common/marked/cgmanifest.json | 4 +- src/vs/base/common/marked/marked.js | 252 ++++++++++++++++------ 2 files changed, 183 insertions(+), 73 deletions(-) diff --git a/src/vs/base/common/marked/cgmanifest.json b/src/vs/base/common/marked/cgmanifest.json index 47100d82d7f4d..60e11b4144ea9 100644 --- a/src/vs/base/common/marked/cgmanifest.json +++ b/src/vs/base/common/marked/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "marked", "repositoryUrl": "https://github.com/markedjs/marked", - "commitHash": "d1b7d521c41bcf915f81f0218b0e5acd607c1b72" + "commitHash": "2002557d004139ca2208c910d9ca999829b65406" } }, "license": "MIT", - "version": "3.0.2" + "version": "4.0.16" } ], "version": 1 diff --git a/src/vs/base/common/marked/marked.js b/src/vs/base/common/marked/marked.js index 09c308378d455..b7d8fe40e5e1b 100644 --- a/src/vs/base/common/marked/marked.js +++ b/src/vs/base/common/marked/marked.js @@ -23,6 +23,7 @@ typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.marked = {})); })(this, (function (exports) { 'use strict'; + function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; @@ -141,6 +142,10 @@ return html; } var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; + /** + * @param {string} html + */ + function unescape(html) { // explicitly match decimal, hex, and named HTML entities return html.replace(unescapeTest, function (_, n) { @@ -155,8 +160,13 @@ }); } var caret = /(^|[^\[])\^/g; + /** + * @param {string | RegExp} regex + * @param {string} opt + */ + function edit(regex, opt) { - regex = regex.source || regex; + regex = typeof regex === 'string' ? regex : regex.source; opt = opt || ''; var obj = { replace: function replace(name, val) { @@ -173,6 +183,12 @@ } var nonWordAndColonTest = /[^\w:]/g; var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; + /** + * @param {boolean} sanitize + * @param {string} base + * @param {string} href + */ + function cleanUrl(sanitize, base, href) { if (sanitize) { var prot; @@ -204,6 +220,11 @@ var justDomain = /^[^:]+:\/*[^/]*$/; var protocol = /^([^:]+:)[\s\S]*$/; var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/; + /** + * @param {string} base + * @param {string} href + */ + function resolveUrl(base, href) { if (!baseUrls[' ' + base]) { // we can ignore everything in base after the last slash of its path component, @@ -282,7 +303,7 @@ cells.shift(); } - if (!cells[cells.length - 1].trim()) { + if (cells.length > 0 && !cells[cells.length - 1].trim()) { cells.pop(); } @@ -300,9 +321,15 @@ } return cells; - } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). - // /c*$/ is vulnerable to REDOS. - // invert: Remove suffix of non-c chars instead. Default falsey. + } + /** + * Remove trailing 'c's. Equivalent to str.replace(/c*$/, ''). + * /c*$/ is vulnerable to REDOS. + * + * @param {string} str + * @param {string} c + * @param {boolean} invert Remove suffix of non-c chars instead. Default falsey. + */ function rtrim(str, c, invert) { var l = str.length; @@ -326,7 +353,7 @@ } } - return str.substr(0, l - suffLen); + return str.slice(0, l - suffLen); } function findClosingBracket(str, b) { if (str.indexOf(b[1]) === -1) { @@ -359,6 +386,11 @@ } } // copied from https://stackoverflow.com/a/5450113/806777 + /** + * @param {string} pattern + * @param {number} count + */ + function repeatString(pattern, count) { if (count < 1) { return ''; @@ -395,15 +427,15 @@ }; lexer.state.inLink = false; return token; - } else { - return { - type: 'image', - raw: raw, - href: href, - title: title, - text: escape(text) - }; } + + return { + type: 'image', + raw: raw, + href: href, + title: title, + text: escape(text) + }; } function indentCodeCompensation(raw, text) { @@ -446,11 +478,11 @@ var cap = this.rules.block.newline.exec(src); if (cap && cap[0].length > 0) { - return { - type: 'space', - raw: cap[0] - }; - } + return { + type: 'space', + raw: cap[0] + }; + } }; _proto.code = function code(src) { @@ -526,7 +558,7 @@ var cap = this.rules.block.blockquote.exec(src); if (cap) { - var text = cap[0].replace(/^ *> ?/gm, ''); + var text = cap[0].replace(/^ *>[ \t]?/gm, ''); return { type: 'blockquote', raw: cap[0], @@ -558,7 +590,7 @@ } // Get next list item - var itemRegex = new RegExp("^( {0,3}" + bull + ")((?: [^\\n]*)?(?:\\n|$))"); // Check if current bullet point can start a new List Item + var itemRegex = new RegExp("^( {0,3}" + bull + ")((?:[\t ][^\\n]*)?(?:\\n|$))"); // Check if current bullet point can start a new List Item while (src) { endEarly = false; @@ -599,31 +631,37 @@ } if (!endEarly) { - var nextBulletRegex = new RegExp("^ {0," + Math.min(3, indent - 1) + "}(?:[*+-]|\\d{1,9}[.)])"); // Check if following lines should be included in List Item + var nextBulletRegex = new RegExp("^ {0," + Math.min(3, indent - 1) + "}(?:[*+-]|\\d{1,9}[.)])((?: [^\\n]*)?(?:\\n|$))"); + var hrRegex = new RegExp("^ {0," + Math.min(3, indent - 1) + "}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)"); // Check if following lines should be included in List Item while (src) { rawLine = src.split('\n', 1)[0]; line = rawLine; // Re-align to follow commonmark nesting rules - if (this.options.pedantic) { - line = line.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' '); - } // End list item if found start of new bullet + if (this.options.pedantic) { + line = line.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' '); + } // End list item if found start of new bullet - if (nextBulletRegex.test(line)) { - break; + if (nextBulletRegex.test(line)) { + break; + } // Horizontal rule found + + + if (hrRegex.test(src)) { + break; } - if (line.search(/[^ ]/) >= indent || !line.trim()) { + if (line.search(/[^ ]/) >= indent || !line.trim()) { // Dedent if possible - itemContents += '\n' + line.slice(indent); + itemContents += '\n' + line.slice(indent); } else if (!blankLine) { // Until blank line, item doesn't need indentation itemContents += '\n' + line; - } else { + } else { // Otherwise, improper indentation ends this item - break; - } + break; + } if (!blankLine && !line.trim()) { // Check if current line is blank @@ -757,7 +795,7 @@ }; }), align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - rows: cap[3] ? cap[3].replace(/\n[ \t]*$/, '').split('\n') : [] + rows: cap[3] && cap[3].trim() ? cap[3].replace(/\n[ \t]*$/, '').split('\n') : [] }; if (item.header.length === item.align.length) { @@ -793,7 +831,7 @@ for (j = 0; j < l; j++) { item.header[j].tokens = []; - this.lexer.inlineTokens(item.header[j].text, item.header[j].tokens); + this.lexer.inline(item.header[j].text, item.header[j].tokens); } // cell child tokens @@ -804,7 +842,7 @@ for (k = 0; k < row.length; k++) { row[k].tokens = []; - this.lexer.inlineTokens(row[k].text, row[k].tokens); + this.lexer.inline(row[k].text, row[k].tokens); } } @@ -1195,10 +1233,10 @@ newline: /^(?: *(?:\n|$))+/, code: /^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/, fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?=\n|$)|$)/, - hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/, + hr: /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/, heading: /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/, blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/, - list: /^( {0,3}bull)( [^\n]+?)?(?:\n|$)/, + list: /^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/, html: '^ {0,3}(?:' // optional indentation + '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) @@ -1288,9 +1326,9 @@ emStrong: { lDelim: /^(?:\*+(?:([punct_])|[^\s*]))|^_+(?:([punct*])|([^\s_]))/, // (1) and (2) can only be a Right Delimiter. (3) and (4) can only be Left. (5) and (6) can be either Left or Right. - // () Skip orphan delim inside strong (1) #*** (2) a***#, a*** (3) #***a, ***a (4) ***# (5) #***# (6) a***a - rDelimAst: /^[^_*]*?\_\_[^_*]*?\*[^_*]*?(?=\_\_)|[punct_](\*+)(?=[\s]|$)|[^punct*_\s](\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|[^punct*_\s](\*+)(?=[^punct*_\s])/, - rDelimUnd: /^[^_*]*?\*\*[^_*]*?\_[^_*]*?(?=\*\*)|[punct*](\_+)(?=[\s]|$)|[^punct*_\s](\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/ // ^- Not allowed for _ + // () Skip orphan inside strong () Consume to delim (1) #*** (2) a***#, a*** (3) #***a, ***a (4) ***# (5) #***# (6) a***a + rDelimAst: /^[^_*]*?\_\_[^_*]*?\*[^_*]*?(?=\_\_)|[^*]+(?=[^*])|[punct_](\*+)(?=[\s]|$)|[^punct*_\s](\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|[^punct*_\s](\*+)(?=[^punct*_\s])/, + rDelimUnd: /^[^_*]*?\*\*[^_*]*?\_[^_*]*?(?=\*\*)|[^_]+(?=[^_])|[punct*](\_+)(?=[\s]|$)|[^punct*_\s](\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/ // ^- Not allowed for _ }, code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, @@ -1372,6 +1410,7 @@ /** * smartypants text replacement + * @param {string} text */ function smartypants(text) { @@ -1386,6 +1425,7 @@ } /** * mangle email addresses + * @param {string} text */ @@ -1476,7 +1516,7 @@ var _proto = Lexer.prototype; _proto.lex = function lex(src) { - src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' '); + src = src.replace(/\r\n|\r/g, '\n'); this.blockTokens(src, this.tokens); var next; @@ -1499,7 +1539,11 @@ } if (this.options.pedantic) { - src = src.replace(/^ +$/gm, ''); + src = src.replace(/\t/g, ' ').replace(/^ +$/gm, ''); + } else { + src = src.replace(/^( *)(\t+)/gm, function (_, leading, tabs) { + return leading + ' '.repeat(tabs.length); + }); } var token, lastToken, cutSrc, lastParagraphClipped; @@ -1959,23 +2003,35 @@ } return '
' + (escaped ? _code : escape(_code, true)) + '
\n'; - }; + } + /** + * @param {string} quote + */ + ; _proto.blockquote = function blockquote(quote) { - return '
\n' + quote + '
\n'; + return "
\n" + quote + "
\n"; }; _proto.html = function html(_html) { return _html; - }; + } + /** + * @param {string} text + * @param {string} level + * @param {string} raw + * @param {any} slugger + */ + ; _proto.heading = function heading(text, level, raw, slugger) { if (this.options.headerIds) { - return '' + text + '\n'; + var id = this.options.headerPrefix + slugger.slug(raw); + return "" + text + "\n"; } // ignore IDs - return '' + text + '\n'; + return "" + text + "\n"; }; _proto.hr = function hr() { @@ -1986,55 +2042,94 @@ var type = ordered ? 'ol' : 'ul', startatt = ordered && start !== 1 ? ' start="' + start + '"' : ''; return '<' + type + startatt + '>\n' + body + '\n'; - }; + } + /** + * @param {string} text + */ + ; _proto.listitem = function listitem(text) { - return '
  • ' + text + '
  • \n'; + return "
  • " + text + "
  • \n"; }; _proto.checkbox = function checkbox(checked) { return ' '; - }; + } + /** + * @param {string} text + */ + ; _proto.paragraph = function paragraph(text) { - return '

    ' + text + '

    \n'; - }; + return "

    " + text + "

    \n"; + } + /** + * @param {string} header + * @param {string} body + */ + ; _proto.table = function table(header, body) { - if (body) body = '' + body + ''; + if (body) body = "" + body + ""; return '\n' + '\n' + header + '\n' + body + '
    \n'; - }; + } + /** + * @param {string} content + */ + ; _proto.tablerow = function tablerow(content) { - return '\n' + content + '\n'; + return "\n" + content + "\n"; }; _proto.tablecell = function tablecell(content, flags) { var type = flags.header ? 'th' : 'td'; - var tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>'; - return tag + content + '\n'; - } // span level renderer + var tag = flags.align ? "<" + type + " align=\"" + flags.align + "\">" : "<" + type + ">"; + return tag + content + ("\n"); + } + /** + * span level renderer + * @param {string} text + */ ; _proto.strong = function strong(text) { - return '' + text + ''; - }; + return "" + text + ""; + } + /** + * @param {string} text + */ + ; _proto.em = function em(text) { - return '' + text + ''; - }; + return "" + text + ""; + } + /** + * @param {string} text + */ + ; _proto.codespan = function codespan(text) { - return '' + text + ''; + return "" + text + ""; }; _proto.br = function br() { return this.options.xhtml ? '
    ' : '
    '; - }; + } + /** + * @param {string} text + */ + ; _proto.del = function del(text) { - return '' + text + ''; - }; + return "" + text + ""; + } + /** + * @param {string} href + * @param {string} title + * @param {string} text + */ + ; _proto.link = function link(href, title, text) { href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); @@ -2051,7 +2146,13 @@ out += '>' + text + ''; return out; - }; + } + /** + * @param {string} href + * @param {string} title + * @param {string} text + */ + ; _proto.image = function image(href, title, text) { href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); @@ -2060,10 +2161,10 @@ return text; } - var out = '' + text + '' : '>'; @@ -2133,6 +2234,10 @@ function Slugger() { this.seen = {}; } + /** + * @param {string} value + */ + var _proto = Slugger.prototype; @@ -2143,6 +2248,8 @@ } /** * Finds the next safe (unique) slug to use + * @param {string} originalSlug + * @param {boolean} isDryRun */ ; @@ -2168,8 +2275,9 @@ } /** * Convert string to unique id - * @param {object} options - * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator. + * @param {object} [options] + * @param {boolean} [options.dryrun] Generates the next unique slug without + * updating the internal accumulator. */ ; @@ -2866,6 +2974,7 @@ }; /** * Parse Inline + * @param {string} src */ @@ -2941,6 +3050,7 @@ exports.walkTokens = walkTokens; Object.defineProperty(exports, '__esModule', { value: true }); + })); // ESM-uncomment-begin From 1c7ab357618b568746790b5a30f204794aa50afa Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 17 May 2022 09:26:54 -0700 Subject: [PATCH 109/942] Rename to DocumentOnDropEditProvider (#149730) This change renames `DocumentOnDropProvider` to `DocumentOnDropEditProvider`. This new name aligns with the existing `DocumentFormattingEditProvider` --- .../src/languageFeatures/dropIntoEditor.ts | 2 +- .../api/browser/mainThreadLanguageFeatures.ts | 14 +++++++------- src/vs/workbench/api/common/extHost.api.impl.ts | 4 ++-- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostLanguageFeatures.ts | 14 +++++++------- src/vscode-dts/vscode.proposed.textEditorDrop.d.ts | 6 +++--- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts index c3fb1f55631bf..d5d7c4d9d2916 100644 --- a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts +++ b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts @@ -24,7 +24,7 @@ const imageFileExtensions = new Set([ ]); export function registerDropIntoEditor(selector: vscode.DocumentSelector) { - return vscode.languages.registerDocumentOnDropProvider(selector, new class implements vscode.DocumentOnDropProvider { + return vscode.languages.registerDocumentOnDropEditProvider(selector, new class implements vscode.DocumentOnDropEditProvider { async provideDocumentOnDropEdits(document: vscode.TextDocument, position: vscode.Position, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.drop.enabled', true); if (!enabled) { diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index e01c59d4ec4f1..07d04a05c60c4 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -863,19 +863,19 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread // --- document drop Edits - private readonly _documentOnDropProviders = new Map(); + private readonly _documentOnDropEditProviders = new Map(); - $registerDocumentOnDropProvider(handle: number, selector: IDocumentFilterDto[]): void { - const provider = new MainThreadDocumentOnDropProvider(handle, this._proxy); - this._documentOnDropProviders.set(handle, provider); + $registerDocumentOnDropEditProvider(handle: number, selector: IDocumentFilterDto[]): void { + const provider = new MainThreadDocumentOnDropEditProvider(handle, this._proxy); + this._documentOnDropEditProviders.set(handle, provider); this._registrations.set(handle, combinedDisposable( this._languageFeaturesService.documentOnDropEditProvider.register(selector, provider), - toDisposable(() => this._documentOnDropProviders.delete(handle)), + toDisposable(() => this._documentOnDropEditProviders.delete(handle)), )); } async $resolveDocumentOnDropFileData(handle: number, requestId: number, dataIndex: number): Promise { - const provider = this._documentOnDropProviders.get(handle); + const provider = this._documentOnDropEditProviders.get(handle); if (!provider) { throw new Error('Could not find provider'); } @@ -883,7 +883,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread } } -class MainThreadDocumentOnDropProvider implements languages.DocumentOnDropEditProvider { +class MainThreadDocumentOnDropEditProvider implements languages.DocumentOnDropEditProvider { private readonly dataTransfers = new DataTransferCache(); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index c2ad7565594ce..313a9aadfd12e 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -570,9 +570,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I createLanguageStatusItem(id: string, selector: vscode.DocumentSelector): vscode.LanguageStatusItem { return extHostLanguages.createLanguageStatusItem(extension, id, selector); }, - registerDocumentOnDropProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentOnDropProvider): vscode.Disposable { + registerDocumentOnDropEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentOnDropEditProvider): vscode.Disposable { checkProposedApiEnabled(extension, 'textEditorDrop'); - return extHostLanguageFeatures.registerDocumentOnDropProvider(extension, selector, provider); + return extHostLanguageFeatures.registerDocumentOnDropEditProvider(extension, selector, provider); } }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9fa08b4b6c31b..6d936351c3556 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -392,7 +392,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerSelectionRangeProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerCallHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerTypeHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void; - $registerDocumentOnDropProvider(handle: number, selector: IDocumentFilterDto[]): void; + $registerDocumentOnDropEditProvider(handle: number, selector: IDocumentFilterDto[]): void; $resolveDocumentOnDropFileData(handle: number, requestId: number, dataIndex: number): Promise; $setLanguageConfiguration(handle: number, languageId: string, configuration: ILanguageConfigurationDto): void; } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 32bf1eab95313..3b6b789641a62 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1745,12 +1745,12 @@ class TypeHierarchyAdapter { } } -class DocumentOnDropAdapter { +class DocumentOnDropEditAdapter { constructor( private readonly _proxy: extHostProtocol.MainThreadLanguageFeaturesShape, private readonly _documents: ExtHostDocuments, - private readonly _provider: vscode.DocumentOnDropProvider, + private readonly _provider: vscode.DocumentOnDropEditProvider, private readonly _handle: number, ) { } @@ -1778,7 +1778,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter | InlineValuesAdapter | LinkedEditingRangeAdapter | InlayHintsAdapter | InlineCompletionAdapter | InlineCompletionAdapterNew - | DocumentOnDropAdapter; + | DocumentOnDropEditAdapter; class AdapterData { constructor( @@ -2402,15 +2402,15 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- Document on drop - registerDocumentOnDropProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentOnDropProvider) { + registerDocumentOnDropEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentOnDropEditProvider) { const handle = this._nextHandle(); - this._adapter.set(handle, new AdapterData(new DocumentOnDropAdapter(this._proxy, this._documents, provider, handle), extension)); - this._proxy.$registerDocumentOnDropProvider(handle, this._transformDocumentSelector(selector)); + this._adapter.set(handle, new AdapterData(new DocumentOnDropEditAdapter(this._proxy, this._documents, provider, handle), extension)); + this._proxy.$registerDocumentOnDropEditProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } $provideDocumentOnDropEdits(handle: number, requestId: number, resource: UriComponents, position: IPosition, dataTransferDto: DataTransferDTO, token: CancellationToken): Promise | undefined> { - return this._withAdapter(handle, DocumentOnDropAdapter, adapter => + return this._withAdapter(handle, DocumentOnDropEditAdapter, adapter => Promise.resolve(adapter.provideDocumentOnDropEdits(requestId, URI.revive(resource), position, dataTransferDto, token)), undefined, undefined); } diff --git a/src/vscode-dts/vscode.proposed.textEditorDrop.d.ts b/src/vscode-dts/vscode.proposed.textEditorDrop.d.ts index 879e5a757b718..5468bc218cd83 100644 --- a/src/vscode-dts/vscode.proposed.textEditorDrop.d.ts +++ b/src/vscode-dts/vscode.proposed.textEditorDrop.d.ts @@ -18,7 +18,7 @@ declare module 'vscode' { * * The user can drop into a text editor by holding down `shift` while dragging. Requires `workbench.experimental.editor.dropIntoEditor.enabled` to be on. */ - export interface DocumentOnDropProvider { + export interface DocumentOnDropEditProvider { /** * Provide edits which inserts the content being dragged and dropped into the document. * @@ -35,13 +35,13 @@ declare module 'vscode' { export namespace languages { /** - * Registers a new {@link DocumentOnDropProvider}. + * Registers a new {@link DocumentOnDropEditProvider}. * * @param selector A selector that defines the documents this provider applies to. * @param provider A drop provider. * * @return A {@link Disposable} that unregisters this provider when disposed of. */ - export function registerDocumentOnDropProvider(selector: DocumentSelector, provider: DocumentOnDropProvider): Disposable; + export function registerDocumentOnDropEditProvider(selector: DocumentSelector, provider: DocumentOnDropEditProvider): Disposable; } } From aef0e27d249ae48f856a410a7bf7c447a9e1f7f8 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 17 May 2022 10:00:20 -0700 Subject: [PATCH 110/942] fix layer break (#149735) --- .../platform/terminal/common/xterm/shellIntegrationAddon.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index c28607fc175e2..df36e27041d8f 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -125,7 +125,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private _terminal?: Terminal; readonly capabilities = new TerminalCapabilityStore(); private _hasUpdatedTelemetry: boolean = false; - private _activationTimeout: number | undefined; + private _activationTimeout: any; constructor( private readonly _telemetryService: ITelemetryService | undefined, @@ -230,7 +230,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati } private async _ensureCapabilitiesOrAddFailureTelemetry(): Promise { - this._activationTimeout = window.setTimeout(() => { + this._activationTimeout = setTimeout(() => { if (!this.capabilities.get(TerminalCapability.CommandDetection) && !this.capabilities.get(TerminalCapability.CwdDetection)) { this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivationTimeout'); this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); From 16db4dc2bd5221ad61aba1f22d7c4d77a60452d4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 17 May 2022 19:10:37 +0200 Subject: [PATCH 111/942] make tests faster - remove unnecessary validations - group writes --- .../test/browser/indexedDBFileService.test.ts | 94 ++++++++----------- 1 file changed, 38 insertions(+), 56 deletions(-) diff --git a/src/vs/platform/files/test/browser/indexedDBFileService.test.ts b/src/vs/platform/files/test/browser/indexedDBFileService.test.ts index 3387e3b989bf2..a12b43189f0ff 100644 --- a/src/vs/platform/files/test/browser/indexedDBFileService.test.ts +++ b/src/vs/platform/files/test/browser/indexedDBFileService.test.ts @@ -12,20 +12,16 @@ import { basename, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { flakySuite } from 'vs/base/test/common/testUtils'; import { IndexedDBFileSystemProvider } from 'vs/platform/files/browser/indexedDBFileSystemProvider'; -import { FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FileSystemProviderError, FileSystemProviderErrorCode, FileType, IFileStatWithMetadata } from 'vs/platform/files/common/files'; +import { FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FileSystemProviderError, FileSystemProviderErrorCode, FileType } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; import { NullLogService } from 'vs/platform/log/common/log'; flakySuite('IndexedDBFileSystemProvider', function () { - const logSchema = 'logs'; - let service: FileService; - let logFileProvider: IndexedDBFileSystemProvider; let userdataFileProvider: IndexedDBFileSystemProvider; const testDir = '/'; - const logfileURIFromPaths = (paths: string[]) => joinPath(URI.from({ scheme: logSchema, path: testDir }), ...paths); const userdataURIFromPaths = (paths: readonly string[]) => joinPath(URI.from({ scheme: Schemas.vscodeUserData, path: testDir }), ...paths); const disposables = new DisposableStore(); @@ -69,10 +65,6 @@ flakySuite('IndexedDBFileSystemProvider', function () { const indexedDB = await IndexedDB.create('vscode-web-db-test', 1, ['vscode-userdata-store', 'vscode-logs-store']); - logFileProvider = new IndexedDBFileSystemProvider(logSchema, indexedDB, 'vscode-logs-store', false); - disposables.add(service.registerProvider(logSchema, logFileProvider)); - disposables.add(logFileProvider); - userdataFileProvider = new IndexedDBFileSystemProvider(Schemas.vscodeUserData, indexedDB, 'vscode-userdata-store', true); disposables.add(service.registerProvider(Schemas.vscodeUserData, userdataFileProvider)); disposables.add(userdataFileProvider); @@ -84,8 +76,7 @@ flakySuite('IndexedDBFileSystemProvider', function () { }); teardown(async () => { - await logFileProvider.delete(logfileURIFromPaths([]), { recursive: true, useTrash: false }); - await userdataFileProvider.delete(userdataURIFromPaths([]), { recursive: true, useTrash: false }); + await userdataFileProvider.reset(); disposables.clear(); }); @@ -234,60 +225,43 @@ flakySuite('IndexedDBFileSystemProvider', function () { assert.strictEqual(event!.target!.resource.path, resource.path); } - const makeBatchTester = (size: number, name: string) => { + const fileCreateBatchTester = (size: number, name: string) => { const batch = Array.from({ length: size }).map((_, i) => ({ contents: `Hello${i}`, resource: userdataURIFromPaths(['batched', name, `Hello${i}.txt`]) })); - let stats: Promise | undefined = undefined; + let creationPromises: Promise | undefined = undefined; return { async create() { - return stats = Promise.all(batch.map(entry => service.createFile(entry.resource, VSBuffer.fromString(entry.contents)))); + return creationPromises = Promise.all(batch.map(entry => userdataFileProvider.writeFile(entry.resource, VSBuffer.fromString(entry.contents).buffer, { create: true, overwrite: true, unlock: false }))); }, async assertContentsCorrect() { + if (!creationPromises) { throw Error('read called before create'); } + await creationPromises; await Promise.all(batch.map(async (entry, i) => { - if (!stats) { throw Error('read called before create'); } - const stat = (await stats!)[i]; - assert.strictEqual(stat.name, `Hello${i}.txt`); - assert.strictEqual((await userdataFileProvider.stat(stat.resource)).type, FileType.File); - assert.strictEqual(new TextDecoder().decode(await userdataFileProvider.readFile(stat.resource)), entry.contents); - })); - }, - async delete() { - await service.del(userdataURIFromPaths(['batched', name]), { recursive: true, useTrash: false }); - }, - async assertContentsEmpty() { - if (!stats) { throw Error('assertContentsEmpty called before create'); } - await Promise.all((await stats).map(async stat => { - const newStat = await userdataFileProvider.stat(stat.resource).catch(e => e.code); - assert.strictEqual(newStat, FileSystemProviderErrorCode.FileNotFound); + assert.strictEqual((await userdataFileProvider.stat(entry.resource)).type, FileType.File); + assert.strictEqual(new TextDecoder().decode(await userdataFileProvider.readFile(entry.resource)), entry.contents); })); } }; }; - test('createFile (small batch)', async () => { - const tester = makeBatchTester(50, 'smallBatch'); + test('createFile - batch', async () => { + const tester = fileCreateBatchTester(20, 'batch'); await tester.create(); await tester.assertContentsCorrect(); - await tester.delete(); - await tester.assertContentsEmpty(); }); - test('createFile (mixed parallel/sequential)', async () => { - const single1 = makeBatchTester(1, 'single1'); - const single2 = makeBatchTester(1, 'single2'); - - const batch1 = makeBatchTester(20, 'batch1'); - const batch2 = makeBatchTester(20, 'batch2'); + test('createFile - batch (mixed parallel/sequential)', async () => { + const batch1 = fileCreateBatchTester(1, 'batch1'); + const batch2 = fileCreateBatchTester(20, 'batch2'); + const batch3 = fileCreateBatchTester(1, 'batch3'); + const batch4 = fileCreateBatchTester(20, 'batch4'); - single1.create(); batch1.create(); - await Promise.all([single1.assertContentsCorrect(), batch1.assertContentsCorrect()]); - single2.create(); batch2.create(); - await Promise.all([single2.assertContentsCorrect(), batch2.assertContentsCorrect()]); - await Promise.all([single1.assertContentsCorrect(), batch1.assertContentsCorrect()]); - - await (Promise.all([single1.delete(), single2.delete(), batch1.delete(), batch2.delete()])); - await (Promise.all([single1.assertContentsEmpty(), single2.assertContentsEmpty(), batch1.assertContentsEmpty(), batch2.assertContentsEmpty()])); + await Promise.all([batch1.assertContentsCorrect(), batch2.assertContentsCorrect()]); + batch3.create(); + batch4.create(); + await Promise.all([batch3.assertContentsCorrect(), batch4.assertContentsCorrect()]); + await Promise.all([batch1.assertContentsCorrect(), batch2.assertContentsCorrect()]); }); test('rename not existing resource', async () => { @@ -392,10 +366,12 @@ flakySuite('IndexedDBFileSystemProvider', function () { test('rename to an existing file with overwrite', async () => { const parent = await service.resolve(userdataURIFromPaths([])); const sourceFile = joinPath(parent.resource, 'sourceFile'); - await service.writeFile(sourceFile, VSBuffer.fromString('This is source file')); - const targetFile = joinPath(parent.resource, 'targetFile'); - await service.writeFile(targetFile, VSBuffer.fromString('This is target file')); + + await Promise.all([ + service.writeFile(sourceFile, VSBuffer.fromString('This is source file')), + service.writeFile(targetFile, VSBuffer.fromString('This is target file')) + ]); await service.move(sourceFile, targetFile, true); @@ -434,11 +410,14 @@ flakySuite('IndexedDBFileSystemProvider', function () { const sourceFolder = joinPath(parent.resource, 'sourceFolder'); const sourceFile1 = joinPath(sourceFolder, 'folder1', 'file1'); - await service.writeFile(sourceFile1, VSBuffer.fromString('Source File 1')); const sourceFile2 = joinPath(sourceFolder, 'folder2', 'file1'); - await service.writeFile(sourceFile2, VSBuffer.fromString('Source File 2')); const sourceEmptyFolder = joinPath(sourceFolder, 'folder3'); - await service.createFolder(sourceEmptyFolder); + + await Promise.all([ + service.writeFile(sourceFile1, VSBuffer.fromString('Source File 1')), + service.writeFile(sourceFile2, VSBuffer.fromString('Source File 2')), + service.createFolder(sourceEmptyFolder) + ]); const targetFolder = joinPath(parent.resource, 'targetFolder'); const targetFile1 = joinPath(targetFolder, 'folder1', 'file1'); @@ -459,14 +438,17 @@ flakySuite('IndexedDBFileSystemProvider', function () { const sourceFolder = joinPath(parent.resource, 'sourceFolder'); const sourceFile1 = joinPath(sourceFolder, 'folder1', 'file1'); - await service.writeFile(sourceFile1, VSBuffer.fromString('Source File 1')); const targetFolder = joinPath(parent.resource, 'targetFolder'); const targetFile1 = joinPath(targetFolder, 'folder1', 'file1'); const targetFile2 = joinPath(targetFolder, 'folder1', 'file2'); - await service.writeFile(targetFile2, VSBuffer.fromString('Target File 2')); const targetFile3 = joinPath(targetFolder, 'folder2', 'file1'); - await service.writeFile(targetFile3, VSBuffer.fromString('Target File 3')); + + await Promise.all([ + service.writeFile(sourceFile1, VSBuffer.fromString('Source File 1')), + service.writeFile(targetFile2, VSBuffer.fromString('Target File 2')), + service.writeFile(targetFile3, VSBuffer.fromString('Target File 3')) + ]); await service.move(sourceFolder, targetFolder, true); From aa2996cbb7da1022d378d3425c6bc0027481d074 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 17 May 2022 10:40:45 -0700 Subject: [PATCH 112/942] Localize "Suggested" (#149684) For #146338 --- src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts index 06a6e7f8ea448..7f9e2de3788b6 100644 --- a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts @@ -360,7 +360,7 @@ export class AdapterManager extends Disposable implements IAdapterManager { const picks: { label: string; debugger?: Debugger; type?: string }[] = []; if (suggestedCandidates.length > 0) { picks.push( - { type: 'separator', label: 'Suggested' }, + { type: 'separator', label: nls.localize('suggestedDebuggers', "Suggested") }, ...suggestedCandidates.map(c => ({ label: c.label, debugger: c }))); } From 7ca85c375d5d76711e1f53c8637fa336b7be62be Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 17 May 2022 19:45:36 +0200 Subject: [PATCH 113/942] Revert "Adopt `setTimeout0()` which doesn't suffer from the 4ms artificial delay that browsers set when the nesting level is > 5. (#149723)" (#149742) This reverts commit 2c97c8064722c44a10cf548990d3457864b6c89f. --- src/vs/base/test/common/timeTravelScheduler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/base/test/common/timeTravelScheduler.ts b/src/vs/base/test/common/timeTravelScheduler.ts index 470d93ac86486..9081b7c5632cc 100644 --- a/src/vs/base/test/common/timeTravelScheduler.ts +++ b/src/vs/base/test/common/timeTravelScheduler.ts @@ -5,7 +5,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { setTimeout0 } from 'vs/base/common/platform'; interface PriorityQueue { length: number; @@ -179,7 +178,7 @@ export class AsyncSchedulerProcessor extends Disposable { if (this.useSetImmediate) { originalGlobalValues.setImmediate(() => this.process()); } else { - setTimeout0(() => this.process()); + originalGlobalValues.setTimeout(() => this.process()); } }); } From 4f7f2c6135bab4bbe67717aaf8f1c9ca5fb2bc4c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 17 May 2022 19:53:04 +0200 Subject: [PATCH 114/942] convert to integration tests (#149744) --- ...ileService.test.ts => indexedDBFileService.integrationTest.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/vs/platform/files/test/browser/{indexedDBFileService.test.ts => indexedDBFileService.integrationTest.ts} (100%) diff --git a/src/vs/platform/files/test/browser/indexedDBFileService.test.ts b/src/vs/platform/files/test/browser/indexedDBFileService.integrationTest.ts similarity index 100% rename from src/vs/platform/files/test/browser/indexedDBFileService.test.ts rename to src/vs/platform/files/test/browser/indexedDBFileService.integrationTest.ts From d26569164e873e5f3010a83283103a6ae6a66f45 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 17 May 2022 10:57:08 -0700 Subject: [PATCH 115/942] Adopt queue review bot behaviour (#149745) --- .github/workflows/pr-chat.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-chat.yml b/.github/workflows/pr-chat.yml index 95cee4ed1968c..0c8d6bab79388 100644 --- a/.github/workflows/pr-chat.yml +++ b/.github/workflows/pr-chat.yml @@ -1,7 +1,7 @@ name: PR Chat on: pull_request_target: - types: [opened, ready_for_review] + types: [opened, ready_for_review, closed] jobs: main: @@ -20,4 +20,5 @@ jobs: with: token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} slack_token: ${{ secrets.SLACK_TOKEN }} + slack_bot_name: "VSCodeBot" notification_channel: codereview From 06cf633d63a6bc0dbadaf9a446ade3a87da4732f Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 17 May 2022 11:03:19 -0700 Subject: [PATCH 116/942] Revert running on PR close (#149747) --- .github/workflows/pr-chat.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-chat.yml b/.github/workflows/pr-chat.yml index 0c8d6bab79388..969c3cc17c0d2 100644 --- a/.github/workflows/pr-chat.yml +++ b/.github/workflows/pr-chat.yml @@ -1,7 +1,7 @@ name: PR Chat on: pull_request_target: - types: [opened, ready_for_review, closed] + types: [opened, ready_for_review] jobs: main: From 116c10e0eeed56dc9b0801d079e2a2374b6ccf98 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 17 May 2022 11:35:04 -0700 Subject: [PATCH 117/942] Add drop into editor option (#147049) * Add drop into editor option This change adds a new `enableDropIntoEditor` editor option that enables/disables dropping an extermal resources into an editor Previously this option was exposed `IEditorConstructionOptions`, however this did not correctly disable drop into editor when dragging and dropping unknown types (such as dragging emoji from the MacOS emoji panel) With this change, disabling `workbench.editor.dropIntoEditor.enabled` should fully disable the new drop into behavior * Move drop into editor from workbench to editor This moves the `dropIntoEditorContribution` from the workbench layer to the platform layer As part of this change, I also add to move `extractEditorsDropData` up to `platform` so that it could be used from the `editor` layer This change also enables drop into for the SCM message box * Fixing monaco errors * Revert id change --- src/tsconfig.monaco.json | 5 +- .../dnd.ts => base/common/dataTransfer.ts} | 0 .../browser/config/editorConfiguration.ts | 6 - .../editor/browser/widget/codeEditorWidget.ts | 61 +-- src/vs/editor/common/config/editorOptions.ts | 11 + src/vs/editor/common/languages.ts | 6 +- .../common/standalone/standaloneEnums.ts | 203 +++++----- .../browser/dropIntoEditorContribution.ts} | 121 ++++-- src/vs/editor/editor.all.ts | 1 + src/vs/monaco.d.ts | 216 +++++------ src/vs/platform/dnd/browser/dnd.ts | 339 +++++++++++++++++ .../api/browser/mainThreadLanguageFeatures.ts | 2 +- .../api/browser/mainThreadTreeViews.ts | 2 +- .../workbench/api/common/extHostTreeViews.ts | 2 +- .../api/common/shared/dataTransfer.ts | 2 +- .../api/common/shared/dataTransferCache.ts | 2 +- src/vs/workbench/browser/dnd.ts | 356 ++---------------- .../browser/parts/editor/editorDropTarget.ts | 3 +- .../workbench/browser/parts/views/treeView.ts | 5 +- src/vs/workbench/common/views.ts | 2 +- .../extensions/browser/extensionsViewlet.ts | 2 +- .../contrib/files/browser/fileImportExport.ts | 2 +- .../files/browser/views/explorerViewer.ts | 3 +- .../files/browser/views/openEditorsView.ts | 3 +- .../contrib/scm/browser/scmViewPane.ts | 7 +- .../terminal/browser/terminal.contribution.ts | 2 +- .../terminal/browser/terminalInstance.ts | 2 +- .../terminal/browser/terminalTabsList.ts | 2 +- .../dialogs/browser/fileDialogService.ts | 2 +- .../views/browser/treeViewsService.ts | 2 +- src/vs/workbench/workbench.common.main.ts | 3 - 31 files changed, 734 insertions(+), 641 deletions(-) rename src/vs/{editor/common/dnd.ts => base/common/dataTransfer.ts} (100%) rename src/vs/{workbench/contrib/dropIntoEditor/browser/dropIntoEditor.contibution.ts => editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts} (62%) create mode 100644 src/vs/platform/dnd/browser/dnd.ts diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index f9f0c874eb6c6..7d18928f6b449 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -2,7 +2,10 @@ "extends": "./tsconfig.base.json", "compilerOptions": { "noEmit": true, - "types": ["trusted-types"], + "types": [ + "trusted-types", + "wicg-file-system-access" + ], "paths": {}, "module": "amd", "moduleResolution": "classic", diff --git a/src/vs/editor/common/dnd.ts b/src/vs/base/common/dataTransfer.ts similarity index 100% rename from src/vs/editor/common/dnd.ts rename to src/vs/base/common/dataTransfer.ts diff --git a/src/vs/editor/browser/config/editorConfiguration.ts b/src/vs/editor/browser/config/editorConfiguration.ts index 58474ac936306..e3f41e0d4165a 100644 --- a/src/vs/editor/browser/config/editorConfiguration.ts +++ b/src/vs/editor/browser/config/editorConfiguration.ts @@ -30,12 +30,6 @@ export interface IEditorConstructionOptions extends IEditorOptions { * Defaults to an internal DOM node. */ overflowWidgetsDomNode?: HTMLElement; - /** - * Enables dropping into the editor. - * - * This shows a preview of the drop location and triggers an `onDropIntoEditor` event. - */ - enableDropIntoEditor?: boolean; } export class EditorConfiguration extends Disposable implements IEditorConfiguration { diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 801501376ce64..25890eb233209 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -368,35 +368,42 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._actions[internalAction.id] = internalAction; }); - if (_options.enableDropIntoEditor) { - this._register(new dom.DragAndDropObserver(this._domElement, { - onDragEnter: () => undefined, - onDragOver: e => { - const target = this.getTargetAtClientPoint(e.clientX, e.clientY); - if (target?.position) { - this.showDropIndicatorAt(target.position); - } - }, - onDrop: async e => { - this.removeDropIndicator(); + this._register(new dom.DragAndDropObserver(this._domElement, { + onDragEnter: () => undefined, + onDragOver: e => { + if (!this._configuration.options.get(EditorOption.enableDropIntoEditor)) { + return; + } - if (!e.dataTransfer) { - return; - } + const target = this.getTargetAtClientPoint(e.clientX, e.clientY); + if (target?.position) { + this.showDropIndicatorAt(target.position); + } + }, + onDrop: async e => { + if (!this._configuration.options.get(EditorOption.enableDropIntoEditor)) { + return; + } + + this.removeDropIndicator(); + + if (!e.dataTransfer) { + return; + } + + const target = this.getTargetAtClientPoint(e.clientX, e.clientY); + if (target?.position) { + this._onDropIntoEditor.fire({ position: target.position, event: e }); + } + }, + onDragLeave: () => { + this.removeDropIndicator(); + }, + onDragEnd: () => { + this.removeDropIndicator(); + }, + })); - const target = this.getTargetAtClientPoint(e.clientX, e.clientY); - if (target?.position) { - this._onDropIntoEditor.fire({ position: target.position, event: e }); - } - }, - onDragLeave: () => { - this.removeDropIndicator(); - }, - onDragEnd: () => { - this.removeDropIndicator(); - }, - })); - } this._codeEditorService.addCodeEditor(this); } diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index feb0bd01946a8..022895e3e554c 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -659,6 +659,13 @@ export interface IEditorOptions { * Configures bracket pair colorization (disabled by default). */ bracketPairColorization?: IBracketPairColorizationOptions; + + /** + * Enables dropping into the editor from an external source. + * + * This shows a preview of the drop location and triggers an `onDropIntoEditor` event. + */ + enableDropIntoEditor?: boolean; } /** @@ -4431,6 +4438,7 @@ export const enum EditorOption { disableMonospaceOptimizations, domReadOnly, dragAndDrop, + enableDropIntoEditor, emptySelectionClipboard, extraEditorClassName, fastScrollSensitivity, @@ -4739,6 +4747,9 @@ export const EditorOptions = { { description: nls.localize('dragAndDrop', "Controls whether the editor should allow moving selections via drag and drop.") } )), emptySelectionClipboard: register(new EditorEmptySelectionClipboard()), + enableDropIntoEditor: register(new EditorBooleanOption( + EditorOption.enableDropIntoEditor, 'enableDropIntoEditor', true + )), extraEditorClassName: register(new EditorStringOption( EditorOption.extraEditorClassName, 'extraEditorClassName', '', )), diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 74306ee05d987..b5c7361ce88ad 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -4,11 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; +import { Codicon, CSSIcon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; +import { IDataTransfer } from 'vs/base/common/dataTransfer'; import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; +import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -16,10 +19,7 @@ import * as model from 'vs/editor/common/model'; import { TokenizationRegistry as TokenizationRegistryImpl } from 'vs/editor/common/tokenizationRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IMarkerData } from 'vs/platform/markers/common/markers'; -import { Codicon, CSSIcon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; -import { IDataTransfer } from 'vs/editor/common/dnd'; /** * Open ended enum at runtime diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 265cb5430543f..8d709286d5ccb 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -199,107 +199,108 @@ export enum EditorOption { disableMonospaceOptimizations = 29, domReadOnly = 30, dragAndDrop = 31, - emptySelectionClipboard = 32, - extraEditorClassName = 33, - fastScrollSensitivity = 34, - find = 35, - fixedOverflowWidgets = 36, - folding = 37, - foldingStrategy = 38, - foldingHighlight = 39, - foldingImportsByDefault = 40, - foldingMaximumRegions = 41, - unfoldOnClickAfterEndOfLine = 42, - fontFamily = 43, - fontInfo = 44, - fontLigatures = 45, - fontSize = 46, - fontWeight = 47, - formatOnPaste = 48, - formatOnType = 49, - glyphMargin = 50, - gotoLocation = 51, - hideCursorInOverviewRuler = 52, - hover = 53, - inDiffEditor = 54, - inlineSuggest = 55, - letterSpacing = 56, - lightbulb = 57, - lineDecorationsWidth = 58, - lineHeight = 59, - lineNumbers = 60, - lineNumbersMinChars = 61, - linkedEditing = 62, - links = 63, - matchBrackets = 64, - minimap = 65, - mouseStyle = 66, - mouseWheelScrollSensitivity = 67, - mouseWheelZoom = 68, - multiCursorMergeOverlapping = 69, - multiCursorModifier = 70, - multiCursorPaste = 71, - occurrencesHighlight = 72, - overviewRulerBorder = 73, - overviewRulerLanes = 74, - padding = 75, - parameterHints = 76, - peekWidgetDefaultFocus = 77, - definitionLinkOpensInPeek = 78, - quickSuggestions = 79, - quickSuggestionsDelay = 80, - readOnly = 81, - renameOnType = 82, - renderControlCharacters = 83, - renderFinalNewline = 84, - renderLineHighlight = 85, - renderLineHighlightOnlyWhenFocus = 86, - renderValidationDecorations = 87, - renderWhitespace = 88, - revealHorizontalRightPadding = 89, - roundedSelection = 90, - rulers = 91, - scrollbar = 92, - scrollBeyondLastColumn = 93, - scrollBeyondLastLine = 94, - scrollPredominantAxis = 95, - selectionClipboard = 96, - selectionHighlight = 97, - selectOnLineNumbers = 98, - showFoldingControls = 99, - showUnused = 100, - snippetSuggestions = 101, - smartSelect = 102, - smoothScrolling = 103, - stickyTabStops = 104, - stopRenderingLineAfter = 105, - suggest = 106, - suggestFontSize = 107, - suggestLineHeight = 108, - suggestOnTriggerCharacters = 109, - suggestSelection = 110, - tabCompletion = 111, - tabIndex = 112, - unicodeHighlighting = 113, - unusualLineTerminators = 114, - useShadowDOM = 115, - useTabStops = 116, - wordSeparators = 117, - wordWrap = 118, - wordWrapBreakAfterCharacters = 119, - wordWrapBreakBeforeCharacters = 120, - wordWrapColumn = 121, - wordWrapOverride1 = 122, - wordWrapOverride2 = 123, - wrappingIndent = 124, - wrappingStrategy = 125, - showDeprecated = 126, - inlayHints = 127, - editorClassName = 128, - pixelRatio = 129, - tabFocusMode = 130, - layoutInfo = 131, - wrappingInfo = 132 + enableDropIntoEditor = 32, + emptySelectionClipboard = 33, + extraEditorClassName = 34, + fastScrollSensitivity = 35, + find = 36, + fixedOverflowWidgets = 37, + folding = 38, + foldingStrategy = 39, + foldingHighlight = 40, + foldingImportsByDefault = 41, + foldingMaximumRegions = 42, + unfoldOnClickAfterEndOfLine = 43, + fontFamily = 44, + fontInfo = 45, + fontLigatures = 46, + fontSize = 47, + fontWeight = 48, + formatOnPaste = 49, + formatOnType = 50, + glyphMargin = 51, + gotoLocation = 52, + hideCursorInOverviewRuler = 53, + hover = 54, + inDiffEditor = 55, + inlineSuggest = 56, + letterSpacing = 57, + lightbulb = 58, + lineDecorationsWidth = 59, + lineHeight = 60, + lineNumbers = 61, + lineNumbersMinChars = 62, + linkedEditing = 63, + links = 64, + matchBrackets = 65, + minimap = 66, + mouseStyle = 67, + mouseWheelScrollSensitivity = 68, + mouseWheelZoom = 69, + multiCursorMergeOverlapping = 70, + multiCursorModifier = 71, + multiCursorPaste = 72, + occurrencesHighlight = 73, + overviewRulerBorder = 74, + overviewRulerLanes = 75, + padding = 76, + parameterHints = 77, + peekWidgetDefaultFocus = 78, + definitionLinkOpensInPeek = 79, + quickSuggestions = 80, + quickSuggestionsDelay = 81, + readOnly = 82, + renameOnType = 83, + renderControlCharacters = 84, + renderFinalNewline = 85, + renderLineHighlight = 86, + renderLineHighlightOnlyWhenFocus = 87, + renderValidationDecorations = 88, + renderWhitespace = 89, + revealHorizontalRightPadding = 90, + roundedSelection = 91, + rulers = 92, + scrollbar = 93, + scrollBeyondLastColumn = 94, + scrollBeyondLastLine = 95, + scrollPredominantAxis = 96, + selectionClipboard = 97, + selectionHighlight = 98, + selectOnLineNumbers = 99, + showFoldingControls = 100, + showUnused = 101, + snippetSuggestions = 102, + smartSelect = 103, + smoothScrolling = 104, + stickyTabStops = 105, + stopRenderingLineAfter = 106, + suggest = 107, + suggestFontSize = 108, + suggestLineHeight = 109, + suggestOnTriggerCharacters = 110, + suggestSelection = 111, + tabCompletion = 112, + tabIndex = 113, + unicodeHighlighting = 114, + unusualLineTerminators = 115, + useShadowDOM = 116, + useTabStops = 117, + wordSeparators = 118, + wordWrap = 119, + wordWrapBreakAfterCharacters = 120, + wordWrapBreakBeforeCharacters = 121, + wordWrapColumn = 122, + wordWrapOverride1 = 123, + wordWrapOverride2 = 124, + wrappingIndent = 125, + wrappingStrategy = 126, + showDeprecated = 127, + inlayHints = 128, + editorClassName = 129, + pixelRatio = 130, + tabFocusMode = 131, + layoutInfo = 132, + wrappingInfo = 133 } /** diff --git a/src/vs/workbench/contrib/dropIntoEditor/browser/dropIntoEditor.contibution.ts b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts similarity index 62% rename from src/vs/workbench/contrib/dropIntoEditor/browser/dropIntoEditor.contibution.ts rename to src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts index def227ff8b3db..d113f9e513c45 100644 --- a/src/vs/workbench/contrib/dropIntoEditor/browser/dropIntoEditor.contibution.ts +++ b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts @@ -5,6 +5,7 @@ import { distinct } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IDataTransfer, IDataTransferItem } from 'vs/base/common/dataTransfer'; import { Disposable } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import { relativePath } from 'vs/base/common/resources'; @@ -13,13 +14,15 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IPosition } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { IDataTransfer, IDataTransferItem } from 'vs/editor/common/dnd'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { DocumentOnDropEditProvider, SnippetTextEdit } from 'vs/editor/common/languages'; +import { ITextModel } from 'vs/editor/common/model'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { performSnippetEdit } from 'vs/editor/contrib/snippet/browser/snippetController2'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { extractEditorsDropData, FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { extractEditorsDropData } from 'vs/workbench/browser/dnd'; export class DropIntoEditorController extends Disposable implements IEditorContribution { @@ -28,13 +31,31 @@ export class DropIntoEditorController extends Disposable implements IEditorContr constructor( editor: ICodeEditor, + @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, - @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, + @IConfigurationService private readonly _configurationService: IConfigurationService, ) { super(); - editor.onDropIntoEditor(e => this.onDropIntoEditor(editor, e.position, e.event)); + this._register(editor.onDropIntoEditor(e => this.onDropIntoEditor(editor, e.position, e.event))); + + + this._languageFeaturesService.documentOnDropEditProvider.register('*', new DefaultOnDropProvider(workspaceContextService)); + + this._register(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('workbench.experimental.editor.dropIntoEditor.enabled')) { + this.updateEditorOptions(editor); + } + })); + + this.updateEditorOptions(editor); + } + + private updateEditorOptions(editor: ICodeEditor) { + editor.updateOptions({ + enableDropIntoEditor: this._configurationService.getValue('workbench.experimental.editor.dropIntoEditor.enabled') + }); } private async onDropIntoEditor(editor: ICodeEditor, position: IPosition, dragEvent: DragEvent) { @@ -45,7 +66,39 @@ export class DropIntoEditorController extends Disposable implements IEditorContr const model = editor.getModel(); const modelVersionNow = model.getVersionId(); + const ourDataTransfer = await this.extractDataTransferData(dragEvent); + if (ourDataTransfer.size === 0) { + return; + } + + if (editor.getModel().getVersionId() !== modelVersionNow) { + return; + } + + const cts = new CancellationTokenSource(); + editor.onDidDispose(() => cts.cancel()); + model.onDidChangeContent(() => cts.cancel()); + + const providers = this._languageFeaturesService.documentOnDropEditProvider.ordered(model); + for (const provider of providers) { + const edit = await provider.provideDocumentOnDropEdits(model, position, ourDataTransfer, cts.token); + if (cts.token.isCancellationRequested || editor.getModel().getVersionId() !== modelVersionNow) { + return; + } + + if (edit) { + performSnippetEdit(editor, edit); + return; + } + } + } + + public async extractDataTransferData(dragEvent: DragEvent): Promise { const textEditorDataTransfer: IDataTransfer = new Map(); + if (!dragEvent.dataTransfer) { + return textEditorDataTransfer; + } + for (const item of dragEvent.dataTransfer.items) { const type = item.type; if (item.kind === 'string') { @@ -61,7 +114,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr textEditorDataTransfer.set(type, { asString: () => Promise.resolve(''), asFile: () => { - const uri = file.path ? URI.parse(file.path) : undefined; + const uri = (file as FileAdditionalNativeProperties).path ? URI.parse((file as FileAdditionalNativeProperties).path!) : undefined; return { name: file.name, uri: uri, @@ -76,66 +129,52 @@ export class DropIntoEditorController extends Disposable implements IEditorContr } } - if (!textEditorDataTransfer.has(Mimes.uriList.toLowerCase())) { + if (!textEditorDataTransfer.has(Mimes.uriList)) { const editorData = (await this._instantiationService.invokeFunction(extractEditorsDropData, dragEvent)) .filter(input => input.resource) .map(input => input.resource!.toString()); if (editorData.length) { + const added: IDataTransfer = new Map(); + const str = distinct(editorData).join('\n'); - textEditorDataTransfer.set(Mimes.uriList.toLowerCase(), { - asString: () => Promise.resolve(str), + added.set(Mimes.uriList.toLowerCase(), { asFile: () => undefined, - value: undefined + asString: async () => str, + value: str, }); + return added; } } - if (textEditorDataTransfer.size === 0) { - return; - } - - if (editor.getModel().getVersionId() !== modelVersionNow) { - return; - } - - const cts = new CancellationTokenSource(); - editor.onDidDispose(() => cts.cancel()); - model.onDidChangeContent(() => cts.cancel()); - - const ordered = this._languageFeaturesService.documentOnDropEditProvider.ordered(model); - for (const provider of ordered) { - const edit = await provider.provideDocumentOnDropEdits(model, position, textEditorDataTransfer, cts.token); - if (cts.token.isCancellationRequested || editor.getModel().getVersionId() !== modelVersionNow) { - return; - } + return textEditorDataTransfer; + } +} - if (edit) { - performSnippetEdit(editor, edit); - return; - } - } +class DefaultOnDropProvider implements DocumentOnDropEditProvider { - return this.doDefaultDrop(editor, position, textEditorDataTransfer, cts.token); - } + constructor( + @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, + ) { } - private async doDefaultDrop(editor: ICodeEditor, position: IPosition, textEditorDataTransfer: IDataTransfer, token: CancellationToken): Promise { + async provideDocumentOnDropEdits(model: ITextModel, position: IPosition, dataTransfer: IDataTransfer, _token: CancellationToken): Promise { const range = new Range(position.lineNumber, position.column, position.lineNumber, position.column); - const urlListEntry = textEditorDataTransfer.get('text/uri-list'); + const urlListEntry = dataTransfer.get('text/uri-list'); if (urlListEntry) { const urlList = await urlListEntry.asString(); - return this.doUriListDrop(editor, range, urlList, token); + return this.doUriListDrop(range, urlList); } - const textEntry = textEditorDataTransfer.get('text') ?? textEditorDataTransfer.get(Mimes.text); + const textEntry = dataTransfer.get('text') ?? dataTransfer.get(Mimes.text); if (textEntry) { const text = await textEntry.asString(); - performSnippetEdit(editor, { range, snippet: text }); + return { range, snippet: text }; } + return undefined; } - private async doUriListDrop(editor: ICodeEditor, range: Range, urlList: string, token: CancellationToken): Promise { + private doUriListDrop(range: Range, urlList: string): SnippetTextEdit | undefined { const uris: URI[] = []; for (const resource of urlList.split('\n')) { try { @@ -162,7 +201,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr }) .join(' '); - performSnippetEdit(editor, { range, snippet }); + return { range, snippet }; } } diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index fdd3f3f8d7fe1..aba1e89aa1db6 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -19,6 +19,7 @@ import 'vs/editor/contrib/comment/browser/comment'; import 'vs/editor/contrib/contextmenu/browser/contextmenu'; import 'vs/editor/contrib/cursorUndo/browser/cursorUndo'; import 'vs/editor/contrib/dnd/browser/dnd'; +import 'vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution'; import 'vs/editor/contrib/find/browser/findController'; import 'vs/editor/contrib/folding/browser/folding'; import 'vs/editor/contrib/fontZoom/browser/fontZoom'; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 8390e8adca5bb..cba1ad5adc5a0 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3413,6 +3413,12 @@ declare namespace monaco.editor { * Configures bracket pair colorization (disabled by default). */ bracketPairColorization?: IBracketPairColorizationOptions; + /** + * Enables dropping into the editor from an external source. + * + * This shows a preview of the drop location and triggers an `onDropIntoEditor` event. + */ + enableDropIntoEditor?: boolean; } export interface IDiffEditorBaseOptions { @@ -4314,107 +4320,108 @@ declare namespace monaco.editor { disableMonospaceOptimizations = 29, domReadOnly = 30, dragAndDrop = 31, - emptySelectionClipboard = 32, - extraEditorClassName = 33, - fastScrollSensitivity = 34, - find = 35, - fixedOverflowWidgets = 36, - folding = 37, - foldingStrategy = 38, - foldingHighlight = 39, - foldingImportsByDefault = 40, - foldingMaximumRegions = 41, - unfoldOnClickAfterEndOfLine = 42, - fontFamily = 43, - fontInfo = 44, - fontLigatures = 45, - fontSize = 46, - fontWeight = 47, - formatOnPaste = 48, - formatOnType = 49, - glyphMargin = 50, - gotoLocation = 51, - hideCursorInOverviewRuler = 52, - hover = 53, - inDiffEditor = 54, - inlineSuggest = 55, - letterSpacing = 56, - lightbulb = 57, - lineDecorationsWidth = 58, - lineHeight = 59, - lineNumbers = 60, - lineNumbersMinChars = 61, - linkedEditing = 62, - links = 63, - matchBrackets = 64, - minimap = 65, - mouseStyle = 66, - mouseWheelScrollSensitivity = 67, - mouseWheelZoom = 68, - multiCursorMergeOverlapping = 69, - multiCursorModifier = 70, - multiCursorPaste = 71, - occurrencesHighlight = 72, - overviewRulerBorder = 73, - overviewRulerLanes = 74, - padding = 75, - parameterHints = 76, - peekWidgetDefaultFocus = 77, - definitionLinkOpensInPeek = 78, - quickSuggestions = 79, - quickSuggestionsDelay = 80, - readOnly = 81, - renameOnType = 82, - renderControlCharacters = 83, - renderFinalNewline = 84, - renderLineHighlight = 85, - renderLineHighlightOnlyWhenFocus = 86, - renderValidationDecorations = 87, - renderWhitespace = 88, - revealHorizontalRightPadding = 89, - roundedSelection = 90, - rulers = 91, - scrollbar = 92, - scrollBeyondLastColumn = 93, - scrollBeyondLastLine = 94, - scrollPredominantAxis = 95, - selectionClipboard = 96, - selectionHighlight = 97, - selectOnLineNumbers = 98, - showFoldingControls = 99, - showUnused = 100, - snippetSuggestions = 101, - smartSelect = 102, - smoothScrolling = 103, - stickyTabStops = 104, - stopRenderingLineAfter = 105, - suggest = 106, - suggestFontSize = 107, - suggestLineHeight = 108, - suggestOnTriggerCharacters = 109, - suggestSelection = 110, - tabCompletion = 111, - tabIndex = 112, - unicodeHighlighting = 113, - unusualLineTerminators = 114, - useShadowDOM = 115, - useTabStops = 116, - wordSeparators = 117, - wordWrap = 118, - wordWrapBreakAfterCharacters = 119, - wordWrapBreakBeforeCharacters = 120, - wordWrapColumn = 121, - wordWrapOverride1 = 122, - wordWrapOverride2 = 123, - wrappingIndent = 124, - wrappingStrategy = 125, - showDeprecated = 126, - inlayHints = 127, - editorClassName = 128, - pixelRatio = 129, - tabFocusMode = 130, - layoutInfo = 131, - wrappingInfo = 132 + enableDropIntoEditor = 32, + emptySelectionClipboard = 33, + extraEditorClassName = 34, + fastScrollSensitivity = 35, + find = 36, + fixedOverflowWidgets = 37, + folding = 38, + foldingStrategy = 39, + foldingHighlight = 40, + foldingImportsByDefault = 41, + foldingMaximumRegions = 42, + unfoldOnClickAfterEndOfLine = 43, + fontFamily = 44, + fontInfo = 45, + fontLigatures = 46, + fontSize = 47, + fontWeight = 48, + formatOnPaste = 49, + formatOnType = 50, + glyphMargin = 51, + gotoLocation = 52, + hideCursorInOverviewRuler = 53, + hover = 54, + inDiffEditor = 55, + inlineSuggest = 56, + letterSpacing = 57, + lightbulb = 58, + lineDecorationsWidth = 59, + lineHeight = 60, + lineNumbers = 61, + lineNumbersMinChars = 62, + linkedEditing = 63, + links = 64, + matchBrackets = 65, + minimap = 66, + mouseStyle = 67, + mouseWheelScrollSensitivity = 68, + mouseWheelZoom = 69, + multiCursorMergeOverlapping = 70, + multiCursorModifier = 71, + multiCursorPaste = 72, + occurrencesHighlight = 73, + overviewRulerBorder = 74, + overviewRulerLanes = 75, + padding = 76, + parameterHints = 77, + peekWidgetDefaultFocus = 78, + definitionLinkOpensInPeek = 79, + quickSuggestions = 80, + quickSuggestionsDelay = 81, + readOnly = 82, + renameOnType = 83, + renderControlCharacters = 84, + renderFinalNewline = 85, + renderLineHighlight = 86, + renderLineHighlightOnlyWhenFocus = 87, + renderValidationDecorations = 88, + renderWhitespace = 89, + revealHorizontalRightPadding = 90, + roundedSelection = 91, + rulers = 92, + scrollbar = 93, + scrollBeyondLastColumn = 94, + scrollBeyondLastLine = 95, + scrollPredominantAxis = 96, + selectionClipboard = 97, + selectionHighlight = 98, + selectOnLineNumbers = 99, + showFoldingControls = 100, + showUnused = 101, + snippetSuggestions = 102, + smartSelect = 103, + smoothScrolling = 104, + stickyTabStops = 105, + stopRenderingLineAfter = 106, + suggest = 107, + suggestFontSize = 108, + suggestLineHeight = 109, + suggestOnTriggerCharacters = 110, + suggestSelection = 111, + tabCompletion = 112, + tabIndex = 113, + unicodeHighlighting = 114, + unusualLineTerminators = 115, + useShadowDOM = 116, + useTabStops = 117, + wordSeparators = 118, + wordWrap = 119, + wordWrapBreakAfterCharacters = 120, + wordWrapBreakBeforeCharacters = 121, + wordWrapColumn = 122, + wordWrapOverride1 = 123, + wordWrapOverride2 = 124, + wrappingIndent = 125, + wrappingStrategy = 126, + showDeprecated = 127, + inlayHints = 128, + editorClassName = 129, + pixelRatio = 130, + tabFocusMode = 131, + layoutInfo = 132, + wrappingInfo = 133 } export const EditorOptions: { @@ -4452,6 +4459,7 @@ declare namespace monaco.editor { domReadOnly: IEditorOption; dragAndDrop: IEditorOption; emptySelectionClipboard: IEditorOption; + enableDropIntoEditor: IEditorOption; extraEditorClassName: IEditorOption; fastScrollSensitivity: IEditorOption; find: IEditorOption>>; @@ -4573,12 +4581,6 @@ declare namespace monaco.editor { * Defaults to an internal DOM node. */ overflowWidgetsDomNode?: HTMLElement; - /** - * Enables dropping into the editor. - * - * This shows a preview of the drop location and triggers an `onDropIntoEditor` event. - */ - enableDropIntoEditor?: boolean; } /** diff --git a/src/vs/platform/dnd/browser/dnd.ts b/src/vs/platform/dnd/browser/dnd.ts new file mode 100644 index 0000000000000..14c6c8d11d322 --- /dev/null +++ b/src/vs/platform/dnd/browser/dnd.ts @@ -0,0 +1,339 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DataTransfers } from 'vs/base/browser/dnd'; +import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; +import { coalesce } from 'vs/base/common/arrays'; +import { DeferredPromise } from 'vs/base/common/async'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { parse } from 'vs/base/common/marshalling'; +import { Schemas } from 'vs/base/common/network'; +import { isWeb } from 'vs/base/common/platform'; +import Severity from 'vs/base/common/severity'; +import { withNullAsUndefined } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IBaseTextResourceEditorInput } from 'vs/platform/editor/common/editor'; +import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystemProvider'; +import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; +import { ByteSize, IFileService } from 'vs/platform/files/common/files'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { extractSelection } from 'vs/platform/opener/common/opener'; +import { Registry } from 'vs/platform/registry/common/platform'; + +export interface FileAdditionalNativeProperties { + /** + * The real path to the file on the users filesystem. Only available on electron. + */ + readonly path?: string; +} + + +//#region Editor / Resources DND + +export const CodeDataTransfers = { + EDITORS: 'CodeEditors', + FILES: 'CodeFiles' +}; + +export interface IDraggedResourceEditorInput extends IBaseTextResourceEditorInput { + resource: URI | undefined; + + /** + * A hint that the source of the dragged editor input + * might not be the application but some external tool. + */ + isExternal?: boolean; + + /** + * Whether we probe for the dropped editor to be a workspace + * (i.e. code-workspace file or even a folder), allowing to + * open it as workspace instead of opening as editor. + */ + allowWorkspaceOpen?: boolean; +} + +export async function extractEditorsDropData(accessor: ServicesAccessor, e: DragEvent): Promise> { + const editors: IDraggedResourceEditorInput[] = []; + if (e.dataTransfer && e.dataTransfer.types.length > 0) { + + // Data Transfer: Code Editors + const rawEditorsData = e.dataTransfer.getData(CodeDataTransfers.EDITORS); + if (rawEditorsData) { + try { + editors.push(...parse(rawEditorsData)); + } catch (error) { + // Invalid transfer + } + } + + // Data Transfer: Resources + else { + try { + const rawResourcesData = e.dataTransfer.getData(DataTransfers.RESOURCES); + editors.push(...createDraggedEditorInputFromRawResourcesData(rawResourcesData)); + } catch (error) { + // Invalid transfer + } + } + + // Check for native file transfer + if (e.dataTransfer?.files) { + for (let i = 0; i < e.dataTransfer.files.length; i++) { + const file = e.dataTransfer.files[i]; + if (file && (file as FileAdditionalNativeProperties).path /* Electron only */) { + try { + editors.push({ resource: URI.file((file as FileAdditionalNativeProperties).path!), isExternal: true, allowWorkspaceOpen: true }); + } catch (error) { + // Invalid URI + } + } + } + } + + // Check for CodeFiles transfer + const rawCodeFiles = e.dataTransfer.getData(CodeDataTransfers.FILES); + if (rawCodeFiles) { + try { + const codeFiles: string[] = JSON.parse(rawCodeFiles); + for (const codeFile of codeFiles) { + editors.push({ resource: URI.file(codeFile), isExternal: true, allowWorkspaceOpen: true }); + } + } catch (error) { + // Invalid transfer + } + } + + // Web: Check for file transfer + if (isWeb && containsDragType(e, DataTransfers.FILES)) { + const files = e.dataTransfer.items; + if (files) { + const instantiationService = accessor.get(IInstantiationService); + const filesData = await instantiationService.invokeFunction(accessor => extractFilesDropData(accessor, e)); + for (const fileData of filesData) { + editors.push({ resource: fileData.resource, contents: fileData.contents?.toString(), isExternal: true, allowWorkspaceOpen: fileData.isDirectory }); + } + } + } + + // Workbench contributions + const contributions = Registry.as(Extensions.DragAndDropContribution).getAll(); + for (const contribution of contributions) { + const data = e.dataTransfer.getData(contribution.dataFormatKey); + if (data) { + try { + editors.push(...contribution.getEditorInputs(data)); + } catch (error) { + // Invalid transfer + } + } + } + } + + return editors; +} + +export function createDraggedEditorInputFromRawResourcesData(rawResourcesData: string | undefined): IDraggedResourceEditorInput[] { + const editors: IDraggedResourceEditorInput[] = []; + + if (rawResourcesData) { + const resourcesRaw: string[] = JSON.parse(rawResourcesData); + for (const resourceRaw of resourcesRaw) { + if (resourceRaw.indexOf(':') > 0) { // mitigate https://github.com/microsoft/vscode/issues/124946 + const { selection, uri } = extractSelection(URI.parse(resourceRaw)); + editors.push({ resource: uri, options: { selection } }); + } + } + } + + return editors; +} + + +interface IFileTransferData { + resource: URI; + isDirectory?: boolean; + contents?: VSBuffer; +} + +async function extractFilesDropData(accessor: ServicesAccessor, event: DragEvent): Promise { + + // Try to extract via `FileSystemHandle` + if (WebFileSystemAccess.supported(window)) { + const items = event.dataTransfer?.items; + if (items) { + return extractFileTransferData(accessor, items); + } + } + + // Try to extract via `FileList` + const files = event.dataTransfer?.files; + if (!files) { + return []; + } + + return extractFileListData(accessor, files); +} + +async function extractFileTransferData(accessor: ServicesAccessor, items: DataTransferItemList): Promise { + const fileSystemProvider = accessor.get(IFileService).getProvider(Schemas.file); + if (!(fileSystemProvider instanceof HTMLFileSystemProvider)) { + return []; // only supported when running in web + } + + const results: DeferredPromise[] = []; + + for (let i = 0; i < items.length; i++) { + const file = items[i]; + if (file) { + const result = new DeferredPromise(); + results.push(result); + + (async () => { + try { + const handle = await file.getAsFileSystemHandle(); + if (!handle) { + result.complete(undefined); + return; + } + + if (WebFileSystemAccess.isFileSystemFileHandle(handle)) { + result.complete({ + resource: await fileSystemProvider.registerFileHandle(handle), + isDirectory: false + }); + } else if (WebFileSystemAccess.isFileSystemDirectoryHandle(handle)) { + result.complete({ + resource: await fileSystemProvider.registerDirectoryHandle(handle), + isDirectory: true + }); + } else { + result.complete(undefined); + } + } catch (error) { + result.complete(undefined); + } + })(); + } + } + + return coalesce(await Promise.all(results.map(result => result.p))); +} + +export async function extractFileListData(accessor: ServicesAccessor, files: FileList): Promise { + const dialogService = accessor.get(IDialogService); + + const results: DeferredPromise[] = []; + + for (let i = 0; i < files.length; i++) { + const file = files.item(i); + if (file) { + + // Skip for very large files because this operation is unbuffered + if (file.size > 100 * ByteSize.MB) { + dialogService.show(Severity.Warning, localize('fileTooLarge', "File is too large to open as untitled editor. Please upload it first into the file explorer and then try again.")); + continue; + } + + const result = new DeferredPromise(); + results.push(result); + + const reader = new FileReader(); + + reader.onerror = () => result.complete(undefined); + reader.onabort = () => result.complete(undefined); + + reader.onload = async event => { + const name = file.name; + const loadResult = withNullAsUndefined(event.target?.result); + if (typeof name !== 'string' || typeof loadResult === 'undefined') { + result.complete(undefined); + return; + } + + result.complete({ + resource: URI.from({ scheme: Schemas.untitled, path: name }), + contents: typeof loadResult === 'string' ? VSBuffer.fromString(loadResult) : VSBuffer.wrap(new Uint8Array(loadResult)) + }); + }; + + // Start reading + reader.readAsArrayBuffer(file); + } + } + + return coalesce(await Promise.all(results.map(result => result.p))); +} + +//#endregion + +export function containsDragType(event: DragEvent, ...dragTypesToFind: string[]): boolean { + if (!event.dataTransfer) { + return false; + } + + const dragTypes = event.dataTransfer.types; + const lowercaseDragTypes: string[] = []; + for (let i = 0; i < dragTypes.length; i++) { + lowercaseDragTypes.push(dragTypes[i].toLowerCase()); // somehow the types are lowercase + } + + for (const dragType of dragTypesToFind) { + if (lowercaseDragTypes.indexOf(dragType.toLowerCase()) >= 0) { + return true; + } + } + + return false; +} + +//#region DND contributions + +export interface IResourceStat { + resource: URI; + isDirectory?: boolean; +} + +export interface IDragAndDropContributionRegistry { + /** + * Registers a drag and drop contribution. + */ + register(contribution: IDragAndDropContribution): void; + + /** + * Returns all registered drag and drop contributions. + */ + getAll(): IterableIterator; +} + +export interface IDragAndDropContribution { + readonly dataFormatKey: string; + getEditorInputs(data: string): IDraggedResourceEditorInput[]; + setData(resources: IResourceStat[], event: DragMouseEvent | DragEvent): void; +} + +class DragAndDropContributionRegistry implements IDragAndDropContributionRegistry { + private readonly _contributions = new Map(); + + register(contribution: IDragAndDropContribution): void { + if (this._contributions.has(contribution.dataFormatKey)) { + throw new Error(`A drag and drop contributiont with key '${contribution.dataFormatKey}' was already registered.`); + } + this._contributions.set(contribution.dataFormatKey, contribution); + } + + getAll(): IterableIterator { + return this._contributions.values(); + } +} + +export const Extensions = { + DragAndDropContribution: 'workbench.contributions.dragAndDrop' +}; + +Registry.add(Extensions.DragAndDropContribution, new DragAndDropContributionRegistry()); + +//#endregion diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 07d04a05c60c4..16fada27b5b4a 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -30,7 +30,7 @@ import * as search from 'vs/workbench/contrib/search/common/search'; import * as typeh from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { ExtHostContext, ExtHostLanguageFeaturesShape, ICallHierarchyItemDto, ICodeActionDto, ICodeActionProviderMetadataDto, IdentifiableInlineCompletion, IdentifiableInlineCompletions, IDocumentFilterDto, IIndentationRuleDto, IInlayHintDto, ILanguageConfigurationDto, ILanguageWordDefinitionDto, ILinkDto, ILocationDto, ILocationLinkDto, IOnEnterRuleDto, IRegExpDto, ISignatureHelpProviderMetadataDto, ISuggestDataDto, ISuggestDataDtoField, ISuggestResultDtoField, ITypeHierarchyItemDto, IWorkspaceSymbolDto, MainContext, MainThreadLanguageFeaturesShape, reviveWorkspaceEditDto } from '../common/extHost.protocol'; -import { IDataTransfer } from 'vs/editor/common/dnd'; +import { IDataTransfer } from 'vs/base/common/dataTransfer'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) export class MainThreadLanguageFeatures extends Disposable implements MainThreadLanguageFeaturesShape { diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 54e79e9918cd3..6e96ff8161fc6 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -15,7 +15,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { ILogService } from 'vs/platform/log/common/log'; import { DataTransferConverter } from 'vs/workbench/api/common/shared/dataTransfer'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IDataTransfer } from 'vs/editor/common/dnd'; +import { IDataTransfer } from 'vs/base/common/dataTransfer'; import { VSBuffer } from 'vs/base/common/buffer'; import { DataTransferCache } from 'vs/workbench/api/common/shared/dataTransferCache'; diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 960b8d39a9ec9..b984ae6569e8d 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -25,7 +25,7 @@ import { Command } from 'vs/editor/common/languages'; import { DataTransferConverter, DataTransferDTO } from 'vs/workbench/api/common/shared/dataTransfer'; import { ITreeViewsService, TreeviewsService } from 'vs/workbench/services/views/common/treeViewsService'; import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; -import { IDataTransfer } from 'vs/editor/common/dnd'; +import { IDataTransfer } from 'vs/base/common/dataTransfer'; type TreeItemHandle = string; diff --git a/src/vs/workbench/api/common/shared/dataTransfer.ts b/src/vs/workbench/api/common/shared/dataTransfer.ts index dd4749b607be8..a16dfe65f963a 100644 --- a/src/vs/workbench/api/common/shared/dataTransfer.ts +++ b/src/vs/workbench/api/common/shared/dataTransfer.ts @@ -5,7 +5,7 @@ import { once } from 'vs/base/common/functional'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IDataTransfer, IDataTransferItem } from 'vs/editor/common/dnd'; +import { IDataTransfer, IDataTransferItem } from 'vs/base/common/dataTransfer'; export interface IDataTransferFileDTO { readonly name: string; diff --git a/src/vs/workbench/api/common/shared/dataTransferCache.ts b/src/vs/workbench/api/common/shared/dataTransferCache.ts index c3e845c61d419..02184d68d18b6 100644 --- a/src/vs/workbench/api/common/shared/dataTransferCache.ts +++ b/src/vs/workbench/api/common/shared/dataTransferCache.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { VSBuffer } from 'vs/base/common/buffer'; -import { IDataTransfer, IDataTransferItem } from 'vs/editor/common/dnd'; +import { IDataTransfer, IDataTransferItem } from 'vs/base/common/dataTransfer'; export class DataTransferCache { diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index b1f9001e03cbc..63cfe74d6deec 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -3,45 +3,37 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { VSBuffer } from 'vs/base/common/buffer'; -import Severity from 'vs/base/common/severity'; -import { IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; -import { basename, isEqual } from 'vs/base/common/resources'; -import { ByteSize, IFileService } from 'vs/platform/files/common/files'; -import { IWindowOpenable } from 'vs/platform/window/common/window'; -import { URI } from 'vs/base/common/uri'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { FileAccess, Schemas } from 'vs/base/common/network'; -import { IBaseTextResourceEditorInput } from 'vs/platform/editor/common/editor'; import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd'; +import { addDisposableListener, DragAndDropObserver, EventType } from 'vs/base/browser/dom'; import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IListDragAndDrop } from 'vs/base/browser/ui/list/list'; +import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree'; +import { coalesce } from 'vs/base/common/arrays'; +import { IDataTransfer } from 'vs/base/common/dataTransfer'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { stringify } from 'vs/base/common/marshalling'; import { Mimes } from 'vs/base/common/mime'; -import { isWeb, isWindows } from 'vs/base/common/platform'; +import { FileAccess, Schemas } from 'vs/base/common/network'; +import { isWindows } from 'vs/base/common/platform'; +import { basename, isEqual } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import { CodeDataTransfers, createDraggedEditorInputFromRawResourcesData, Extensions, extractEditorsDropData, IDragAndDropContributionRegistry, IDraggedResourceEditorInput, IResourceStat } from 'vs/platform/dnd/browser/dnd'; +import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IEditorIdentifier, GroupIdentifier, isEditorIdentifier, EditorResourceAccessor } from 'vs/workbench/common/editor'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { addDisposableListener, DragAndDropObserver, EventType } from 'vs/base/browser/dom'; -import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { Emitter } from 'vs/base/common/event'; -import { coalesce } from 'vs/base/common/arrays'; -import { parse, stringify } from 'vs/base/common/marshalling'; import { ILabelService } from 'vs/platform/label/common/label'; -import { hasWorkspaceFileExtension, isTemporaryWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { withNullAsUndefined } from 'vs/base/common/types'; -import { IDataTransfer } from 'vs/editor/common/dnd'; import { extractSelection } from 'vs/platform/opener/common/opener'; -import { IListDragAndDrop } from 'vs/base/browser/ui/list/list'; -import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; -import { ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree'; -import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; -import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystemProvider'; -import { DeferredPromise } from 'vs/base/common/async'; import { Registry } from 'vs/platform/registry/common/platform'; +import { IWindowOpenable } from 'vs/platform/window/common/window'; +import { hasWorkspaceFileExtension, isTemporaryWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; +import { EditorResourceAccessor, GroupIdentifier, IEditorIdentifier, isEditorIdentifier } from 'vs/workbench/common/editor'; +import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; //#region Editor / Resources DND @@ -60,124 +52,6 @@ export class DraggedTreeItemsIdentifier { constructor(readonly identifier: string) { } } -export const CodeDataTransfers = { - EDITORS: 'CodeEditors', - FILES: 'CodeFiles' -}; - -export interface IDraggedResourceEditorInput extends IBaseTextResourceEditorInput { - resource: URI | undefined; - - /** - * A hint that the source of the dragged editor input - * might not be the application but some external tool. - */ - isExternal?: boolean; - - /** - * Whether we probe for the dropped editor to be a workspace - * (i.e. code-workspace file or even a folder), allowing to - * open it as workspace instead of opening as editor. - */ - allowWorkspaceOpen?: boolean; -} - -export async function extractEditorsDropData(accessor: ServicesAccessor, e: DragEvent): Promise> { - const editors: IDraggedResourceEditorInput[] = []; - if (e.dataTransfer && e.dataTransfer.types.length > 0) { - - // Data Transfer: Code Editors - const rawEditorsData = e.dataTransfer.getData(CodeDataTransfers.EDITORS); - if (rawEditorsData) { - try { - editors.push(...parse(rawEditorsData)); - } catch (error) { - // Invalid transfer - } - } - - // Data Transfer: Resources - else { - try { - const rawResourcesData = e.dataTransfer.getData(DataTransfers.RESOURCES); - editors.push(...createDraggedEditorInputFromRawResourcesData(rawResourcesData)); - } catch (error) { - // Invalid transfer - } - } - - // Check for native file transfer - if (e.dataTransfer?.files) { - for (let i = 0; i < e.dataTransfer.files.length; i++) { - const file = e.dataTransfer.files[i]; - if (file?.path /* Electron only */) { - try { - editors.push({ resource: URI.file(file.path), isExternal: true, allowWorkspaceOpen: true }); - } catch (error) { - // Invalid URI - } - } - } - } - - // Check for CodeFiles transfer - const rawCodeFiles = e.dataTransfer.getData(CodeDataTransfers.FILES); - if (rawCodeFiles) { - try { - const codeFiles: string[] = JSON.parse(rawCodeFiles); - for (const codeFile of codeFiles) { - editors.push({ resource: URI.file(codeFile), isExternal: true, allowWorkspaceOpen: true }); - } - } catch (error) { - // Invalid transfer - } - } - - // Web: Check for file transfer - if (isWeb && containsDragType(e, DataTransfers.FILES)) { - const files = e.dataTransfer.items; - if (files) { - const instantiationService = accessor.get(IInstantiationService); - const filesData = await instantiationService.invokeFunction(accessor => extractFilesDropData(accessor, e)); - for (const fileData of filesData) { - editors.push({ resource: fileData.resource, contents: fileData.contents?.toString(), isExternal: true, allowWorkspaceOpen: fileData.isDirectory }); - } - } - } - - // Workbench contributions - const contributions = Registry.as(Extensions.DragAndDropContribution).getAll(); - for (const contribution of contributions) { - const data = e.dataTransfer.getData(contribution.dataFormatKey); - if (data) { - try { - editors.push(...contribution.getEditorInputs(data)); - } catch (error) { - // Invalid transfer - } - } - } - } - - return editors; -} - -function createDraggedEditorInputFromRawResourcesData(rawResourcesData: string | undefined): IDraggedResourceEditorInput[] { - const editors: IDraggedResourceEditorInput[] = []; - - if (rawResourcesData) { - const resourcesRaw: string[] = JSON.parse(rawResourcesData); - for (const resourceRaw of resourcesRaw) { - if (resourceRaw.indexOf(':') > 0) { // mitigate https://github.com/microsoft/vscode/issues/124946 - const { selection, uri } = extractSelection(URI.parse(resourceRaw)); - editors.push({ resource: uri, options: { selection } }); - } - } - } - - return editors; -} - export async function extractTreeDropData(dataTransfer: IDataTransfer): Promise> { const editors: IDraggedResourceEditorInput[] = []; const resourcesKey = Mimes.uriList.toLowerCase(); @@ -201,121 +75,6 @@ export function convertResourceUrlsToUriList(resourceUrls: string): string { return asJson.map(uri => uri.toString()).join('\n'); } -interface IFileTransferData { - resource: URI; - isDirectory?: boolean; - contents?: VSBuffer; -} - -async function extractFilesDropData(accessor: ServicesAccessor, event: DragEvent): Promise { - - // Try to extract via `FileSystemHandle` - if (WebFileSystemAccess.supported(window)) { - const items = event.dataTransfer?.items; - if (items) { - return extractFileTransferData(accessor, items); - } - } - - // Try to extract via `FileList` - const files = event.dataTransfer?.files; - if (!files) { - return []; - } - - return extractFileListData(accessor, files); -} - -async function extractFileTransferData(accessor: ServicesAccessor, items: DataTransferItemList): Promise { - const fileSystemProvider = accessor.get(IFileService).getProvider(Schemas.file); - if (!(fileSystemProvider instanceof HTMLFileSystemProvider)) { - return []; // only supported when running in web - } - - const results: DeferredPromise[] = []; - - for (let i = 0; i < items.length; i++) { - const file = items[i]; - if (file) { - const result = new DeferredPromise(); - results.push(result); - - (async () => { - try { - const handle = await file.getAsFileSystemHandle(); - if (!handle) { - result.complete(undefined); - return; - } - - if (WebFileSystemAccess.isFileSystemFileHandle(handle)) { - result.complete({ - resource: await fileSystemProvider.registerFileHandle(handle), - isDirectory: false - }); - } else if (WebFileSystemAccess.isFileSystemDirectoryHandle(handle)) { - result.complete({ - resource: await fileSystemProvider.registerDirectoryHandle(handle), - isDirectory: true - }); - } else { - result.complete(undefined); - } - } catch (error) { - result.complete(undefined); - } - })(); - } - } - - return coalesce(await Promise.all(results.map(result => result.p))); -} - -export async function extractFileListData(accessor: ServicesAccessor, files: FileList): Promise { - const dialogService = accessor.get(IDialogService); - - const results: DeferredPromise[] = []; - - for (let i = 0; i < files.length; i++) { - const file = files.item(i); - if (file) { - - // Skip for very large files because this operation is unbuffered - if (file.size > 100 * ByteSize.MB) { - dialogService.show(Severity.Warning, localize('fileTooLarge', "File is too large to open as untitled editor. Please upload it first into the file explorer and then try again.")); - continue; - } - - const result = new DeferredPromise(); - results.push(result); - - const reader = new FileReader(); - - reader.onerror = () => result.complete(undefined); - reader.onabort = () => result.complete(undefined); - - reader.onload = async event => { - const name = file.name; - const loadResult = withNullAsUndefined(event.target?.result); - if (typeof name !== 'string' || typeof loadResult === 'undefined') { - result.complete(undefined); - return; - } - - result.complete({ - resource: URI.from({ scheme: Schemas.untitled, path: name }), - contents: typeof loadResult === 'string' ? VSBuffer.fromString(loadResult) : VSBuffer.wrap(new Uint8Array(loadResult)) - }); - }; - - // Start reading - reader.readAsArrayBuffer(file); - } - } - - return coalesce(await Promise.all(results.map(result => result.p))); -} - export interface IResourcesDropHandlerOptions { /** @@ -443,11 +202,6 @@ export class ResourcesDropHandler { } } -interface IResourceStat { - resource: URI; - isDirectory?: boolean; -} - export function fillEditorsDragData(accessor: ServicesAccessor, resources: URI[], event: DragMouseEvent | DragEvent): void; export function fillEditorsDragData(accessor: ServicesAccessor, resources: IResourceStat[], event: DragMouseEvent | DragEvent): void; export function fillEditorsDragData(accessor: ServicesAccessor, editors: IEditorIdentifier[], event: DragMouseEvent | DragEvent): void; @@ -591,48 +345,6 @@ export function fillEditorsDragData(accessor: ServicesAccessor, resourcesOrEdito //#endregion -//#region DND contributions - -export interface IDragAndDropContributionRegistry { - /** - * Registers a drag and drop contribution. - */ - register(contribution: IDragAndDropContribution): void; - - /** - * Returns all registered drag and drop contributions. - */ - getAll(): IterableIterator; -} - -export interface IDragAndDropContribution { - readonly dataFormatKey: string; - getEditorInputs(data: string): IDraggedResourceEditorInput[]; - setData(resources: IResourceStat[], event: DragMouseEvent | DragEvent): void; -} - -class DragAndDropContributionRegistry implements IDragAndDropContributionRegistry { - private readonly _contributions = new Map(); - - register(contribution: IDragAndDropContribution): void { - if (this._contributions.has(contribution.dataFormatKey)) { - throw new Error(`A drag and drop contributiont with key '${contribution.dataFormatKey}' was already registered.`); - } - this._contributions.set(contribution.dataFormatKey, contribution); - } - - getAll(): IterableIterator { - return this._contributions.values(); - } -} - -export const Extensions = { - DragAndDropContribution: 'workbench.contributions.dragAndDrop' -}; - -Registry.add(Extensions.DragAndDropContribution, new DragAndDropContributionRegistry()); - -//#endregion //#region DND Utilities @@ -681,26 +393,6 @@ export class LocalSelectionTransfer { } } -export function containsDragType(event: DragEvent, ...dragTypesToFind: string[]): boolean { - if (!event.dataTransfer) { - return false; - } - - const dragTypes = event.dataTransfer.types; - const lowercaseDragTypes: string[] = []; - for (let i = 0; i < dragTypes.length; i++) { - lowercaseDragTypes.push(dragTypes[i].toLowerCase()); // somehow the types are lowercase - } - - for (const dragType of dragTypesToFind) { - if (lowercaseDragTypes.indexOf(dragType.toLowerCase()) >= 0) { - return true; - } - } - - return false; -} - //#endregion //#region Composites DND diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index f81d3790b5da1..ad6a0289f125f 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -18,7 +18,8 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; import { isTemporaryWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { CodeDataTransfers, containsDragType, DraggedEditorGroupIdentifier, DraggedEditorIdentifier, DraggedTreeItemsIdentifier, Extensions as DragAndDropExtensions, extractTreeDropData, IDragAndDropContributionRegistry, LocalSelectionTransfer, ResourcesDropHandler } from 'vs/workbench/browser/dnd'; +import { CodeDataTransfers, containsDragType, Extensions as DragAndDropExtensions, IDragAndDropContributionRegistry } from 'vs/platform/dnd/browser/dnd'; +import { DraggedEditorGroupIdentifier, DraggedEditorIdentifier, DraggedTreeItemsIdentifier, extractTreeDropData, LocalSelectionTransfer, ResourcesDropHandler } from 'vs/workbench/browser/dnd'; import { fillActiveEditorViewState, IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { EditorInputCapabilities, IEditorIdentifier, IUntypedEditorInput } from 'vs/workbench/common/editor'; import { EDITOR_DRAG_AND_DROP_BACKGROUND, EDITOR_DROP_INTO_PROMPT_BACKGROUND, EDITOR_DROP_INTO_PROMPT_BORDER, EDITOR_DROP_INTO_PROMPT_FOREGROUND } from 'vs/workbench/common/theme'; diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 9b06e86ae1f91..9d2c5d0dab008 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -31,7 +31,7 @@ import { isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import 'vs/css!./media/views'; -import { IDataTransfer } from 'vs/editor/common/dnd'; +import { IDataTransfer } from 'vs/base/common/dataTransfer'; import { Command } from 'vs/editor/common/languages'; import { localize } from 'vs/nls'; import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -54,7 +54,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder, textCodeBlockBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; import { ColorScheme } from 'vs/platform/theme/common/theme'; import { FileThemeIcon, FolderThemeIcon, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { CodeDataTransfers, convertResourceUrlsToUriList, DraggedTreeItemsIdentifier, fillEditorsDragData, LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; +import { convertResourceUrlsToUriList, DraggedTreeItemsIdentifier, fillEditorsDragData, LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { API_OPEN_DIFF_EDITOR_COMMAND_ID, API_OPEN_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; @@ -66,6 +66,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; import { ThemeSettings } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITreeViewsService } from 'vs/workbench/services/views/browser/treeViewsService'; +import { CodeDataTransfers } from 'vs/platform/dnd/browser/dnd'; export class TreeViewPane extends ViewPane { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 7bd541c124828..5c9c7d93a9dd0 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -27,7 +27,7 @@ import { mixin } from 'vs/base/common/objects'; import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IDataTransfer } from 'vs/editor/common/dnd'; +import { IDataTransfer } from 'vs/base/common/dataTransfer'; export const defaultViewIcon = registerIcon('default-view-icon', Codicon.window, localize('defaultViewIcon', 'Default view icon.')); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 99b7c3eb1f180..0d895be2eae02 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -58,7 +58,7 @@ import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/act import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { coalesce } from 'vs/base/common/arrays'; -import { extractEditorsDropData } from 'vs/workbench/browser/dnd'; +import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd'; import { extname } from 'vs/base/common/resources'; const SearchMarketplaceExtensionsContext = new RawContextKey('searchMarketplaceExtensions', false); diff --git a/src/vs/workbench/contrib/files/browser/fileImportExport.ts b/src/vs/workbench/contrib/files/browser/fileImportExport.ts index 71eda39201c85..2215ecd93ad56 100644 --- a/src/vs/workbench/contrib/files/browser/fileImportExport.ts +++ b/src/vs/workbench/contrib/files/browser/fileImportExport.ts @@ -20,7 +20,7 @@ import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { URI } from 'vs/base/common/uri'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { extractEditorsDropData } from 'vs/workbench/browser/dnd'; +import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { isWeb } from 'vs/base/common/platform'; import { triggerDownload } from 'vs/base/browser/dom'; diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 1181f9867c478..a24f390cf51f3 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -30,7 +30,8 @@ import { equals, deepClone } from 'vs/base/common/objects'; import * as path from 'vs/base/common/path'; import { ExplorerItem, NewExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { compareFileExtensionsDefault, compareFileNamesDefault, compareFileNamesUpper, compareFileExtensionsUpper, compareFileNamesLower, compareFileExtensionsLower, compareFileNamesUnicode, compareFileExtensionsUnicode } from 'vs/base/common/comparers'; -import { fillEditorsDragData, CodeDataTransfers, containsDragType } from 'vs/workbench/browser/dnd'; +import { CodeDataTransfers, containsDragType } from 'vs/platform/dnd/browser/dnd'; +import { fillEditorsDragData } from 'vs/workbench/browser/dnd'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDragAndDropData, DataTransfers } from 'vs/base/browser/dnd'; import { Schemas } from 'vs/base/common/network'; diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 06ba5acdbdeca..fdfcc0a46a0e9 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -32,7 +32,8 @@ import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/m import { IMenuService, MenuId, IMenu, Action2, registerAction2, MenuRegistry } from 'vs/platform/actions/common/actions'; import { OpenEditorsDirtyEditorContext, OpenEditorsGroupContext, OpenEditorsReadonlyEditorContext, SAVE_ALL_LABEL, SAVE_ALL_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileConstants'; import { ResourceContextKey } from 'vs/workbench/common/contextkeys'; -import { ResourcesDropHandler, fillEditorsDragData, CodeDataTransfers, containsDragType } from 'vs/workbench/browser/dnd'; +import { CodeDataTransfers, containsDragType } from 'vs/platform/dnd/browser/dnd'; +import { ResourcesDropHandler, fillEditorsDragData } from 'vs/workbench/browser/dnd'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IDragAndDropData, DataTransfers } from 'vs/base/browser/dnd'; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index e7d863ed61c3d..a3059234eb15f 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -85,6 +85,7 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdownRenderer/browser/mar import { Button, ButtonWithDescription } from 'vs/base/browser/ui/button/button'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { RepositoryContextKeys } from 'vs/workbench/contrib/scm/browser/scmViewService'; +import { DropIntoEditorController } from 'vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution'; type TreeElement = ISCMRepository | ISCMInput | ISCMActionButton | ISCMResourceGroup | IResourceNode | ISCMResource; @@ -1935,7 +1936,8 @@ class SCMInputWidget extends Disposable { quickSuggestions: false, scrollbar: { alwaysConsumeMouseWheel: false }, overflowWidgetsDomNode, - renderWhitespace: 'none' + renderWhitespace: 'none', + enableDropIntoEditor: true, }; const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { @@ -1948,7 +1950,8 @@ class SCMInputWidget extends Disposable { ContextMenuController.ID, ColorDetector.ID, ModesHoverController.ID, - LinkDetector.ID + LinkDetector.ID, + DropIntoEditorController.ID ]) }; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index f8aae95de73f3..104e9f455e11d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -16,7 +16,7 @@ import { KeybindingWeight, KeybindingsRegistry, IKeybindings } from 'vs/platform import { Registry } from 'vs/platform/registry/common/platform'; import { getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess'; import { Extensions as ViewContainerExtensions, IViewContainersRegistry, ViewContainerLocation, IViewsRegistry } from 'vs/workbench/common/views'; -import { Extensions as DragAndDropExtensions, IDragAndDropContributionRegistry, IDraggedResourceEditorInput } from 'vs/workbench/browser/dnd'; +import { Extensions as DragAndDropExtensions, IDragAndDropContributionRegistry, IDraggedResourceEditorInput } from 'vs/platform/dnd/browser/dnd'; import { registerTerminalActions, terminalSendSequenceCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; import { TERMINAL_VIEW_ID, TerminalCommandId, ITerminalProfileService } from 'vs/workbench/contrib/terminal/common/terminal'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index f845f8ddc335a..61c2efbb88501 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -48,7 +48,7 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; -import { CodeDataTransfers, containsDragType } from 'vs/workbench/browser/dnd'; +import { CodeDataTransfers, containsDragType } from 'vs/platform/dnd/browser/dnd'; import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views'; import { IDetectedLinks, TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager'; import { TerminalLinkQuickpick } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkQuickpick'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index 868644fc9184f..0a39093d05ef6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -40,7 +40,7 @@ import { once } from 'vs/base/common/functional'; import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { CodeDataTransfers, containsDragType } from 'vs/workbench/browser/dnd'; +import { CodeDataTransfers, containsDragType } from 'vs/platform/dnd/browser/dnd'; import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IProcessDetails } from 'vs/platform/terminal/common/terminalProcess'; diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts index 1fc0ccc35703b..87f6e2ca052fa 100644 --- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -16,7 +16,7 @@ import { basename } from 'vs/base/common/resources'; import { triggerDownload, triggerUpload } from 'vs/base/browser/dom'; import Severity from 'vs/base/common/severity'; import { VSBuffer } from 'vs/base/common/buffer'; -import { extractFileListData } from 'vs/workbench/browser/dnd'; +import { extractFileListData } from 'vs/platform/dnd/browser/dnd'; import { Iterable } from 'vs/base/common/iterator'; import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; diff --git a/src/vs/workbench/services/views/browser/treeViewsService.ts b/src/vs/workbench/services/views/browser/treeViewsService.ts index 988300aadee95..93d426deed5b1 100644 --- a/src/vs/workbench/services/views/browser/treeViewsService.ts +++ b/src/vs/workbench/services/views/browser/treeViewsService.ts @@ -5,7 +5,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IDataTransfer } from 'vs/editor/common/dnd'; +import { IDataTransfer } from 'vs/base/common/dataTransfer'; import { ITreeItem } from 'vs/workbench/common/views'; import { ITreeViewsService as ITreeViewsServiceCommon, TreeviewsService } from 'vs/workbench/services/views/common/treeViewsService'; diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index bf87db19d53f7..992aec9c53fb8 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -343,7 +343,4 @@ import 'vs/workbench/contrib/list/browser/list.contribution'; // Audio Cues import 'vs/workbench/contrib/audioCues/browser/audioCues.contribution'; -// Drop into editor -import 'vs/workbench/contrib/dropIntoEditor/browser/dropIntoEditor.contibution'; - //#endregion From 1181a658000b021a70de82d407e9d8b6684b4156 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Tue, 17 May 2022 12:10:25 -0700 Subject: [PATCH 118/942] add back running on close (#149751) --- .github/workflows/pr-chat.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-chat.yml b/.github/workflows/pr-chat.yml index 969c3cc17c0d2..0c8d6bab79388 100644 --- a/.github/workflows/pr-chat.yml +++ b/.github/workflows/pr-chat.yml @@ -1,7 +1,7 @@ name: PR Chat on: pull_request_target: - types: [opened, ready_for_review] + types: [opened, ready_for_review, closed] jobs: main: From 18cf3d01ed22f7b35895a9b3c531048c835d4489 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 17 May 2022 12:13:44 -0700 Subject: [PATCH 119/942] Polish notebook workspace edit (#149734) - Add `insertCells` helper - Add a `newNotebookMetadata` property for setting the metadata for the entire notebook - Add a `NotebookEdit.updateNotebookMetadata` helper - Remove the previous `WorkspaceEdit.updateNotebookMetadata` function since you can now use `NotebookEdit.updateNotebookMetadata` --- src/vs/workbench/api/common/extHostTypes.ts | 24 ++++++++--- ...vscode.proposed.notebookWorkspaceEdit.d.ts | 43 +++++++++++++------ 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 5976d7d182e17..dc08ede3d9868 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -616,22 +616,34 @@ export class NotebookEdit implements vscode.NotebookEdit { return new NotebookEdit(range, newCells); } + static insertCells(index: number, newCells: vscode.NotebookCellData[]): vscode.NotebookEdit { + return new NotebookEdit(new NotebookRange(index, index), newCells); + } + static deleteCells(range: NotebookRange): NotebookEdit { return new NotebookEdit(range, []); } static updateCellMetadata(index: number, newMetadata: { [key: string]: any }): NotebookEdit { - return new NotebookEdit(new NotebookRange(index, index), [], newMetadata); + const edit = new NotebookEdit(new NotebookRange(index, index), []); + edit.newCellMetadata = newMetadata; + return edit; + } + + static updateNotebookMetadata(newMetadata: { [key: string]: any }): NotebookEdit { + const edit = new NotebookEdit(new NotebookRange(0, 0), []); + edit.newNotebookMetadata = newMetadata; + return edit; } - readonly range: NotebookRange; - readonly newCells: NotebookCellData[]; - readonly newCellMetadata?: { [key: string]: any }; + range: NotebookRange; + newCells: NotebookCellData[]; + newCellMetadata?: { [key: string]: any }; + newNotebookMetadata?: { [key: string]: any }; - constructor(range: NotebookRange, newCells: NotebookCellData[], newCellMetadata?: { [key: string]: any }) { + constructor(range: NotebookRange, newCells: NotebookCellData[]) { this.range = range; this.newCells = newCells; - this.newCellMetadata = newCellMetadata; } } diff --git a/src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts b/src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts index 01330ec9cdd1f..14965fbe054db 100644 --- a/src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts @@ -21,44 +21,59 @@ declare module 'vscode' { static replaceCells(range: NotebookRange, newCells: NotebookCellData[]): NotebookEdit; /** - * Utility to create a edit that deletes cells in a notebook. + * Utility to create an edit that replaces cells in a notebook. + * + * @param index The index to insert cells at. + * @param newCells The new notebook cells. + */ + static insertCells(index: number, newCells: NotebookCellData[]): NotebookEdit; + + /** + * Utility to create an edit that deletes cells in a notebook. * * @param range The range of cells to delete. */ static deleteCells(range: NotebookRange): NotebookEdit; /** - * Utility to update a cells metadata. + * Utility to create an edit that update a cell's metadata. * * @param index The index of the cell to update. - * @param newMetadata The new metadata for the cell. + * @param newCellMetadata The new metadata for the cell. */ - static updateCellMetadata(index: number, newMetadata: { [key: string]: any }): NotebookEdit; + static updateCellMetadata(index: number, newCellMetadata: { [key: string]: any }): NotebookEdit; /** - * Range of the cells being edited + * Utility to create an edit that updates the notebook's metadata. + * + * @param newNotebookMetadata The new metadata for the notebook. */ - readonly range: NotebookRange; + static updateNotebookMetadata(newNotebookMetadata: { [key: string]: any }): NotebookEdit; + + /** + * Range of the cells being edited. May be empty. + */ + range: NotebookRange; /** * New cells being inserted. May be empty. */ - readonly newCells: NotebookCellData[]; + newCells: NotebookCellData[]; /** * Optional new metadata for the cells. */ - readonly newCellMetadata?: { [key: string]: any }; - - constructor(range: NotebookRange, newCells: NotebookCellData[], newCellMetadata?: { [key: string]: any }); - } + newCellMetadata?: { [key: string]: any }; - export interface WorkspaceEdit { /** - * Replaces the metadata for a notebook document. + * Optional new metadata for the notebook. */ - replaceNotebookMetadata(uri: Uri, value: { [key: string]: any }): void; + newNotebookMetadata?: { [key: string]: any }; + constructor(range: NotebookRange, newCells: NotebookCellData[]); + } + + export interface WorkspaceEdit { /** * Set (and replace) edits for a resource. * From ca4db53d718f07c8b9732b9a59322b6ecc574fe4 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 17 May 2022 12:14:54 -0700 Subject: [PATCH 120/942] Skip shell integration smoke tests on mac as well Part of #149757 --- .../smoke/src/areas/terminal/terminal-shellIntegration.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts index b94b40045f7a3..a5f5842b4c6bd 100644 --- a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts +++ b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts @@ -26,7 +26,8 @@ export function setup() { describe('Shell integration', function () { // TODO: Fix on Linux, some distros use sh as the default shell in which case shell integration will fail - (process.platform === 'win32' || process.platform === 'linux' ? describe.skip : describe)('Decorations', function () { + // TODO: Fix on macOS, not sure reason for failing https://github.com/microsoft/vscode/issues/149757 + describe.skip('Decorations', function () { describe('Should show default icons', function () { it('Placeholder', async () => { await terminal.createTerminal(); From 769a8f15c725e203bcf8ee79055588caabd95c2d Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 17 May 2022 15:29:22 -0400 Subject: [PATCH 121/942] Annotate error fragment (#149760) --- .../telemetry/common/errorTelemetry.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/telemetry/common/errorTelemetry.ts b/src/vs/platform/telemetry/common/errorTelemetry.ts index 309b372c83b51..8de88fe914253 100644 --- a/src/vs/platform/telemetry/common/errorTelemetry.ts +++ b/src/vs/platform/telemetry/common/errorTelemetry.ts @@ -10,14 +10,16 @@ import { safeStringify } from 'vs/base/common/objects'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; type ErrorEventFragment = { - callstack: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; - msg?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; - file?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; - line?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; isMeasurement: true }; - column?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; isMeasurement: true }; - uncaught_error_name?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; - uncaught_error_msg?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth' }; - count?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; isMeasurement: true }; + owner: 'lramos15, sbatten' + comment: 'Whenever an error in VS Code is thrown.' + callstack: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth', comment: 'The callstack of the error.' }; + msg?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth', comment: 'The message of the error. Normally the first line int the callstack.' }; + file?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth', comment: 'The file the error originated from.'}; + line?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; isMeasurement: true, comment: 'The line the error originate on.' }; + column?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; isMeasurement: true, comment: 'The column of the line which the error orginated on.'}; + uncaught_error_name?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth', comment: 'If the error is uncaught what is the error type' }; + uncaught_error_msg?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth', comment: 'If the error is uncaught this is just msg but for uncaught errors.' }; + count?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; isMeasurement: true, comment: 'How many times this error has been thrown' }; }; export interface ErrorEvent { callstack: string; From c3d6d7ec7832fa7607422f24e62638d94d50aff2 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Tue, 17 May 2022 12:32:01 -0700 Subject: [PATCH 122/942] fixes #149438 (#149761) --- src/vs/workbench/browser/parts/panel/panelPart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 5ffa3a07bbc27..453dd8c5f1162 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -378,7 +378,7 @@ export abstract class BasePanelPart extends CompositePart impleme contextKey.set(true); this.compositeBar.addComposite({ id: viewContainer.id, name: viewContainer.title, order: viewContainer.order, requestedIndex: viewContainer.requestedIndex }); - if (this.layoutService.isVisible(this.partId)) { + if (this.layoutService.isRestored() && this.layoutService.isVisible(this.partId)) { const activeComposite = this.getActiveComposite(); if (activeComposite === undefined || activeComposite.getId() === viewContainer.id) { this.compositeBar.activateComposite(viewContainer.id); From 95603b0f2ed4a6bf55d33eb9ac35d8350db12f44 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 17 May 2022 16:00:42 -0400 Subject: [PATCH 123/942] Fix hygiene (#149763) --- .../telemetry/common/errorTelemetry.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/platform/telemetry/common/errorTelemetry.ts b/src/vs/platform/telemetry/common/errorTelemetry.ts index 8de88fe914253..acc2dbd083300 100644 --- a/src/vs/platform/telemetry/common/errorTelemetry.ts +++ b/src/vs/platform/telemetry/common/errorTelemetry.ts @@ -10,16 +10,16 @@ import { safeStringify } from 'vs/base/common/objects'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; type ErrorEventFragment = { - owner: 'lramos15, sbatten' - comment: 'Whenever an error in VS Code is thrown.' - callstack: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth', comment: 'The callstack of the error.' }; - msg?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth', comment: 'The message of the error. Normally the first line int the callstack.' }; - file?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth', comment: 'The file the error originated from.'}; - line?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; isMeasurement: true, comment: 'The line the error originate on.' }; - column?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; isMeasurement: true, comment: 'The column of the line which the error orginated on.'}; - uncaught_error_name?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth', comment: 'If the error is uncaught what is the error type' }; - uncaught_error_msg?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth', comment: 'If the error is uncaught this is just msg but for uncaught errors.' }; - count?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; isMeasurement: true, comment: 'How many times this error has been thrown' }; + owner: 'lramos15, sbatten'; + comment: 'Whenever an error in VS Code is thrown.'; + callstack: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; comment: 'The callstack of the error.' }; + msg?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; comment: 'The message of the error. Normally the first line int the callstack.' }; + file?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; comment: 'The file the error originated from.' }; + line?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The line the error originate on.' }; + column?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The column of the line which the error orginated on.' }; + uncaught_error_name?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; comment: 'If the error is uncaught what is the error type' }; + uncaught_error_msg?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; comment: 'If the error is uncaught this is just msg but for uncaught errors.' }; + count?: { classification: 'CallstackOrException'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'How many times this error has been thrown' }; }; export interface ErrorEvent { callstack: string; From ba59882f7175b573ef553ee106437d07a338b908 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 18 May 2022 00:08:27 +0200 Subject: [PATCH 124/942] Reduce usages of `editor.deltaDecorations` (#149768) --- .../editor/browser/widget/codeEditorWidget.ts | 16 ++++-- .../editor/browser/widget/diffEditorWidget.ts | 4 +- src/vs/editor/common/editorCommon.ts | 2 +- .../anchorSelect/browser/anchorSelect.ts | 30 +++++++----- src/vs/editor/contrib/dnd/browser/dnd.ts | 15 +++--- .../gotoSymbol/browser/goToCommands.ts | 4 +- .../inPlaceReplace/browser/inPlaceReplace.ts | 9 ++-- .../contrib/zoneWidget/browser/zoneWidget.ts | 42 +++++++--------- src/vs/monaco.d.ts | 2 +- .../browser/callHierarchyPeek.ts | 4 +- .../browser/outline/documentSymbolsOutline.ts | 4 +- .../comments/browser/commentGlyphWidget.ts | 14 +++--- .../browser/commentsEditorContribution.ts | 6 ++- .../browser/callStackEditorContribution.ts | 6 +-- .../debug/browser/debugEditorContribution.ts | 8 +-- .../contrib/debug/browser/debugHover.ts | 8 ++- .../browser/viewModel/baseCellViewModel.ts | 49 +++++++++++-------- .../browser/keybindingsEditorContribution.ts | 6 +-- .../browser/preferencesRenderers.ts | 6 +-- .../preferences/browser/preferencesWidgets.ts | 7 ++- .../browser/typeHierarchyPeek.ts | 4 +- 21 files changed, 128 insertions(+), 118 deletions(-) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 25890eb233209..991afab9ef830 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1262,8 +1262,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._modelData.viewModel.executeCommands(commands, source); } - public createDecorationsCollection(): EditorDecorationsCollection { - return new EditorDecorationsCollection(this); + public createDecorationsCollection(decorations?: IModelDeltaDecoration[]): EditorDecorationsCollection { + return new EditorDecorationsCollection(this, decorations); } public changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any { @@ -2152,8 +2152,13 @@ class EditorDecorationsCollection implements editorCommon.IEditorDecorationsColl } constructor( - private readonly _editor: editorBrowser.ICodeEditor - ) { } + private readonly _editor: editorBrowser.ICodeEditor, + decorations: IModelDeltaDecoration[] | undefined + ) { + if (Array.isArray(decorations) && decorations.length > 0) { + this.set(decorations); + } + } public onDidChange(listener: (e: IModelDecorationsChangedEvent) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable { return this._editor.onDidChangeModelDecorations((e) => { @@ -2168,6 +2173,9 @@ class EditorDecorationsCollection implements editorCommon.IEditorDecorationsColl if (!this._editor.hasModel()) { return null; } + if (index >= this._decorationIds.length) { + return null; + } return this._editor.getModel().getDecorationRange(this._decorationIds[index]); } diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index d2129bc8e38dd..e9e666a6ce0d3 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -920,8 +920,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._modifiedEditor.trigger(source, handlerId, payload); } - public createDecorationsCollection(): editorCommon.IEditorDecorationsCollection { - return this._modifiedEditor.createDecorationsCollection(); + public createDecorationsCollection(decorations?: IModelDeltaDecoration[]): editorCommon.IEditorDecorationsCollection { + return this._modifiedEditor.createDecorationsCollection(decorations); } public changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any { diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 4b8f8d58fbcf6..9260b986d88f3 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -465,7 +465,7 @@ export interface IEditor { * will get the ownerId of the editor (meaning they will not show up in other editors). * These decorations will be automatically cleared when the editor's model changes. */ - createDecorationsCollection(): IEditorDecorationsCollection; + createDecorationsCollection(decorations?: IModelDeltaDecoration[]): IEditorDecorationsCollection; /** * Change the decorations. All decorations added through this changeAccessor diff --git a/src/vs/editor/contrib/anchorSelect/browser/anchorSelect.ts b/src/vs/editor/contrib/anchorSelect/browser/anchorSelect.ts index ceaefc19bb1ac..6972a300abb19 100644 --- a/src/vs/editor/contrib/anchorSelect/browser/anchorSelect.ts +++ b/src/vs/editor/contrib/anchorSelect/browser/anchorSelect.ts @@ -43,17 +43,20 @@ class SelectionAnchorController implements IEditorContribution { setSelectionAnchor(): void { if (this.editor.hasModel()) { const position = this.editor.getPosition(); - const previousDecorations = this.decorationId ? [this.decorationId] : []; - const newDecorationId = this.editor.deltaDecorations(previousDecorations, [{ - range: Selection.fromPositions(position, position), - options: { - description: 'selection-anchor', - stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - hoverMessage: new MarkdownString().appendText(localize('selectionAnchor', "Selection Anchor")), - className: 'selection-anchor' + this.editor.changeDecorations((accessor) => { + if (this.decorationId) { + accessor.removeDecoration(this.decorationId); } - }]); - this.decorationId = newDecorationId[0]; + this.decorationId = accessor.addDecoration( + Selection.fromPositions(position, position), + { + description: 'selection-anchor', + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + hoverMessage: new MarkdownString().appendText(localize('selectionAnchor', "Selection Anchor")), + className: 'selection-anchor' + } + ); + }); this.selectionAnchorSetContextKey.set(!!this.decorationId); alert(localize('anchorSet', "Anchor set at {0}:{1}", position.lineNumber, position.column)); } @@ -81,8 +84,11 @@ class SelectionAnchorController implements IEditorContribution { cancelSelectionAnchor(): void { if (this.decorationId) { - this.editor.deltaDecorations([this.decorationId], []); - this.decorationId = undefined; + const decorationId = this.decorationId; + this.editor.changeDecorations((accessor) => { + accessor.removeDecoration(decorationId); + this.decorationId = undefined; + }); this.selectionAnchorSetContextKey.set(false); } } diff --git a/src/vs/editor/contrib/dnd/browser/dnd.ts b/src/vs/editor/contrib/dnd/browser/dnd.ts index ce2faf6d2ebe3..cfb074ae7adc4 100644 --- a/src/vs/editor/contrib/dnd/browser/dnd.ts +++ b/src/vs/editor/contrib/dnd/browser/dnd.ts @@ -17,8 +17,7 @@ import { CursorChangeReason } from 'vs/editor/common/cursorEvents'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; -import { IModelDeltaDecoration } from 'vs/editor/common/model'; +import { IEditorContribution, IEditorDecorationsCollection, ScrollType } from 'vs/editor/common/editorCommon'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { DragAndDropCommand } from 'vs/editor/contrib/dnd/browser/dragAndDropCommand'; @@ -36,7 +35,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu private readonly _editor: ICodeEditor; private _dragSelection: Selection | null; - private _dndDecorationIds: string[]; + private readonly _dndDecorationIds: IEditorDecorationsCollection; private _mouseDown: boolean; private _modifierPressed: boolean; static readonly TRIGGER_KEY_VALUE = isMacintosh ? KeyCode.Alt : KeyCode.Ctrl; @@ -48,6 +47,7 @@ export class DragAndDropController extends Disposable implements IEditorContribu constructor(editor: ICodeEditor) { super(); this._editor = editor; + this._dndDecorationIds = this._editor.createDecorationsCollection(); this._register(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e))); this._register(this._editor.onMouseDrag((e: IEditorMouseEvent) => this._onEditorMouseDrag(e))); @@ -57,7 +57,6 @@ export class DragAndDropController extends Disposable implements IEditorContribu this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(e))); this._register(this._editor.onDidBlurEditorWidget(() => this.onEditorBlur())); this._register(this._editor.onDidBlurEditorText(() => this.onEditorBlur())); - this._dndDecorationIds = []; this._mouseDown = false; this._modifierPressed = false; this._dragSelection = null; @@ -209,17 +208,15 @@ export class DragAndDropController extends Disposable implements IEditorContribu }); public showAt(position: Position): void { - let newDecorations: IModelDeltaDecoration[] = [{ + this._dndDecorationIds.set([{ range: new Range(position.lineNumber, position.column, position.lineNumber, position.column), options: DragAndDropController._DECORATION_OPTIONS - }]; - - this._dndDecorationIds = this._editor.deltaDecorations(this._dndDecorationIds, newDecorations); + }]); this._editor.revealPosition(position, ScrollType.Immediate); } private _removeDecoration(): void { - this._dndDecorationIds = this._editor.deltaDecorations(this._dndDecorationIds, []); + this._dndDecorationIds.clear(); } private _hitContent(target: IMouseTarget): boolean { diff --git a/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts index 75af03dba4c8f..cb1b242d7a317 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts @@ -213,10 +213,10 @@ export abstract class SymbolNavigationAction extends EditorAction { if (highlight) { const modelNow = targetEditor.getModel(); - const ids = targetEditor.deltaDecorations([], [{ range, options: { description: 'symbol-navigate-action-highlight', className: 'symbolHighlight' } }]); + const decorations = targetEditor.createDecorationsCollection([{ range, options: { description: 'symbol-navigate-action-highlight', className: 'symbolHighlight' } }]); setTimeout(() => { if (targetEditor.getModel() === modelNow) { - targetEditor.deltaDecorations(ids, []); + decorations.clear(); } }, 350); } diff --git a/src/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace.ts b/src/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace.ts index bdcc799b1ca4d..2ec112e6a3f33 100644 --- a/src/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace.ts +++ b/src/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace.ts @@ -11,7 +11,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, registerEditorAction, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IEditorContribution, IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IInplaceReplaceSupportResult } from 'vs/editor/common/languages'; @@ -37,7 +37,7 @@ class InPlaceReplaceController implements IEditorContribution { private readonly editor: ICodeEditor; private readonly editorWorkerService: IEditorWorkerService; - private decorationIds: string[] = []; + private readonly decorations: IEditorDecorationsCollection; private currentRequest?: CancelablePromise; private decorationRemover?: CancelablePromise; @@ -47,6 +47,7 @@ class InPlaceReplaceController implements IEditorContribution { ) { this.editor = editor; this.editorWorkerService = editorWorkerService; + this.decorations = this.editor.createDecorationsCollection(); } public dispose(): void { @@ -114,7 +115,7 @@ class InPlaceReplaceController implements IEditorContribution { this.editor.pushUndoStop(); // add decoration - this.decorationIds = this.editor.deltaDecorations(this.decorationIds, [{ + this.decorations.set([{ range: highlightRange, options: InPlaceReplaceController.DECORATION }]); @@ -124,7 +125,7 @@ class InPlaceReplaceController implements IEditorContribution { this.decorationRemover.cancel(); } this.decorationRemover = timeout(350); - this.decorationRemover.then(() => this.decorationIds = this.editor.deltaDecorations(this.decorationIds, [])).catch(onUnexpectedError); + this.decorationRemover.then(() => this.decorations.clear()).catch(onUnexpectedError); }).catch(onUnexpectedError); } diff --git a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts index c65d7f7e539e0..e3fbe97873eb0 100644 --- a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts @@ -14,7 +14,7 @@ import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, IViewZo import { EditorLayoutInfo, EditorOption } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { ScrollType } from 'vs/editor/common/editorCommon'; +import { IEditorDecorationsCollection, ScrollType } from 'vs/editor/common/editorCommon'; import { TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -108,15 +108,13 @@ class Arrow { private static readonly _IdGenerator = new IdGenerator('.arrow-decoration-'); private readonly _ruleName = Arrow._IdGenerator.nextId(); - private _decorations: string[] = []; + private readonly _decorations = this._editor.createDecorationsCollection(); private _color: string | null = null; private _height: number = -1; constructor( private readonly _editor: ICodeEditor - ) { - // - } + ) { } dispose(): void { this.hide(); @@ -152,14 +150,18 @@ class Arrow { where = { lineNumber: where.lineNumber, column: 2 }; } - this._decorations = this._editor.deltaDecorations( - this._decorations, - [{ range: Range.fromPositions(where), options: { description: 'zone-widget-arrow', className: this._ruleName, stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }] - ); + this._decorations.set([{ + range: Range.fromPositions(where), + options: { + description: 'zone-widget-arrow', + className: this._ruleName, + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges + } + }]); } hide(): void { - this._editor.deltaDecorations(this._decorations, []); + this._decorations.clear(); } } @@ -168,7 +170,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { private _arrow: Arrow | null = null; private _overlayWidget: OverlayWidgetDelegate | null = null; private _resizeSash: Sash | null = null; - private _positionMarkerId: string[] = []; + private readonly _positionMarkerId: IEditorDecorationsCollection; protected _viewZone: ViewZoneDelegate | null = null; protected readonly _disposables = new DisposableStore(); @@ -181,6 +183,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { constructor(editor: ICodeEditor, options: IOptions = {}) { this.editor = editor; + this._positionMarkerId = this.editor.createDecorationsCollection(); this.options = objects.deepClone(options); objects.mixin(this.options, defaultOptions, false); this.domNode = document.createElement('div'); @@ -212,8 +215,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { }); } - this.editor.deltaDecorations(this._positionMarkerId, []); - this._positionMarkerId = []; + this._positionMarkerId.clear(); this._disposables.dispose(); } @@ -291,17 +293,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { } get position(): Position | undefined { - const [id] = this._positionMarkerId; - if (!id) { - return undefined; - } - - const model = this.editor.getModel(); - if (!model) { - return undefined; - } - - const range = model.getDecorationRange(id); + const range = this._positionMarkerId.getRange(0); if (!range) { return undefined; } @@ -315,7 +307,7 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { this._isShowing = true; this._showImpl(range, heightInLines); this._isShowing = false; - this._positionMarkerId = this.editor.deltaDecorations(this._positionMarkerId, [{ range, options: ModelDecorationOptions.EMPTY }]); + this._positionMarkerId.set([{ range, options: ModelDecorationOptions.EMPTY }]); } hide(): void { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index cba1ad5adc5a0..ee1b68b4155e3 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2529,7 +2529,7 @@ declare namespace monaco.editor { * will get the ownerId of the editor (meaning they will not show up in other editors). * These decorations will be automatically cleared when the editor's model changes. */ - createDecorationsCollection(): IEditorDecorationsCollection; + createDecorationsCollection(decorations?: IModelDeltaDecoration[]): IEditorDecorationsCollection; } /** diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts index f3ea21106491b..f1e945e1bef74 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts @@ -334,8 +334,8 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget { } if (fullRange) { this._editor.revealRangeInCenter(fullRange, ScrollType.Immediate); - const ids = this._editor.deltaDecorations([], decorations); - this._previewDisposable.add(toDisposable(() => this._editor.deltaDecorations(ids, []))); + const decorationsCollection = this._editor.createDecorationsCollection(decorations); + this._previewDisposable.add(toDisposable(() => decorationsCollection.clear())); } this._previewDisposable.add(value); diff --git a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts index 8a23eaaeb0a01..e0fffb28ca839 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts @@ -239,7 +239,7 @@ class DocumentSymbolsOutline implements IOutline { const { symbol } = entry; this._editor.revealRangeInCenterIfOutsideViewport(symbol.range, ScrollType.Smooth); - const ids = this._editor.deltaDecorations([], [{ + const decorationsCollection = this._editor.createDecorationsCollection([{ range: symbol.range, options: { description: 'document-symbols-outline-range-highlight', @@ -247,7 +247,7 @@ class DocumentSymbolsOutline implements IOutline { isWholeLine: true } }]); - return toDisposable(() => this._editor.deltaDecorations(ids, [])); + return toDisposable(() => decorationsCollection.clear()); } captureViewState(): IDisposable { diff --git a/src/vs/workbench/contrib/comments/browser/commentGlyphWidget.ts b/src/vs/workbench/contrib/comments/browser/commentGlyphWidget.ts index 7be06df3a41a4..563ce8a4c9e4a 100644 --- a/src/vs/workbench/contrib/comments/browser/commentGlyphWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentGlyphWidget.ts @@ -10,6 +10,7 @@ import { IModelDecorationOptions, OverviewRulerLane } from 'vs/editor/common/mod import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; +import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; const overviewRulerDefault = new Color(new RGBA(197, 197, 197, 1)); @@ -19,12 +20,13 @@ export class CommentGlyphWidget { public static description = 'comment-glyph-widget'; private _lineNumber!: number; private _editor: ICodeEditor; - private commentsDecorations: string[] = []; + private readonly _commentsDecorations: IEditorDecorationsCollection; private _commentsOptions: ModelDecorationOptions; constructor(editor: ICodeEditor, lineNumber: number) { this._commentsOptions = this.createDecorationOptions(); this._editor = editor; + this._commentsDecorations = this._editor.createDecorationsCollection(); this.setLineNumber(lineNumber); } @@ -52,13 +54,11 @@ export class CommentGlyphWidget { options: this._commentsOptions }]; - this.commentsDecorations = this._editor.deltaDecorations(this.commentsDecorations, commentsDecorations); + this._commentsDecorations.set(commentsDecorations); } getPosition(): IContentWidgetPosition { - const range = this._editor.hasModel() && this.commentsDecorations && this.commentsDecorations.length - ? this._editor.getModel().getDecorationRange(this.commentsDecorations[0]) - : null; + const range = (this._commentsDecorations.length > 0 ? this._commentsDecorations.getRange(0) : null); return { position: { @@ -70,8 +70,6 @@ export class CommentGlyphWidget { } dispose() { - if (this.commentsDecorations) { - this._editor.deltaDecorations(this.commentsDecorations, []); - } + this._commentsDecorations.clear(); } } diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index fa9db7ee6603e..2d6e03e1c6078 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -241,8 +241,10 @@ class CommentingRangeDecorator { }); } - this.decorationIds = editor.deltaDecorations(this.decorationIds, commentingRangeDecorations); - commentingRangeDecorations.forEach((decoration, index) => decoration.id = this.decorationIds[index]); + editor.changeDecorations((accessor) => { + this.decorationIds = accessor.deltaDecorations(this.decorationIds, commentingRangeDecorations); + commentingRangeDecorations.forEach((decoration, index) => decoration.id = this.decorationIds[index]); + }); const rangesDifference = this.commentingRangeDecorations.length - commentingRangeDecorations.length; this.commentingRangeDecorations = commentingRangeDecorations; diff --git a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts index b858ddb06596b..b09f8c1157cee 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts @@ -109,7 +109,7 @@ export function createDecorationsForStackFrame(stackFrame: IStackFrame, isFocuse export class CallStackEditorContribution implements IEditorContribution { private toDispose: IDisposable[] = []; - private decorationIds: string[] = []; + private decorations = this.editor.createDecorationsCollection(); constructor( private readonly editor: ICodeEditor, @@ -117,7 +117,7 @@ export class CallStackEditorContribution implements IEditorContribution { @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @ILogService private readonly logService: ILogService, ) { - const setDecorations = () => this.decorationIds = this.editor.deltaDecorations(this.decorationIds, this.createCallStackDecorations()); + const setDecorations = () => this.decorations.set(this.createCallStackDecorations()); this.toDispose.push(Event.any(this.debugService.getViewModel().onDidFocusStackFrame, this.debugService.getModel().onDidChangeCallStack)(() => { setDecorations(); })); @@ -170,7 +170,7 @@ export class CallStackEditorContribution implements IEditorContribution { } dispose(): void { - this.editor.deltaDecorations(this.decorationIds, []); + this.decorations.clear(); this.toDispose = dispose(this.toDispose); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 848242efad649..98bcc2d1d6fc9 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -221,7 +221,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { private configurationWidget: FloatingClickWidget | undefined; private altListener: IDisposable | undefined; private altPressed = false; - private oldDecorations: string[] = []; + private oldDecorations = this.editor.createDecorationsCollection(); private readonly debounceInfo: IFeatureDebounceInformation; constructor( @@ -605,7 +605,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { private get removeInlineValuesScheduler(): RunOnceScheduler { return new RunOnceScheduler( () => { - this.oldDecorations = this.editor.deltaDecorations(this.oldDecorations, []); + this.oldDecorations.clear(); }, 100 ); @@ -759,7 +759,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { decoration => `${decoration.range.startLineNumber}:${decoration?.options.after?.content}`); } - this.oldDecorations = this.editor.deltaDecorations(this.oldDecorations, allDecorations); + this.oldDecorations.set(allDecorations); } dispose(): void { @@ -771,6 +771,6 @@ export class DebugEditorContribution implements IDebugEditorContribution { } this.toDispose = dispose(this.toDispose); - this.oldDecorations = this.editor.deltaDecorations(this.oldDecorations, []); + this.oldDecorations.clear(); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 66626216c5083..21d65a90cb30b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -77,7 +77,7 @@ export class DebugHoverWidget implements IContentWidget { private tree!: AsyncDataTree; private showAtPosition: Position | null; private positionPreference: ContentWidgetPositionPreference[]; - private highlightDecorations: string[]; + private readonly highlightDecorations = this.editor.createDecorationsCollection(); private complexValueContainer!: HTMLElement; private complexValueTitle!: HTMLElement; private valueContainer!: HTMLElement; @@ -96,7 +96,6 @@ export class DebugHoverWidget implements IContentWidget { this._isVisible = false; this.showAtPosition = null; - this.highlightDecorations = []; this.positionPreference = [ContentWidgetPositionPreference.ABOVE, ContentWidgetPositionPreference.BELOW]; } @@ -264,7 +263,7 @@ export class DebugHoverWidget implements IContentWidget { } if (rng) { - this.highlightDecorations = this.editor.deltaDecorations(this.highlightDecorations, [{ + this.highlightDecorations.set([{ range: rng, options: DebugHoverWidget._HOVER_HIGHLIGHT_DECORATION_OPTIONS }]); @@ -351,8 +350,7 @@ export class DebugHoverWidget implements IContentWidget { this.editor.focus(); } this._isVisible = false; - this.editor.deltaDecorations(this.highlightDecorations, []); - this.highlightDecorations = []; + this.highlightDecorations.clear(); this.editor.layoutContentWidget(this); this.positionPreference = [ContentWidgetPositionPreference.ABOVE, ContentWidgetPositionPreference.BELOW]; } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index 11b51e17e2fde..d602602277f40 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -241,16 +241,18 @@ export abstract class BaseCellViewModel extends Disposable { writeTransientState(editor.getModel(), this._editorTransientState, this._codeEditorService); } - this._resolvedDecorations.forEach((value, key) => { - if (key.startsWith('_lazy_')) { - // lazy ones - const ret = this._textEditor!.deltaDecorations([], [value.options]); - this._resolvedDecorations.get(key)!.id = ret[0]; - } - else { - const ret = this._textEditor!.deltaDecorations([], [value.options]); - this._resolvedDecorations.get(key)!.id = ret[0]; - } + this._textEditor.changeDecorations((accessor) => { + this._resolvedDecorations.forEach((value, key) => { + if (key.startsWith('_lazy_')) { + // lazy ones + const ret = accessor.addDecoration(value.options.range, value.options.options); + this._resolvedDecorations.get(key)!.id = ret; + } + else { + const ret = accessor.addDecoration(value.options.range, value.options.options); + this._resolvedDecorations.get(key)!.id = ret; + } + }); }); this._editorListeners.push(this._textEditor.onDidChangeCursorSelection(() => { this._onDidChangeState.fire({ selectionChanged: true }); })); @@ -263,12 +265,14 @@ export abstract class BaseCellViewModel extends Disposable { this.saveViewState(); this.saveTransientState(); // decorations need to be cleared first as editors can be resued. - this._resolvedDecorations.forEach(value => { - const resolvedid = value.id; - - if (resolvedid) { - this._textEditor?.deltaDecorations([resolvedid], []); - } + this._textEditor?.changeDecorations((accessor) => { + this._resolvedDecorations.forEach(value => { + const resolvedid = value.id; + + if (resolvedid) { + accessor.removeDecoration(resolvedid); + } + }); }); this._textEditor = undefined; @@ -332,16 +336,21 @@ export abstract class BaseCellViewModel extends Disposable { return decorationId; } - const result = this._textEditor.deltaDecorations([], [decoration]); - this._resolvedDecorations.set(result[0], { id: result[0], options: decoration }); - return result[0]; + let id: string; + this._textEditor.changeDecorations((accessor) => { + id = accessor.addDecoration(decoration.range, decoration.options); + this._resolvedDecorations.set(id, { id, options: decoration }); + }); + return id!; } removeModelDecoration(decorationId: string) { const realDecorationId = this._resolvedDecorations.get(decorationId); if (this._textEditor && realDecorationId && realDecorationId.id !== undefined) { - this._textEditor.deltaDecorations([realDecorationId.id!], []); + this._textEditor.changeDecorations((accessor) => { + accessor.removeDecoration(realDecorationId.id!); + }); } // lastly, remove all the cache diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts index 814f9e1282aa6..e69f1504400ad 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts @@ -163,7 +163,7 @@ export class KeybindingWidgetRenderer extends Disposable { export class KeybindingEditorDecorationsRenderer extends Disposable { private _updateDecorations: RunOnceScheduler; - private _dec: string[] = []; + private readonly _dec = this._editor.createDecorationsCollection(); constructor( private _editor: ICodeEditor, @@ -178,7 +178,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable { this._register(this._keybindingService.onDidUpdateKeybindings((e) => this._updateDecorations.schedule())); this._register({ dispose: () => { - this._dec = this._editor.deltaDecorations(this._dec, []); + this._dec.clear(); this._updateDecorations.cancel(); } }); @@ -201,7 +201,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable { } } - this._dec = this._editor.deltaDecorations(this._dec, newDecorations); + this._dec.set(newDecorations); } private _getDecorationForEntry(model: ITextModel, entry: Node): IModelDeltaDecoration | null { diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index 606a925588443..3bb3a346bd33b 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -668,7 +668,7 @@ class UnsupportedSettingsRenderer extends Disposable implements languages.CodeAc class WorkspaceConfigurationRenderer extends Disposable { private static readonly supportedKeys = ['folders', 'tasks', 'launch', 'extensions', 'settings', 'remoteAuthority', 'transient']; - private decorationIds: string[] = []; + private readonly decorations = this.editor.createDecorationsCollection(); private renderingDelayer: Delayer = new Delayer(200); constructor(private editor: ICodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel, @@ -697,7 +697,7 @@ class WorkspaceConfigurationRenderer extends Disposable { } } } - this.decorationIds = this.editor.deltaDecorations(this.decorationIds, ranges.map(range => this.createDecoration(range))); + this.decorations.set(ranges.map(range => this.createDecoration(range))); } if (markerData.length) { this.markerService.changeOne('WorkspaceConfigurationRenderer', this.workspaceSettingsEditorModel.uri, markerData); @@ -721,7 +721,7 @@ class WorkspaceConfigurationRenderer extends Disposable { override dispose(): void { this.markerService.remove('WorkspaceConfigurationRenderer', [this.workspaceSettingsEditorModel.uri]); - this.decorationIds = this.editor.deltaDecorations(this.decorationIds, []); + this.decorations.clear(); super.dispose(); } } diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 4c942330f520b..f74ec72414cd4 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -518,14 +518,13 @@ export class EditPreferenceWidget extends Disposable { private _line: number = -1; private _preferences: T[] = []; - private _editPreferenceDecoration: string[]; + private readonly _editPreferenceDecoration = this.editor.createDecorationsCollection(); private readonly _onClick = this._register(new Emitter()); readonly onClick: Event = this._onClick.event; constructor(private editor: ICodeEditor) { super(); - this._editPreferenceDecoration = []; this._register(this.editor.onMouseDown((e: IEditorMouseEvent) => { if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || e.target.detail.isAfterLines || !this.isVisible()) { return; @@ -560,11 +559,11 @@ export class EditPreferenceWidget extends Disposable { endColumn: 1 } }); - this._editPreferenceDecoration = this.editor.deltaDecorations(this._editPreferenceDecoration, newDecoration); + this._editPreferenceDecoration.set(newDecoration); } hide(): void { - this._editPreferenceDecoration = this.editor.deltaDecorations(this._editPreferenceDecoration, []); + this._editPreferenceDecoration.clear(); } isVisible(): boolean { diff --git a/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts b/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts index dca7e901bce8d..d0956de545709 100644 --- a/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts +++ b/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts @@ -329,8 +329,8 @@ export class TypeHierarchyTreePeekWidget extends peekView.PeekViewWidget { } if (fullRange) { this._editor.revealRangeInCenter(fullRange, ScrollType.Immediate); - const ids = this._editor.deltaDecorations([], decorations); - this._previewDisposable.add(toDisposable(() => this._editor.deltaDecorations(ids, []))); + const decorationsCollection = this._editor.createDecorationsCollection(decorations); + this._previewDisposable.add(toDisposable(() => decorationsCollection.clear())); } this._previewDisposable.add(value); From 612af7602ac6c33882f3e3639cd4c86b788db287 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 17 May 2022 15:15:10 -0700 Subject: [PATCH 125/942] Sort used debug extensions to the top when clicking "Run and Debug". Towards #146338 (#149773) --- .../debug/browser/debugAdapterManager.ts | 29 ++++++++++++++----- .../browser/debugConfigurationManager.ts | 2 +- .../contrib/debug/browser/debugService.ts | 2 +- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts index 7f9e2de3788b6..7a9ec3bbb3dbf 100644 --- a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts @@ -34,6 +34,10 @@ import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecyc const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); +export interface IAdapterManagerDelegate { + onDidNewSession: Event; +} + export class AdapterManager extends Disposable implements IAdapterManager { private debuggers: Debugger[]; @@ -49,7 +53,10 @@ export class AdapterManager extends Disposable implements IAdapterManager { /** Extensions that were already active before any debugger activation events */ private earlyActivatedExtensions: Set | undefined; + private usedDebugTypes = new Set(); + constructor( + delegate: IAdapterManagerDelegate, @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @IQuickInputService private readonly quickInputService: IQuickInputService, @@ -59,7 +66,7 @@ export class AdapterManager extends Disposable implements IAdapterManager { @IContextKeyService private readonly contextKeyService: IContextKeyService, @ILanguageService private readonly languageService: ILanguageService, @IDialogService private readonly dialogService: IDialogService, - @ILifecycleService private readonly lifecycleService: ILifecycleService, + @ILifecycleService private readonly lifecycleService: ILifecycleService ) { super(); this.adapterDescriptorFactories = []; @@ -79,6 +86,10 @@ export class AdapterManager extends Disposable implements IAdapterManager { })); this.lifecycleService.when(LifecyclePhase.Eventually) .then(() => this.debugExtensionsAvailable.set(this.debuggers.length > 0)); // If no extensions with a debugger contribution are loaded + + this._register(delegate.onDidNewSession(s => { + this.usedDebugTypes.add(s.configuration.type); + })); } private registerListeners(): void { @@ -287,18 +298,18 @@ export class AdapterManager extends Disposable implements IAdapterManager { return this.debuggers.find(dbg => strings.equalsIgnoreCase(dbg.type, type)); } + getEnabledDebugger(type: string): Debugger | undefined { + const adapter = this.getDebugger(type); + return adapter && adapter.enabled ? adapter : undefined; + } + isDebuggerInterestedInLanguage(language: string): boolean { return !!this.debuggers .filter(d => d.enabled) .find(a => language && a.languages && a.languages.indexOf(language) >= 0); } - async guessDebugger(gettingConfigurations: boolean, type?: string): Promise { - if (type) { - const adapter = this.getDebugger(type); - return adapter && adapter.enabled ? adapter : undefined; - } - + async guessDebugger(gettingConfigurations: boolean): Promise { const activeTextEditorControl = this.editorService.activeTextEditorControl; let candidates: Debugger[] = []; let languageLabel: string | null = null; @@ -351,6 +362,10 @@ export class AdapterManager extends Disposable implements IAdapterManager { candidates.forEach(d => { const descriptor = d.getMainExtensionDescriptor(); if (descriptor.id && !!this.earlyActivatedExtensions?.has(descriptor.id)) { + // Was activated early + suggestedCandidates.push(d); + } else if (this.usedDebugTypes.has(d.type)) { + // Was used already suggestedCandidates.push(d); } else { otherCandidates.push(d); diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index e8ed18ca485e4..0245dbd5a2d05 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -523,7 +523,7 @@ abstract class AbstractLaunch { async getInitialConfigurationContent(folderUri?: uri, type?: string, token?: CancellationToken): Promise { let content = ''; - const adapter = await this.adapterManager.guessDebugger(true, type); + const adapter = type ? this.adapterManager.getEnabledDebugger(type) : await this.adapterManager.guessDebugger(true); if (adapter) { const initialConfigs = await this.configurationManager.provideDebugConfigurations(folderUri, adapter.type, token || CancellationToken.None); content = await adapter.getInitialConfigurationContent(initialConfigs); diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 224b72fe4fa85..50c7f78b6b2bb 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -114,7 +114,7 @@ export class DebugService implements IDebugService { this._onWillNewSession = new Emitter(); this._onDidEndSession = new Emitter(); - this.adapterManager = this.instantiationService.createInstance(AdapterManager); + this.adapterManager = this.instantiationService.createInstance(AdapterManager, { onDidNewSession: this.onDidNewSession }); this.disposables.add(this.adapterManager); this.configurationManager = this.instantiationService.createInstance(ConfigurationManager, this.adapterManager); this.disposables.add(this.configurationManager); From cf189907bbfa66a99bc1dd5b58f346b12b867449 Mon Sep 17 00:00:00 2001 From: Anthony Stewart <150152+a-stewart@users.noreply.github.com> Date: Wed, 18 May 2022 01:32:39 +0200 Subject: [PATCH 126/942] Workaround for the webview positioning bug (#137506) * Basic fix for the webview positioning bug * Reduce to 25fps for less load * Fixing typo in comment * Removing interval to make the change look animated * Running formatter for hygene * Fixing typo Co-authored-by: Anthony Stewart Co-authored-by: Matt Bierner --- .../contrib/webview/browser/overlayWebview.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts index d9239b64135d0..057e5e10d4d5e 100644 --- a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts +++ b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts @@ -29,6 +29,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { private _html: string = ''; private _initialScrollProgress: number = 0; private _state: string | undefined = undefined; + private _repositionTimeout: any | undefined = undefined; private _extension: WebviewExtensionDescription | undefined; private _contentOptions: WebviewContentOptions; @@ -76,6 +77,8 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { } this._firstLoadPendingMessages.clear(); + clearTimeout(this._repositionTimeout); + this._onDidDispose.fire(); super.dispose(); @@ -144,6 +147,17 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { } public layoutWebviewOverElement(element: HTMLElement, dimension?: Dimension, clippingContainer?: HTMLElement) { + this.doLayoutWebviewOverElement(element, dimension, clippingContainer); + + // Temporary fix for https://github.com/microsoft/vscode/issues/110450 + // There is an animation that lasts about 200ms, update the webview positioning once this animation is complete. + clearTimeout(this._repositionTimeout); + this._repositionTimeout = setTimeout(() => { + this.doLayoutWebviewOverElement(element, dimension, clippingContainer); + }, 200); + } + + public doLayoutWebviewOverElement(element: HTMLElement, dimension?: Dimension, clippingContainer?: HTMLElement) { if (!this._container || !this._container.parentElement) { return; } From c9c5990e389c9480695cdb9c29683f6501c61122 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 17 May 2022 18:51:25 -0700 Subject: [PATCH 127/942] Adopt notebookWorkspaceEdit proposal internally (#149778) This switches us to use the new `notebookWorkspaceEdit` proposal instead of `notebookEditorEdit` --- extensions/ipynb/package.json | 3 +-- extensions/ipynb/src/cellIdService.ts | 4 ++-- extensions/ipynb/src/ipynbMain.ts | 4 ++-- extensions/ipynb/tsconfig.json | 2 +- extensions/vscode-notebook-tests/package.json | 1 - 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index c8feabc17af4f..32802630b847d 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -9,8 +9,7 @@ "vscode": "^1.57.0" }, "enabledApiProposals": [ - "notebookEditor", - "notebookEditorEdit" + "notebookEditor" ], "activationEvents": [ "*" diff --git a/extensions/ipynb/src/cellIdService.ts b/extensions/ipynb/src/cellIdService.ts index 082d25add9730..ddda0a9fd5fa4 100644 --- a/extensions/ipynb/src/cellIdService.ts +++ b/extensions/ipynb/src/cellIdService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionContext, NotebookDocument, NotebookDocumentChangeEvent, workspace, WorkspaceEdit } from 'vscode'; +import { ExtensionContext, NotebookDocument, NotebookDocumentChangeEvent, NotebookEdit, workspace, WorkspaceEdit } from 'vscode'; import { v4 as uuid } from 'uuid'; import { getCellMetadata } from './serializers'; import { CellMetadata } from './common'; @@ -34,7 +34,7 @@ function onDidChangeNotebookCells(e: NotebookDocumentChangeEvent) { // Don't edit the metadata directly, always get a clone (prevents accidental singletons and directly editing the objects). const updatedMetadata: CellMetadata = { ...JSON.parse(JSON.stringify(cellMetadata || {})) }; updatedMetadata.id = id; - edit.replaceNotebookCellMetadata(cell.notebook.uri, cell.index, { ...(cell.metadata), custom: updatedMetadata }); + edit.set(cell.notebook.uri, [NotebookEdit.updateCellMetadata(cell.index, { ...(cell.metadata), custom: updatedMetadata })]); workspace.applyEdit(edit); }); }); diff --git a/extensions/ipynb/src/ipynbMain.ts b/extensions/ipynb/src/ipynbMain.ts index c2c7044bd7795..33bb1456af8ad 100644 --- a/extensions/ipynb/src/ipynbMain.ts +++ b/extensions/ipynb/src/ipynbMain.ts @@ -94,7 +94,7 @@ export function activate(context: vscode.ExtensionContext) { } const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookMetadata(resource, { + edit.set(resource, [vscode.NotebookEdit.updateNotebookMetadata({ ...document.metadata, custom: { ...(document.metadata.custom ?? {}), @@ -103,7 +103,7 @@ export function activate(context: vscode.ExtensionContext) { ...metadata }, } - }); + })]); return vscode.workspace.applyEdit(edit); }, }; diff --git a/extensions/ipynb/tsconfig.json b/extensions/ipynb/tsconfig.json index 178e86493b456..918db6ea6c258 100644 --- a/extensions/ipynb/tsconfig.json +++ b/extensions/ipynb/tsconfig.json @@ -10,6 +10,6 @@ "src/**/*", "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.notebookEditor.d.ts", - "../../src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts" + "../../src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts" ] } diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json index 8792cd4e6fb1a..8bb9eb8d3b354 100644 --- a/extensions/vscode-notebook-tests/package.json +++ b/extensions/vscode-notebook-tests/package.json @@ -17,7 +17,6 @@ "notebookDeprecated", "notebookEditor", "notebookEditorDecorationType", - "notebookEditorEdit", "notebookLiveShare", "notebookMessaging", "notebookMime" From 6fef251f8f0e9f74b7adb4ed310e35eabb97afdf Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 17 May 2022 19:03:46 -0700 Subject: [PATCH 128/942] Show the debug adapter label when hovering the type in launch.json (#149786) --- src/vs/workbench/contrib/debug/common/debugger.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/debug/common/debugger.ts b/src/vs/workbench/contrib/debug/common/debugger.ts index 8a41ba6c6032f..531f7f789cc0c 100644 --- a/src/vs/workbench/contrib/debug/common/debugger.ts +++ b/src/vs/workbench/contrib/debug/common/debugger.ts @@ -225,6 +225,7 @@ export class Debugger implements IDebugger { const properties = attributes.properties; properties['type'] = { enum: [this.type], + enumDescriptions: [this.label], description: nls.localize('debugType', "Type of configuration."), pattern: '^(?!node2)', deprecationMessage: this.enabled ? undefined : debuggerDisabledMessage(this.type), From 688c80245936b49b7ceca494d9edfe97cae06f8a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 17 May 2022 20:50:42 -0700 Subject: [PATCH 129/942] Fix lazy button layout in debug hover. (#149789) Fix #149638 --- .../browser/media/debug.contribution.css | 23 +++++++++++-------- .../debug/browser/media/debugViewlet.css | 4 ---- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css index 577a80c532ea7..c061b736c55b0 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css @@ -70,6 +70,12 @@ /* Expressions */ + +.monaco-workbench .debug-pane .monaco-list-row .expression, +.monaco-workbench .debug-hover-widget .monaco-list-row .expression { + display: flex; +} + .monaco-workbench .debug-pane .monaco-list-row .expression, .monaco-workbench .debug-hover-widget .monaco-list-row .expression { font-size: 13px; @@ -90,6 +96,13 @@ .monaco-workbench .monaco-list-row .expression .lazy-button { margin-left: 3px; + display: none; + border-radius: 5px; + align-self: center; +} + +.monaco-workbench .monaco-list-row .expression.lazy .lazy-button { + display: inline; } /* Links */ @@ -120,16 +133,6 @@ font-style: italic; } -.monaco-workbench .monaco-list-row .expression .lazy-button { - display: none; - border-radius: 5px; - padding: 3px; -} - -.monaco-workbench .monaco-list-row .expression.lazy .lazy-button { - display: inline; -} - .monaco-workbench .debug-inline-value { background-color: var(--vscode-editor-inlineValuesBackground); color: var(--vscode-editor-inlineValuesForeground); diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index 493c9270e853f..57a946206a853 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -216,10 +216,6 @@ font-size: 11px; } -.debug-pane .monaco-list-row .expression { - display: flex; -} - .debug-pane .monaco-list-row .expression .actionbar-spacer { flex-grow: 1; } From 95b8607540917934d215aa6e2dc8e74cf4db8ad3 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 17 May 2022 21:46:44 -0700 Subject: [PATCH 130/942] load source commands --- src/vs/platform/actions/common/actions.ts | 1 + .../editorStatusBar/editorStatusBar.ts | 159 +++++++++++------- .../browser/notebookKernelServiceImpl.ts | 50 ++++++ .../viewParts/notebookKernelActionViewItem.ts | 21 ++- .../notebook/common/notebookKernelService.ts | 6 + .../actions/common/menusExtensionPoint.ts | 5 + 6 files changed, 174 insertions(+), 68 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index e97c241b95c38..93c7c897b139a 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -140,6 +140,7 @@ export class MenuId { static readonly NotebookDiffCellOutputsTitle = new MenuId('NotebookDiffCellOutputsTitle'); static readonly NotebookOutputToolbar = new MenuId('NotebookOutputToolbar'); static readonly NotebookEditorLayoutConfigure = new MenuId('NotebookEditorLayoutConfigure'); + static readonly NotebookKernelSource = new MenuId('NotebookKernelSource'); static readonly BulkEditTitle = new MenuId('BulkEditTitle'); static readonly BulkEditContext = new MenuId('BulkEditContext'); static readonly TimelineItemContext = new MenuId('TimelineItemContext'); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index 22233100d5e3e..a62de5e8e6590 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IAction } from 'vs/base/common/actions'; import { groupBy } from 'vs/base/common/arrays'; import { Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; @@ -157,86 +158,114 @@ registerAction2(class extends Action2 { } } - if (!newKernel) { - type KernelPick = IQuickPickItem & { kernel: INotebookKernel }; - const configButton: IQuickInputButton = { - iconClass: ThemeIcon.asClassName(configureKernelIcon), - tooltip: nls.localize('notebook.promptKernel.setDefaultTooltip', "Set as default for '{0}' notebooks", editor.textModel.viewType) + if (newKernel) { + notebookKernelService.selectKernelForNotebook(newKernel, notebook); + return true; + } + + type KernelPick = IQuickPickItem & { kernel: INotebookKernel }; + type SourcePick = IQuickPickItem & { action: IAction }; + + const configButton: IQuickInputButton = { + iconClass: ThemeIcon.asClassName(configureKernelIcon), + tooltip: nls.localize('notebook.promptKernel.setDefaultTooltip', "Set as default for '{0}' notebooks", editor.textModel.viewType) + }; + function toQuickPick(kernel: INotebookKernel) { + const res = { + kernel, + picked: kernel.id === selected?.id, + label: kernel.label, + description: kernel.description, + detail: kernel.detail, + buttons: [configButton] }; - function toQuickPick(kernel: INotebookKernel) { - const res = { - kernel, - picked: kernel.id === selected?.id, - label: kernel.label, - description: kernel.description, - detail: kernel.detail, - buttons: [configButton] - }; - if (kernel.id === selected?.id) { - if (!res.description) { - res.description = nls.localize('current1', "Currently Selected"); - } else { - res.description = nls.localize('current2', "{0} - Currently Selected", res.description); - } + if (kernel.id === selected?.id) { + if (!res.description) { + res.description = nls.localize('current1', "Currently Selected"); + } else { + res.description = nls.localize('current2', "{0} - Currently Selected", res.description); } - return res; } - const quickPickItems: QuickPickInput[] = []; - if (all.length) { - // Always display suggested kernels on the top. - if (suggestions.length) { - quickPickItems.push({ - type: 'separator', - label: nls.localize('suggestedKernels', "Suggested") - }); - quickPickItems.push(...suggestions.map(toQuickPick)); - } - - // Next display all of the kernels grouped by categories or extensions. - // If we don't have a kind, always display those at the bottom. - const picks = all.filter(item => !suggestions.includes(item)).map(toQuickPick); - const kernelsPerCategory = groupBy(picks, (a, b) => compareIgnoreCase(a.kernel.kind || 'z', b.kernel.kind || 'z')); - kernelsPerCategory.forEach(items => { - quickPickItems.push({ - type: 'separator', - label: items[0].kernel.kind || nls.localize('otherKernelKinds', "Other") - }); - quickPickItems.push(...items); + return res; + } + const quickPickItems: QuickPickInput[] = []; + if (all.length) { + // Always display suggested kernels on the top. + if (suggestions.length) { + quickPickItems.push({ + type: 'separator', + label: nls.localize('suggestedKernels', "Suggested") }); + quickPickItems.push(...suggestions.map(toQuickPick)); } - if (!all.length) { - // there is no kernel, show the install from marketplace + // Next display all of the kernels grouped by categories or extensions. + // If we don't have a kind, always display those at the bottom. + const picks = all.filter(item => !suggestions.includes(item)).map(toQuickPick); + const kernelsPerCategory = groupBy(picks, (a, b) => compareIgnoreCase(a.kernel.kind || 'z', b.kernel.kind || 'z')); + kernelsPerCategory.forEach(items => { quickPickItems.push({ - id: 'install', - label: nls.localize('installKernels', "Install kernels from the marketplace"), + type: 'separator', + label: items[0].kernel.kind || nls.localize('otherKernelKinds', "Other") }); - } + quickPickItems.push(...items); + }); + } - const pick = await quickInputService.pick(quickPickItems, { - placeHolder: selected - ? nls.localize('prompt.placeholder.change', "Change kernel for '{0}'", labelService.getUriLabel(notebook.uri, { relative: true })) - : nls.localize('prompt.placeholder.select', "Select kernel for '{0}'", labelService.getUriLabel(notebook.uri, { relative: true })), - onDidTriggerItemButton: (context) => { - if ('kernel' in context.item) { - notebookKernelService.selectKernelForNotebookType(context.item.kernel, notebook.viewType); - } - } + if (!all.length) { + // there is no kernel, show the install from marketplace + quickPickItems.push({ + id: 'install', + label: nls.localize('installKernels', "Install kernels from the marketplace"), }); + } + + const sourceActions = notebookKernelService.getSourceActions(); + if (sourceActions.length) { + quickPickItems.push({ + type: 'separator', + // label: nls.localize('sourceActions', "") + }); + + sourceActions.forEach(action => { + const res = { + action, + picked: false, + label: action.label, + }; - if (pick) { - if (pick.id === 'install') { - await this._showKernelExtension(paneCompositeService, notebook.viewType); - } else if ('kernel' in pick) { - newKernel = pick.kernel; + quickPickItems.push(res); + }); + } + + const pick = await quickInputService.pick(quickPickItems, { + placeHolder: selected + ? nls.localize('prompt.placeholder.change', "Change kernel for '{0}'", labelService.getUriLabel(notebook.uri, { relative: true })) + : nls.localize('prompt.placeholder.select', "Select kernel for '{0}'", labelService.getUriLabel(notebook.uri, { relative: true })), + onDidTriggerItemButton: (context) => { + if ('kernel' in context.item) { + notebookKernelService.selectKernelForNotebookType(context.item.kernel, notebook.viewType); } } - } + }); - if (newKernel) { - notebookKernelService.selectKernelForNotebook(newKernel, notebook); - return true; + if (pick) { + if ('kernel' in pick) { + newKernel = pick.kernel; + notebookKernelService.selectKernelForNotebook(newKernel, notebook); + return true; + } + + // actions + + if (pick.id === 'install') { + await this._showKernelExtension(paneCompositeService, notebook.viewType); + } else if ('action' in pick) { + // selected explicilty, it should trigger the execution? + notebookKernelService.runSourceAction(pick.action); + } } + return false; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts index 3a14f27d0ff75..6e0151f222c2b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts @@ -12,6 +12,9 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { URI } from 'vs/base/common/uri'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { runWhenIdle } from 'vs/base/common/async'; +import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IAction } from 'vs/base/common/actions'; class KernelInfo { @@ -56,18 +59,25 @@ export class NotebookKernelService extends Disposable implements INotebookKernel private readonly _onDidAddKernel = this._register(new Emitter()); private readonly _onDidRemoveKernel = this._register(new Emitter()); private readonly _onDidChangeNotebookAffinity = this._register(new Emitter()); + private readonly _onDidChangeSourceActions = this._register(new Emitter()); + private readonly _sourceMenu: IMenu; + private _sourceActions: IAction[]; readonly onDidChangeSelectedNotebooks: Event = this._onDidChangeNotebookKernelBinding.event; readonly onDidAddKernel: Event = this._onDidAddKernel.event; readonly onDidRemoveKernel: Event = this._onDidRemoveKernel.event; readonly onDidChangeNotebookAffinity: Event = this._onDidChangeNotebookAffinity.event; + readonly onDidChangeSourceActions: Event = this._onDidChangeSourceActions.event; private static _storageNotebookBinding = 'notebook.controller2NotebookBindings'; private static _storageTypeBinding = 'notebook.controller2TypeBindings'; + constructor( @INotebookService private readonly _notebookService: INotebookService, @IStorageService private readonly _storageService: IStorageService, + @IMenuService readonly _menuService: IMenuService, + @IContextKeyService contextKeyService: IContextKeyService ) { super(); @@ -80,6 +90,10 @@ export class NotebookKernelService extends Disposable implements INotebookKernel this._onDidChangeNotebookKernelBinding.fire({ notebook: notebook.uri, oldKernel: kernelId, newKernel: undefined }); } })); + this._sourceMenu = this._register(this._menuService.createMenu(MenuId.NotebookKernelSource, contextKeyService)); + this._sourceActions = []; + + this._initSourceActions(); // restore from storage try { @@ -96,6 +110,24 @@ export class NotebookKernelService extends Disposable implements INotebookKernel } } + private _initSourceActions() { + const loadActions = (menu: IMenu) => { + const groups = menu.getActions({ shouldForwardArgs: true }); + const actions: IAction[] = []; + groups.forEach(group => { + actions.push(...group[1]); + }); + this._sourceActions = actions; + this._onDidChangeSourceActions.fire(); + }; + + this._register(this._sourceMenu.onDidChange(() => { + loadActions(this._sourceMenu); + })); + + loadActions(this._sourceMenu); + } + override dispose() { this._kernels.clear(); super.dispose(); @@ -255,4 +287,22 @@ export class NotebookKernelService extends Disposable implements INotebookKernel } this._onDidChangeNotebookAffinity.fire(); } + + private _runningAction: IAction | undefined = undefined; + + getRunningSourceAction() { + return this._runningAction; + } + + getSourceActions(): IAction[] { + return this._sourceActions; + } + + async runSourceAction(action: IAction): Promise { + this._runningAction = action; + this._onDidChangeSourceActions.fire(); + await action.run(); + this._runningAction = undefined; + this._onDidChangeSourceActions.fire(); + } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts index 69917ca972b6f..4f23f001c3a22 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts @@ -9,7 +9,7 @@ import { Action, IAction } from 'vs/base/common/actions'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; +import { executingStateIcon, selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { INotebookKernel, INotebookKernelMatchResult, INotebookKernelService, NotebookControllerState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { Event } from 'vs/base/common/event'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; @@ -33,6 +33,7 @@ export class NotebooKernelActionViewItem extends ActionViewItem { this._register(_editor.onDidChangeModel(this._update, this)); this._register(_notebookKernelService.onDidChangeNotebookAffinity(this._update, this)); this._register(_notebookKernelService.onDidChangeSelectedNotebooks(this._update, this)); + this._register(_notebookKernelService.onDidChangeSourceActions(this._update, this)); this._kernelDisposable = this._register(new DisposableStore()); } @@ -61,8 +62,22 @@ export class NotebooKernelActionViewItem extends ActionViewItem { return; } - const info = this._notebookKernelService.getMatchingKernel(notebook); - this._updateActionFromKernelInfo(info); + const runningAction = this._notebookKernelService.getRunningSourceAction(); + if (runningAction) { + this._updateActionFromSourceAction(runningAction); + return; + } else { + this.action.class = ThemeIcon.asClassName(selectKernelIcon); + const info = this._notebookKernelService.getMatchingKernel(notebook); + this._updateActionFromKernelInfo(info); + } + } + + private _updateActionFromSourceAction(sourceAction: IAction) { + this.action.class = ThemeIcon.asClassName(ThemeIcon.modify(executingStateIcon, 'spin')); + this.updateClass(); + this._action.label = sourceAction.label; + this._action.enabled = true; } private _updateActionFromKernelInfo(info: INotebookKernelMatchResult): void { diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index 13fd479ef1ac0..e47d930372fa6 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IAction } from 'vs/base/common/actions'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; @@ -82,6 +83,7 @@ export interface INotebookKernelService { readonly onDidRemoveKernel: Event; readonly onDidChangeSelectedNotebooks: Event; readonly onDidChangeNotebookAffinity: Event; + readonly onDidChangeSourceActions: Event; registerKernel(kernel: INotebookKernel): IDisposable; @@ -115,4 +117,8 @@ export interface INotebookKernelService { */ updateKernelNotebookAffinity(kernel: INotebookKernel, notebook: URI, preference: number | undefined): void; + getSourceActions(): IAction[]; + getRunningSourceAction(): IAction | undefined; + + runSourceAction(action: IAction): Promise; } diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index 7e06c120f9562..409cfb9924c3f 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -171,6 +171,11 @@ const apiMenus: IAPIMenu[] = [ id: MenuId.NotebookToolbar, description: localize('notebook.toolbar', "The contributed notebook toolbar menu") }, + { + key: 'notebook/kernelSource', + id: MenuId.NotebookKernelSource, + description: localize('notebook.kernelSource', "The contributed notebook kernel sources menu") + }, { key: 'notebook/cell/title', id: MenuId.NotebookCellTitle, From d1f882f635fdd87f2af500dfdfed91f744af680b Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 18 May 2022 10:37:44 +0200 Subject: [PATCH 131/942] Engineering - Exploration to run tests in parallel (#149698) --- .../darwin/product-build-darwin-test.yml | 200 +++++++++------ .../linux/product-build-linux-client.yml | 228 ++++++++++------- build/azure-pipelines/product-build-pr.yml | 94 ++++++- .../win32/product-build-win32.yml | 230 +++++++++++------- scripts/test-integration.bat | 26 +- scripts/test-integration.sh | 26 +- scripts/test-remote-integration.bat | 8 +- scripts/test-remote-integration.sh | 20 +- scripts/test-web-integration.bat | 12 +- scripts/test-web-integration.sh | 14 +- 10 files changed, 540 insertions(+), 318 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin-test.yml b/build/azure-pipelines/darwin/product-build-darwin-test.yml index 773b5a40845dd..dd495426b6dc4 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-test.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-test.yml @@ -1,6 +1,15 @@ parameters: - name: VSCODE_QUALITY type: string + - name: VSCODE_RUN_UNIT_TESTS + type: boolean + default: true + - name: VSCODE_RUN_INTEGRATION_TESTS + type: boolean + default: true + - name: VSCODE_RUN_SMOKE_TESTS + type: boolean + default: true steps: - task: NodeTool@0 @@ -165,91 +174,126 @@ steps: VSCODE_ARCH=$(VSCODE_ARCH) DEBUG=electron-osx-sign* node build/darwin/sign.js displayName: Set Hardened Entitlements - - script: | - set -e - ./scripts/test.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 - - - script: | - set -e - yarn test-node --build - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 - - - script: | - set -e - DEBUG=*browser* yarn test-browser-no-install --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser, Chromium & Webkit) - timeoutInMinutes: 30 - - - script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ - ./scripts/test-integration.sh --build --tfs "Integration Tests" - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 + - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: + - script: | + set -e + ./scripts/test.sh --build --tfs "Unit Tests" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 - - script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ - ./scripts/test-web-integration.sh --browser webkit - displayName: Run integration tests (Browser, Webkit) - timeoutInMinutes: 20 + - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: + - script: | + set -e + yarn test-node --build + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 - - script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ - ./scripts/test-remote-integration.sh - displayName: Run integration tests (Remote) - timeoutInMinutes: 20 + - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: + - script: | + set -e + DEBUG=*browser* yarn test-browser-no-install --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser, Chromium & Webkit) + timeoutInMinutes: 30 - - script: | - set -e - ps -ef - displayName: Diagnostics before smoke test run - continueOnError: true - condition: succeededOrFailed() + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - script: | + set -e + yarn gulp \ + compile-extension:css-language-features-server \ + compile-extension:emmet \ + compile-extension:git \ + compile-extension:github-authentication \ + compile-extension:html-language-features-server \ + compile-extension:ipynb \ + compile-extension:json-language-features-server \ + compile-extension:markdown-language-features \ + compile-extension-media \ + compile-extension:microsoft-authentication \ + compile-extension:typescript-language-features \ + compile-extension:vscode-api-tests \ + compile-extension:vscode-colorize-tests \ + compile-extension:vscode-custom-editor-tests \ + compile-extension:vscode-notebook-tests \ + compile-extension:vscode-test-resolver + displayName: Build integration tests + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - script: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ + ./scripts/test-integration.sh --build --tfs "Integration Tests" + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 + + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ + ./scripts/test-web-integration.sh --browser webkit + displayName: Run integration tests (Browser, Webkit) + timeoutInMinutes: 20 - - script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --web --tracing --headless - timeoutInMinutes: 20 - displayName: Run smoke tests (Browser, Chromium) + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ + ./scripts/test-remote-integration.sh + displayName: Run integration tests (Remote) + timeoutInMinutes: 20 + + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + ps -ef + displayName: Diagnostics before smoke test run + continueOnError: true + condition: succeededOrFailed() - - script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - APP_NAME="`ls $APP_ROOT | head -n 1`" - yarn smoketest-no-compile --tracing --build "$APP_ROOT/$APP_NAME" - timeoutInMinutes: 20 - displayName: Run smoke tests (Electron) + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ + yarn smoketest-no-compile --web --tracing --headless + timeoutInMinutes: 20 + displayName: Run smoke tests (Browser, Chromium) - - script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - APP_NAME="`ls $APP_ROOT | head -n 1`" - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --tracing --remote --build "$APP_ROOT/$APP_NAME" - timeoutInMinutes: 20 - displayName: Run smoke tests (Remote) + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + yarn smoketest-no-compile --tracing --build "$APP_ROOT/$APP_NAME" + timeoutInMinutes: 20 + displayName: Run smoke tests (Electron) - - script: | - set -e - ps -ef - displayName: Diagnostics after smoke test run - continueOnError: true - condition: succeededOrFailed() + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + yarn gulp compile-extension:vscode-test-resolver + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ + yarn smoketest-no-compile --tracing --remote --build "$APP_ROOT/$APP_NAME" + timeoutInMinutes: 20 + displayName: Run smoke tests (Remote) + + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + ps -ef + displayName: Diagnostics after smoke test run + continueOnError: true + condition: succeededOrFailed() - task: PublishPipelineArtifact@0 inputs: diff --git a/build/azure-pipelines/linux/product-build-linux-client.yml b/build/azure-pipelines/linux/product-build-linux-client.yml index 4b82e42689cbf..4e6f8f13d0ec0 100644 --- a/build/azure-pipelines/linux/product-build-linux-client.yml +++ b/build/azure-pipelines/linux/product-build-linux-client.yml @@ -1,6 +1,15 @@ parameters: - name: VSCODE_QUALITY type: string + - name: VSCODE_RUN_UNIT_TESTS + type: boolean + default: true + - name: VSCODE_RUN_INTEGRATION_TESTS + type: boolean + default: true + - name: VSCODE_RUN_SMOKE_TESTS + type: boolean + default: true steps: - task: NodeTool@0 @@ -221,104 +230,139 @@ steps: stat $ELECTRON_ROOT/chrome-sandbox displayName: Change setuid helper binary permission - - script: | - set -e - ./scripts/test.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - set -e - yarn test-node --build - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - set -e - DEBUG=*browser* yarn test-browser-no-install --build --browser chromium --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser, Chromium) - timeoutInMinutes: 15 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") - INTEGRATION_TEST_APP_NAME="$APP_NAME" \ - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - ./scripts/test-integration.sh --build --tfs "Integration Tests" - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ - ./scripts/test-web-integration.sh --browser chromium - displayName: Run integration tests (Browser, Chromium) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: + - script: | + set -e + ./scripts/test.sh --build --tfs "Unit Tests" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") - INTEGRATION_TEST_APP_NAME="$APP_NAME" \ - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - ./scripts/test-remote-integration.sh - displayName: Run integration tests (Remote) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: + - script: | + set -e + yarn test-node --build + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - script: | - set -e - ps -ef - cat /proc/sys/fs/inotify/max_user_watches - lsof | wc -l - displayName: Diagnostics before smoke test run (processes, max_user_watches, number of opened file handles) - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: + - script: | + set -e + DEBUG=*browser* yarn test-browser-no-install --build --browser chromium --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser, Chromium) + timeoutInMinutes: 15 + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --web --tracing --headless --electronArgs="--disable-dev-shm-usage" - timeoutInMinutes: 20 - displayName: Run smoke tests (Browser, Chromium) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - script: | + set -e + yarn gulp \ + compile-extension:css-language-features-server \ + compile-extension:emmet \ + compile-extension:git \ + compile-extension:github-authentication \ + compile-extension:html-language-features-server \ + compile-extension:ipynb \ + compile-extension:json-language-features-server \ + compile-extension:markdown-language-features \ + compile-extension-media \ + compile-extension:microsoft-authentication \ + compile-extension:typescript-language-features \ + compile-extension:vscode-api-tests \ + compile-extension:vscode-colorize-tests \ + compile-extension:vscode-custom-editor-tests \ + compile-extension:vscode-notebook-tests \ + compile-extension:vscode-test-resolver + displayName: Build integration tests + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - script: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") + INTEGRATION_TEST_APP_NAME="$APP_NAME" \ + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + ./scripts/test-integration.sh --build --tfs "Integration Tests" + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ + ./scripts/test-web-integration.sh --browser chromium + displayName: Run integration tests (Browser, Chromium) + timeoutInMinutes: 20 + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - script: | - set -e - APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - yarn smoketest-no-compile --tracing --build "$APP_PATH" - timeoutInMinutes: 20 - displayName: Run smoke tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") + INTEGRATION_TEST_APP_NAME="$APP_NAME" \ + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + ./scripts/test-remote-integration.sh + displayName: Run integration tests (Remote) + timeoutInMinutes: 20 + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + ps -ef + cat /proc/sys/fs/inotify/max_user_watches + lsof | wc -l + displayName: Diagnostics before smoke test run (processes, max_user_watches, number of opened file handles) + continueOnError: true + condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ + yarn smoketest-no-compile --web --tracing --headless --electronArgs="--disable-dev-shm-usage" + timeoutInMinutes: 20 + displayName: Run smoke tests (Browser, Chromium) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - script: | - set -e - APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --tracing --remote --build "$APP_PATH" - timeoutInMinutes: 20 - displayName: Run smoke tests (Remote) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + yarn smoketest-no-compile --tracing --build "$APP_PATH" + timeoutInMinutes: 20 + displayName: Run smoke tests (Electron) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - script: | - set -e - ps -ef - cat /proc/sys/fs/inotify/max_user_watches - lsof | wc -l - displayName: Diagnostics after smoke test run (processes, max_user_watches, number of opened file handles) - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + yarn gulp compile-extension:vscode-test-resolver + APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + yarn smoketest-no-compile --tracing --remote --build "$APP_PATH" + timeoutInMinutes: 20 + displayName: Run smoke tests (Remote) + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + ps -ef + cat /proc/sys/fs/inotify/max_user_watches + lsof | wc -l + displayName: Diagnostics after smoke test run (processes, max_user_watches, number of opened file handles) + continueOnError: true + condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - task: PublishPipelineArtifact@0 inputs: diff --git a/build/azure-pipelines/product-build-pr.yml b/build/azure-pipelines/product-build-pr.yml index 21fbe295cc90a..5b09f8ee77e0d 100644 --- a/build/azure-pipelines/product-build-pr.yml +++ b/build/azure-pipelines/product-build-pr.yml @@ -60,7 +60,8 @@ stages: - Compile pool: vscode-1es-vscode-windows-2019 jobs: - - job: Windows + - job: WindowsUnitTests + displayName: Unit Tests timeoutInMinutes: 120 variables: VSCODE_ARCH: x64 @@ -68,6 +69,33 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: true + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false + - job: WindowsIntegrationTests + displayName: Integration Tests + timeoutInMinutes: 120 + variables: + VSCODE_ARCH: x64 + steps: + - template: win32/product-build-win32.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: true + VSCODE_RUN_SMOKE_TESTS: false + - job: WindowsSmokeTests + displayName: Smoke Tests + timeoutInMinutes: 120 + variables: + VSCODE_ARCH: x64 + steps: + - template: win32/product-build-win32.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: true - stage: Linux dependsOn: @@ -75,7 +103,22 @@ stages: - LinuxServerDependencies pool: vscode-1es-vscode-linux-18.04 jobs: - - job: Linuxx64 + - job: Linuxx64UnitTest + displayName: Unit Tests + container: vscode-bionic-x64 + variables: + VSCODE_ARCH: x64 + NPM_ARCH: x64 + DISPLAY: ":10" + steps: + - template: linux/product-build-linux-client.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: true + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false + - job: Linuxx64IntegrationTest + displayName: Integration Tests container: vscode-bionic-x64 variables: VSCODE_ARCH: x64 @@ -85,6 +128,23 @@ stages: - template: linux/product-build-linux-client.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: true + VSCODE_RUN_SMOKE_TESTS: false + - job: Linuxx64SmokeTest + displayName: Smoke Tests + container: vscode-bionic-x64 + variables: + VSCODE_ARCH: x64 + NPM_ARCH: x64 + DISPLAY: ":10" + steps: + - template: linux/product-build-linux-client.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: true - stage: macOS dependsOn: @@ -94,7 +154,32 @@ stages: variables: BUILDSECMON_OPT_IN: true jobs: - - job: macOSTest + - job: macOSUnitTest + displayName: Unit Tests + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: x64 + steps: + - template: darwin/product-build-darwin-test.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: true + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false + - job: macOSIntegrationTest + displayName: Integration Tests + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: x64 + steps: + - template: darwin/product-build-darwin-test.yml + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: true + VSCODE_RUN_SMOKE_TESTS: false + - job: macOSSmokeTest + displayName: Smoke Tests timeoutInMinutes: 90 variables: VSCODE_ARCH: x64 @@ -102,3 +187,6 @@ stages: - template: darwin/product-build-darwin-test.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: true diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index d089f70d22806..286283a31dc39 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -1,6 +1,15 @@ parameters: - name: VSCODE_QUALITY type: string + - name: VSCODE_RUN_UNIT_TESTS + type: boolean + default: true + - name: VSCODE_RUN_INTEGRATION_TESTS + type: boolean + default: true + - name: VSCODE_RUN_SMOKE_TESTS + type: boolean + default: true steps: - task: NodeTool@0 @@ -184,105 +193,142 @@ steps: displayName: Download Playwright condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn electron $(VSCODE_ARCH) } - exec { .\scripts\test.bat --build --tfs "Unit Tests" } - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn test-node --build } - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn test-browser-no-install --sequential --build --browser chromium --browser firefox --tfs "Browser Unit Tests" } - displayName: Run unit tests (Browser, Chromium & Firefox) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - - powershell: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json - $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" } - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn electron $(VSCODE_ARCH) } + exec { .\scripts\test.bat --build --tfs "Unit Tests" } + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\scripts\test-web-integration.bat --browser firefox } - displayName: Run integration tests (Browser, Firefox) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn test-node --build } + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json - $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-remote-integration.bat } - displayName: Run integration tests (Remote) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn test-browser-no-install --sequential --build --browser chromium --browser firefox --tfs "Browser Unit Tests" } + displayName: Run unit tests (Browser, Chromium & Firefox) + timeoutInMinutes: 20 + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - exec {.\build\azure-pipelines\win32\listprocesses.bat } - displayName: Diagnostics before smoke test run - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn gulp ` + compile-extension:css-language-features-server ` + compile-extension:emmet ` + compile-extension:git ` + compile-extension:github-authentication ` + compile-extension:html-language-features-server ` + compile-extension:ipynb ` + compile-extension:json-language-features-server ` + compile-extension:markdown-language-features ` + compile-extension-media ` + compile-extension:microsoft-authentication ` + compile-extension:typescript-language-features ` + compile-extension:vscode-api-tests ` + compile-extension:vscode-colorize-tests ` + compile-extension:vscode-custom-editor-tests ` + compile-extension:vscode-notebook-tests ` + compile-extension:vscode-test-resolver ` + } + displayName: Build integration tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - powershell: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json + $AppNameShort = $AppProductJson.nameShort + exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" } + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\scripts\test-web-integration.bat --browser firefox } + displayName: Run integration tests (Browser, Firefox) + timeoutInMinutes: 20 + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)" - exec { yarn smoketest-no-compile --web --tracing --headless } - displayName: Run smoke tests (Browser, Chromium) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json + $AppNameShort = $AppProductJson.nameShort + exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-remote-integration.bat } + displayName: Run integration tests (Remote) + timeoutInMinutes: 20 + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + exec {.\build\azure-pipelines\win32\listprocesses.bat } + displayName: Diagnostics before smoke test run + continueOnError: true + condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - exec { yarn smoketest-no-compile --tracing --build "$AppRoot" } - displayName: Run smoke tests (Electron) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)" + exec { yarn smoketest-no-compile --web --tracing --headless } + displayName: Run smoke tests (Browser, Chromium) + timeoutInMinutes: 20 + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)" - exec { yarn smoketest-no-compile --tracing --remote --build "$AppRoot" } - displayName: Run smoke tests (Remote) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + exec { yarn smoketest-no-compile --tracing --build "$AppRoot" } + displayName: Run smoke tests (Electron) + timeoutInMinutes: 20 + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - exec {.\build\azure-pipelines\win32\listprocesses.bat } - displayName: Diagnostics after smoke test run - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)" + exec { yarn gulp compile-extension:vscode-test-resolver } + exec { yarn smoketest-no-compile --tracing --remote --build "$AppRoot" } + displayName: Run smoke tests (Remote) + timeoutInMinutes: 20 + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + exec {.\build\azure-pipelines\win32\listprocesses.bat } + displayName: Diagnostics after smoke test run + continueOnError: true + condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - task: PublishPipelineArtifact@0 inputs: diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index 0260a781d8de1..8ab9e7d76dd49 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -21,19 +21,19 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( :: Run from a built: need to compile all test extensions :: because we run extension tests from their source folders :: and the build bundles extensions into .build webpacked - call yarn gulp compile-extension:vscode-api-tests^ - compile-extension:vscode-colorize-tests^ - compile-extension:markdown-language-features^ - compile-extension:typescript-language-features^ - compile-extension:vscode-custom-editor-tests^ - compile-extension:vscode-notebook-tests^ - compile-extension:emmet^ - compile-extension:css-language-features-server^ - compile-extension:html-language-features-server^ - compile-extension:json-language-features-server^ - compile-extension:git^ - compile-extension:ipynb^ - compile-extension-media + :: call yarn gulp compile-extension:vscode-api-tests^ + :: compile-extension:vscode-colorize-tests^ + :: compile-extension:markdown-language-features^ + :: compile-extension:typescript-language-features^ + :: compile-extension:vscode-custom-editor-tests^ + :: compile-extension:vscode-notebook-tests^ + :: compile-extension:emmet^ + :: compile-extension:css-language-features-server^ + :: compile-extension:html-language-features-server^ + :: compile-extension:json-language-features-server^ + :: compile-extension:git^ + :: compile-extension:ipynb^ + :: compile-extension-media :: Configuration for more verbose output set VSCODE_CLI=1 diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index 8ddfa7e9eae16..e381f61b40a4e 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -30,19 +30,19 @@ else # Run from a built: need to compile all test extensions # because we run extension tests from their source folders # and the build bundles extensions into .build webpacked - yarn gulp compile-extension:vscode-api-tests \ - compile-extension:vscode-colorize-tests \ - compile-extension:vscode-custom-editor-tests \ - compile-extension:vscode-notebook-tests \ - compile-extension:markdown-language-features \ - compile-extension:typescript-language-features \ - compile-extension:emmet \ - compile-extension:css-language-features-server \ - compile-extension:html-language-features-server \ - compile-extension:json-language-features-server \ - compile-extension:git \ - compile-extension:ipynb \ - compile-extension-media + # yarn gulp compile-extension:vscode-api-tests \ + # compile-extension:vscode-colorize-tests \ + # compile-extension:vscode-custom-editor-tests \ + # compile-extension:vscode-notebook-tests \ + # compile-extension:markdown-language-features \ + # compile-extension:typescript-language-features \ + # compile-extension:emmet \ + # compile-extension:css-language-features-server \ + # compile-extension:html-language-features-server \ + # compile-extension:json-language-features-server \ + # compile-extension:git \ + # compile-extension:ipynb \ + # compile-extension-media # Configuration for more verbose output export VSCODE_CLI=1 diff --git a/scripts/test-remote-integration.bat b/scripts/test-remote-integration.bat index 1c04b2e392c53..a7d0719205f1a 100644 --- a/scripts/test-remote-integration.bat +++ b/scripts/test-remote-integration.bat @@ -53,10 +53,10 @@ if "%INTEGRATION_TEST_ELECTRON_PATH%"=="" ( :: Run from a built: need to compile all test extensions :: because we run extension tests from their source folders :: and the build bundles extensions into .build webpacked - call yarn gulp compile-extension:vscode-api-tests^ - compile-extension:microsoft-authentication^ - compile-extension:github-authentication^ - compile-extension:vscode-test-resolver + :: call yarn gulp compile-extension:vscode-api-tests^ + :: compile-extension:microsoft-authentication^ + :: compile-extension:github-authentication^ + :: compile-extension:vscode-test-resolver :: Configuration for more verbose output set VSCODE_CLI=1 diff --git a/scripts/test-remote-integration.sh b/scripts/test-remote-integration.sh index e2212317d3b1f..7decdf3798fb2 100755 --- a/scripts/test-remote-integration.sh +++ b/scripts/test-remote-integration.sh @@ -47,16 +47,16 @@ else # Run from a built: need to compile all test extensions # because we run extension tests from their source folders # and the build bundles extensions into .build webpacked - yarn gulp compile-extension:vscode-api-tests \ - compile-extension:vscode-test-resolver \ - compile-extension:markdown-language-features \ - compile-extension:typescript-language-features \ - compile-extension:emmet \ - compile-extension:git \ - compile-extension:ipynb \ - compile-extension:microsoft-authentication \ - compile-extension:github-authentication \ - compile-extension-media + # yarn gulp compile-extension:vscode-api-tests \ + # compile-extension:vscode-test-resolver \ + # compile-extension:markdown-language-features \ + # compile-extension:typescript-language-features \ + # compile-extension:emmet \ + # compile-extension:git \ + # compile-extension:ipynb \ + # compile-extension:microsoft-authentication \ + # compile-extension:github-authentication \ + # compile-extension-media # Configuration for more verbose output export VSCODE_CLI=1 diff --git a/scripts/test-web-integration.bat b/scripts/test-web-integration.bat index 99bb16b7d5e1f..c5b89b85b367f 100644 --- a/scripts/test-web-integration.bat +++ b/scripts/test-web-integration.bat @@ -25,12 +25,12 @@ if "%VSCODE_REMOTE_SERVER_PATH%"=="" ( :: Run from a built: need to compile all test extensions :: because we run extension tests from their source folders :: and the build bundles extensions into .build webpacked - call yarn gulp compile-extension:vscode-api-tests^ - compile-extension:markdown-language-features^ - compile-extension:typescript-language-features^ - compile-extension:emmet^ - compile-extension:git^ - compile-extension-media + :: call yarn gulp compile-extension:vscode-api-tests^ + :: compile-extension:markdown-language-features^ + :: compile-extension:typescript-language-features^ + :: compile-extension:emmet^ + :: compile-extension:git^ + :: compile-extension-media ) if not exist ".\test\integration\browser\out\index.js" ( diff --git a/scripts/test-web-integration.sh b/scripts/test-web-integration.sh index 8f05929fdc4bc..4246cdc6ac1ef 100755 --- a/scripts/test-web-integration.sh +++ b/scripts/test-web-integration.sh @@ -19,13 +19,13 @@ else # Run from a built: need to compile all test extensions # because we run extension tests from their source folders # and the build bundles extensions into .build webpacked - yarn gulp compile-extension:vscode-api-tests \ - compile-extension:markdown-language-features \ - compile-extension:typescript-language-features \ - compile-extension:emmet \ - compile-extension:git \ - compile-extension:ipynb \ - compile-extension-media + # yarn gulp compile-extension:vscode-api-tests \ + # compile-extension:markdown-language-features \ + # compile-extension:typescript-language-features \ + # compile-extension:emmet \ + # compile-extension:git \ + # compile-extension:ipynb \ + # compile-extension-media fi if [ ! -e 'test/integration/browser/out/index.js' ];then From c88474c6bc775ed68eb2448279ca0a3a79d016ea Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 18 May 2022 11:03:34 +0200 Subject: [PATCH 132/942] depend on vscode-policy-watcher --- package.json | 1 + yarn.lock | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/package.json b/package.json index affa8ac0cdc06..ed114a6ceac80 100644 --- a/package.json +++ b/package.json @@ -221,6 +221,7 @@ }, "optionalDependencies": { "@vscode/windows-registry": "1.0.6", + "vscode-policy-watcher": "^1.0.0", "windows-foreground-love": "0.4.0", "windows-mutex": "0.4.1", "windows-process-tree": "0.3.3" diff --git a/yarn.lock b/yarn.lock index 1380533935548..ac4091e65aa33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8136,6 +8136,11 @@ node-abi@^3.3.0: dependencies: semver "^7.3.5" +node-addon-api@*: + version "5.0.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.0.0.tgz#7d7e6f9ef89043befdb20c1989c905ebde18c501" + integrity sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA== + node-addon-api@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239" @@ -11791,6 +11796,14 @@ vscode-oniguruma@1.6.1: resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.6.1.tgz#2bf4dfcfe3dd2e56eb549a3068c8ee39e6c30ce5" integrity sha512-vc4WhSIaVpgJ0jJIejjYxPvURJavX6QG41vu0mGhqywMkQqulezEqEQ3cO3gc8GvcOpX6ycmKGqRoROEMBNXTQ== +vscode-policy-watcher@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/vscode-policy-watcher/-/vscode-policy-watcher-1.0.0.tgz#08138a5ab15a1d6c021df51716a4daa97d74b471" + integrity sha512-j9nWBInu9wPW+3Uw/flYXP0kTewTqTfM6ADC24stqxURCAGOWxYz3ArBo21o+3zOrxpfCjc5mCbUNfLk+6IHuA== + dependencies: + bindings "^1.5.0" + node-addon-api "*" + vscode-proxy-agent@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.12.0.tgz#0775f464b9519b0c903da4dcf50851e1453f4e48" From e2029b6cbed68372b14856222ea7aaf5fb61fe5f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 18 May 2022 11:15:18 +0200 Subject: [PATCH 133/942] cleanup vscode-policy-watcher --- build/.moduleignore | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build/.moduleignore b/build/.moduleignore index 22c1b4fe48f0c..a2e1b715e1baf 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -107,6 +107,14 @@ vscode-encrypt/binding.gyp vscode-encrypt/README.md !vscode-encrypt/build/Release/vscode-encrypt-native.node +vscode-policy-watcher/build/** +vscode-policy-watcher/.husky/** +vscode-policy-watcher/src/** +vscode-policy-watcher/binding.gyp +vscode-policy-watcher/README.md +vscode-policy-watcher/index.d.ts +!vscode-policy-watcher/build/Release/vscode-policy-watcher.node + vscode-windows-ca-certs/**/* !vscode-windows-ca-certs/package.json !vscode-windows-ca-certs/**/*.node From 0219800af14c2bb3f7a7225ebe1ed8e96dde00c2 Mon Sep 17 00:00:00 2001 From: Anthony Stewart <150152+a-stewart@users.noreply.github.com> Date: Wed, 18 May 2022 11:50:25 +0200 Subject: [PATCH 134/942] Use the pointer cursor for links in the banner bar (#149738) Co-authored-by: Anthony Stewart --- src/vs/workbench/browser/parts/banner/media/bannerpart.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/browser/parts/banner/media/bannerpart.css b/src/vs/workbench/browser/parts/banner/media/bannerpart.css index fb2d5b4d1e84a..aa793f995a62b 100644 --- a/src/vs/workbench/browser/parts/banner/media/bannerpart.css +++ b/src/vs/workbench/browser/parts/banner/media/bannerpart.css @@ -52,10 +52,12 @@ padding: 3px; margin-left: 12px; text-decoration: underline; + cursor: pointer; } .monaco-workbench .part.banner .message-container a { text-decoration: underline; + cursor: pointer; } .monaco-workbench .part.banner .action-container { From ba5691170f4e9c9b3cced095ea498d3c243d1add Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 18 May 2022 09:47:49 +0200 Subject: [PATCH 135/942] update vscode-grammar-updater --- extensions/package.json | 2 +- extensions/yarn.lock | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index 3704c9b327c6d..b6d00d2faafbe 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -12,6 +12,6 @@ "devDependencies": { "@parcel/watcher": "2.0.5", "esbuild": "^0.11.12", - "vscode-grammar-updater": "^1.0.4" + "vscode-grammar-updater": "^1.1.0" } } diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 97766b7943b0a..1c746b8d2d001 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -10,17 +10,17 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -coffee-script@^1.10.0: +coffeescript@1.12.7: version "1.12.7" - resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.7.tgz#c05dae0cb79591d05b3070a8433a98c9a89ccc53" - integrity sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw== + resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-1.12.7.tgz#e57ee4c4867cf7f606bfc4a0f2d550c0981ddd27" + integrity sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA== -cson-parser@^1.3.3: - version "1.3.5" - resolved "https://registry.yarnpkg.com/cson-parser/-/cson-parser-1.3.5.tgz#7ec675e039145533bf2a6a856073f1599d9c2d24" - integrity sha1-fsZ14DkUVTO/KmqFYHPxWZ2cLSQ= +cson-parser@^4.0.9: + version "4.0.9" + resolved "https://registry.yarnpkg.com/cson-parser/-/cson-parser-4.0.9.tgz#eef0cf77edd057f97861ef800300c8239224eedb" + integrity sha512-I79SAcCYquWnEfXYj8hBqOOWKj6eH6zX1hhX3yqmS4K3bYp7jME3UFpHPzu3rUew0oyfc0s8T6IlWGXRAheHag== dependencies: - coffee-script "^1.10.0" + coffeescript "1.12.7" esbuild@^0.11.12: version "0.11.23" @@ -47,10 +47,10 @@ typescript@4.7.1-rc: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.1-rc.tgz#23a0517d36c56de887b4457f29e2d265647bbd7c" integrity sha512-EQd2NVelDe6ZVc2sO1CSpuSs+RHzY8c2n/kTNQAHw4um/eAXY+ZY4IKoUpNK0wO6C5hN+XcUXR7yqT8VbwwNIQ== -vscode-grammar-updater@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vscode-grammar-updater/-/vscode-grammar-updater-1.0.4.tgz#f0b8bd106a499a15f3e6b199055908ed8e860984" - integrity sha512-WjmpFo+jlnxOfHNeSrO3nJx8S2u3f926UL0AHJhDMQghCwEfkMvf37aafF83xvtLW2G9ywhifLbq4caxDQm+wQ== +vscode-grammar-updater@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vscode-grammar-updater/-/vscode-grammar-updater-1.1.0.tgz#030eacd8b8ba8f3f2fe43c9032601f839ba811c4" + integrity sha512-rWcJXyEFK27Mh9bxfBTLaul0KiGQk0GMXj2qTDH9cy3UZVx5MrF035B03os1w4oIXwl/QDhdLnsBK0j2SNiL1A== dependencies: - cson-parser "^1.3.3" + cson-parser "^4.0.9" fast-plist "0.1.2" From 56eca91b26ff4e25f548aae1969990cfa6716544 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 18 May 2022 14:13:58 +0200 Subject: [PATCH 136/942] add vscode-policy-watcher dependency, support registerPolicyDefinitions --- package.json | 2 +- src/vs/code/electron-main/main.ts | 4 +- src/vs/code/node/cliProcessMain.ts | 6 +- .../policy/node/nativePolicyService.ts | 68 ++++++++++++++++++ .../policy/node/windowsPolicyService.ts | 70 ------------------- yarn.lock | 8 +-- 6 files changed, 79 insertions(+), 79 deletions(-) create mode 100644 src/vs/platform/policy/node/nativePolicyService.ts delete mode 100644 src/vs/platform/policy/node/windowsPolicyService.ts diff --git a/package.json b/package.json index ed114a6ceac80..412567abe4546 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "tas-client-umd": "0.1.5", "v8-inspect-profiler": "^0.1.0", "vscode-oniguruma": "1.6.1", + "vscode-policy-watcher": "^1.1.0", "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", @@ -221,7 +222,6 @@ }, "optionalDependencies": { "@vscode/windows-registry": "1.0.6", - "vscode-policy-watcher": "^1.0.0", "windows-foreground-love": "0.4.0", "windows-mutex": "0.4.1", "windows-process-tree": "0.3.3" diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index d1563aebe638d..cb41f40555e0e 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -63,7 +63,7 @@ import { StateMainService } from 'vs/platform/state/electron-main/stateMainServi import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IThemeMainService, ThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; -import { WindowsPolicyService } from 'vs/platform/policy/node/windowsPolicyService'; +import { NativePolicyService } from 'vs/platform/policy/node/nativePolicyService'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; /** @@ -170,7 +170,7 @@ class CodeMain { services.set(ILoggerService, new LoggerService(logService, fileService)); // Policy - const policyService = isWindows && productService.win32RegValueName ? new WindowsPolicyService(productService.win32RegValueName) + const policyService = isWindows && productService.win32RegValueName ? new NativePolicyService(productService.win32RegValueName) : environmentMainService.policyFile ? new FilePolicyService(environmentMainService.policyFile, fileService, logService) : new NullPolicyService(); services.set(IPolicyService, policyService); diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index f0151458057a5..bc630b7cae821 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -42,7 +42,7 @@ import { ConsoleLogger, getLogLevel, ILogger, ILogService, LogLevel, MultiplexLo import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; -import { WindowsPolicyService } from 'vs/platform/policy/node/windowsPolicyService'; +import { NativePolicyService } from 'vs/platform/policy/node/nativePolicyService'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRequestService } from 'vs/platform/request/common/request'; @@ -133,7 +133,9 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, diskFileSystemProvider); // Policy - const policyService = isWindows && productService.win32RegValueName ? new WindowsPolicyService(productService.win32RegValueName) : environmentService.policyFile ? new FilePolicyService(environmentService.policyFile, fileService, logService) : new NullPolicyService(); + const policyService = isWindows && productService.win32RegValueName ? new NativePolicyService(productService.win32RegValueName) + : environmentService.policyFile ? new FilePolicyService(environmentService.policyFile, fileService, logService) + : new NullPolicyService(); services.set(IPolicyService, policyService); // Configuration diff --git a/src/vs/platform/policy/node/nativePolicyService.ts b/src/vs/platform/policy/node/nativePolicyService.ts new file mode 100644 index 0000000000000..10cd2ee5446f1 --- /dev/null +++ b/src/vs/platform/policy/node/nativePolicyService.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; +import { IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { Iterable } from 'vs/base/common/iterator'; +import { Throttler } from 'vs/base/common/async'; +import type { Watcher } from 'vscode-policy-watcher'; + +export class NativePolicyService implements IPolicyService { + + readonly _serviceBrand: undefined; + + private policyDefinitions: IStringDictionary = {}; + private readonly policies = new Map(); + + private readonly _onDidChange = new Emitter(); + readonly onDidChange = this._onDidChange.event; + + private throttler = new Throttler(); + private watcher: Watcher | undefined; + + constructor(private readonly productName: string) { } + + async registerPolicyDefinitions(policyDefinitions: IStringDictionary): Promise> { + const size = Object.keys(this.policyDefinitions).length; + this.policyDefinitions = { ...policyDefinitions, ...this.policyDefinitions }; + + if (size !== Object.keys(this.policyDefinitions).length) { + await this.throttler.queue(async () => { + this.watcher?.dispose(); + + const { createWatcher } = await import('vscode-policy-watcher'); + + await new Promise(c => { + this.watcher = createWatcher(this.productName, policyDefinitions, update => { + for (const key in update) { + const value = update[key] as any; + + if (value === undefined) { + this.policies.delete(key); + } else { + this.policies.set(key, value); + } + } + + this._onDidChange.fire(Object.keys(update)); + c(); + }); + }); + }); + } + + return Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {}); + } + + getPolicyValue(name: PolicyName): PolicyValue | undefined { + return this.policies.get(name); + } + + dispose(): void { + this._onDidChange.dispose(); + this.watcher?.dispose(); + } +} diff --git a/src/vs/platform/policy/node/windowsPolicyService.ts b/src/vs/platform/policy/node/windowsPolicyService.ts deleted file mode 100644 index 23439893dc857..0000000000000 --- a/src/vs/platform/policy/node/windowsPolicyService.ts +++ /dev/null @@ -1,70 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Event, Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; -import { createWatcher, Watcher } from 'vscode-policy-watcher'; -import { IStringDictionary } from 'vs/base/common/collections'; -import { Iterable } from 'vs/base/common/iterator'; - -export class WindowsPolicyService extends Disposable implements IPolicyService { - - readonly _serviceBrand: undefined; - - private readonly policies = new Map(); - private init: Promise | undefined; - - private readonly _onDidChange = new Emitter(); - readonly onDidChange = this._onDidChange.event; - - constructor(private readonly productName: string) { - super(); - } - - async registerPolicyDefinitions(policies: IStringDictionary): Promise> { - if (!this.init) { - this.init = new Promise(c => { - let first = true; - - const watcher = createWatcher(this.productName, policies, update => { - for (const key in update) { - const value = update[key] as any; - - if (value === undefined) { - this.policies.delete(key); - } else { - this.policies.set(key, value); - } - } - - if (first) { - first = false; - c(watcher); - } else { - this._onDidChange.fire(Object.keys(update)); - } - }); - - this._register(watcher); - }); - - await this.init; - } else { - const watcher = await this.init; - const promise = Event.toPromise(this.onDidChange); - watcher.addPolicies(policies); - await promise; - } - - // TODO@joao: heavy cleanup - - return Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {}); - } - - getPolicyValue(name: PolicyName): PolicyValue | undefined { - return this.policies.get(name); - } -} diff --git a/yarn.lock b/yarn.lock index ac4091e65aa33..36cf507aa657f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11796,10 +11796,10 @@ vscode-oniguruma@1.6.1: resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.6.1.tgz#2bf4dfcfe3dd2e56eb549a3068c8ee39e6c30ce5" integrity sha512-vc4WhSIaVpgJ0jJIejjYxPvURJavX6QG41vu0mGhqywMkQqulezEqEQ3cO3gc8GvcOpX6ycmKGqRoROEMBNXTQ== -vscode-policy-watcher@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/vscode-policy-watcher/-/vscode-policy-watcher-1.0.0.tgz#08138a5ab15a1d6c021df51716a4daa97d74b471" - integrity sha512-j9nWBInu9wPW+3Uw/flYXP0kTewTqTfM6ADC24stqxURCAGOWxYz3ArBo21o+3zOrxpfCjc5mCbUNfLk+6IHuA== +vscode-policy-watcher@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vscode-policy-watcher/-/vscode-policy-watcher-1.1.0.tgz#2921353c5080b3452929f1e350b9fab9ff852cc9" + integrity sha512-yPvy3Or66H0l8/FyWbJeGxpWW3GDZf65EIT7fqp1Ethdz4ecEnHThuHti7SlfdRJTf5qnifrX7af02INiHqDMA== dependencies: bindings "^1.5.0" node-addon-api "*" From 089ef919ce457b047df2348360b0981b170ccb98 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 18 May 2022 14:34:19 +0200 Subject: [PATCH 137/942] update native policy service --- .../policy/node/nativePolicyService.ts | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/vs/platform/policy/node/nativePolicyService.ts b/src/vs/platform/policy/node/nativePolicyService.ts index 10cd2ee5446f1..9011bbf1534c4 100644 --- a/src/vs/platform/policy/node/nativePolicyService.ts +++ b/src/vs/platform/policy/node/nativePolicyService.ts @@ -8,35 +8,34 @@ import { IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/pl import { IStringDictionary } from 'vs/base/common/collections'; import { Iterable } from 'vs/base/common/iterator'; import { Throttler } from 'vs/base/common/async'; -import type { Watcher } from 'vscode-policy-watcher'; +import { createWatcher, Watcher } from 'vscode-policy-watcher'; +import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; -export class NativePolicyService implements IPolicyService { +export class NativePolicyService extends Disposable implements IPolicyService { readonly _serviceBrand: undefined; private policyDefinitions: IStringDictionary = {}; private readonly policies = new Map(); - private readonly _onDidChange = new Emitter(); + private readonly _onDidChange = this._register(new Emitter()); readonly onDidChange = this._onDidChange.event; private throttler = new Throttler(); - private watcher: Watcher | undefined; + private watcher = this._register(new MutableDisposable()); - constructor(private readonly productName: string) { } + constructor(private readonly productName: string) { + super(); + } async registerPolicyDefinitions(policyDefinitions: IStringDictionary): Promise> { const size = Object.keys(this.policyDefinitions).length; this.policyDefinitions = { ...policyDefinitions, ...this.policyDefinitions }; if (size !== Object.keys(this.policyDefinitions).length) { - await this.throttler.queue(async () => { - this.watcher?.dispose(); - - const { createWatcher } = await import('vscode-policy-watcher'); - - await new Promise(c => { - this.watcher = createWatcher(this.productName, policyDefinitions, update => { + await this.throttler.queue(() => new Promise((c, e) => { + try { + this.watcher.value = createWatcher(this.productName, policyDefinitions, update => { for (const key in update) { const value = update[key] as any; @@ -50,8 +49,10 @@ export class NativePolicyService implements IPolicyService { this._onDidChange.fire(Object.keys(update)); c(); }); - }); - }); + } catch (err) { + e(err); + } + })); } return Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {}); @@ -60,9 +61,4 @@ export class NativePolicyService implements IPolicyService { getPolicyValue(name: PolicyName): PolicyValue | undefined { return this.policies.get(name); } - - dispose(): void { - this._onDidChange.dispose(); - this.watcher?.dispose(); - } } From 351e3c525ac93a118e117ff65bd839ae6cc35853 Mon Sep 17 00:00:00 2001 From: Harald Kirschner Date: Wed, 18 May 2022 08:00:40 -0700 Subject: [PATCH 138/942] Report extension list (#147960) * Report extensions * Rename and move installedExtensions log --- .../browser/extensionsWorkbenchService.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 3aacb593829f8..7fda957ef4649 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -34,7 +34,7 @@ import * as resources from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IFileService } from 'vs/platform/files/common/files'; -import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension, TargetPlatform } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension, TargetPlatform, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { IProductService } from 'vs/platform/product/common/productService'; import { FileAccess } from 'vs/base/common/network'; @@ -46,11 +46,21 @@ import { IExtensionManifestPropertiesService } from 'vs/workbench/services/exten import { IExtensionService, IExtensionsStatus } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor'; import { isWeb } from 'vs/base/common/platform'; +import { GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; interface IExtensionStateProvider { (extension: Extension): T; } +interface InstalledExtensionsEvent { + readonly extensionIds: string; + readonly count: number; +} +interface ExtensionsLoadClassification extends GDPRClassification { + readonly extensionIds: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; + readonly count: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; +} + export class Extension implements IExtension { public enablementState: EnablementState = EnablementState.EnabledGlobally; @@ -716,6 +726,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension this.queryLocal().then(() => { this.resetIgnoreAutoUpdateExtensions(); this.eventuallyCheckForUpdates(true); + this._reportTelemetry(); // Always auto update builtin extensions in web if (isWeb && !this.isAutoUpdateEnabled()) { this.autoUpdateBuiltinExtensions(); @@ -727,6 +738,14 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension this.updateActivity(); })); } + private _reportTelemetry() { + const extensionIds = this.installed.filter(extension => + extension.type === ExtensionType.User && + (extension.enablementState === EnablementState.EnabledWorkspace || + extension.enablementState === EnablementState.EnabledGlobally)) + .map(extension => ExtensionIdentifier.toKey(extension.identifier.id)); + this.telemetryService.publicLog2('installedExtensions', { extensionIds: extensionIds.join(';'), count: extensionIds.length }); + } get local(): IExtension[] { const byId = groupByExtension(this.installed, r => r.identifier); From f9264e31b5c4ae909c20afc339b54d31738f840d Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Wed, 18 May 2022 09:02:15 -0700 Subject: [PATCH 139/942] fixes #149013 (#149785) --- src/vs/workbench/browser/parts/panel/media/basepanelpart.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/panel/media/basepanelpart.css b/src/vs/workbench/browser/parts/panel/media/basepanelpart.css index 6cb382b8a5d68..1f4f10f21c3ff 100644 --- a/src/vs/workbench/browser/parts/panel/media/basepanelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/basepanelpart.css @@ -176,13 +176,13 @@ } .monaco-workbench .part.basepanel > .composite.title > .panel-switcher-container > .monaco-action-bar .action-item .active-item-indicator { - top: -6px; + top: -4px; left: 10px; width: calc(100% - 20px); } .monaco-workbench .part.basepanel > .composite.title > .panel-switcher-container > .monaco-action-bar .action-item.icon .active-item-indicator { - top: -1px; + top: 1px; left: 2px; width: calc(100% - 4px); } From ae2202631366eea0aaed6f881f87c2b44ae1b647 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Wed, 18 May 2022 09:23:39 -0700 Subject: [PATCH 140/942] fixes #143887 (#149836) --- src/vs/base/browser/ui/menu/menu.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 24feda55daa53..e4984b0965458 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -90,14 +90,13 @@ export class Menu extends ActionBar { context: options.context, actionRunner: options.actionRunner, ariaLabel: options.ariaLabel, + ariaRole: 'menu', focusOnlyEnabledItems: true, triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh || isLinux ? [KeyCode.Space] : [])], keyDown: true } }); this.menuElement = menuElement; - this.actionsList.setAttribute('role', 'menu'); - this.actionsList.tabIndex = 0; this.menuDisposables = this._register(new DisposableStore()); From 5cdfdb435f6fb846c15a65ba3c8f7978003413fc Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 18 May 2022 09:45:17 -0700 Subject: [PATCH 141/942] Support selection foreground in terminal Fixes #149505 --- package.json | 10 +++--- remote/package.json | 8 ++--- remote/web/package.json | 6 ++-- remote/web/yarn.lock | 24 +++++++------- remote/yarn.lock | 32 +++++++++---------- .../terminal/browser/xterm/xtermTerminal.ts | 4 ++- .../terminal/common/terminalColorRegistry.ts | 6 ++++ yarn.lock | 32 +++++++++---------- 8 files changed, 65 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index 5ca24d43a8de3..035efff2f3332 100644 --- a/package.json +++ b/package.json @@ -84,12 +84,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.43", - "xterm-addon-search": "0.9.0-beta.35", + "xterm": "4.19.0-beta.47", + "xterm-addon-search": "0.9.0-beta.37", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.34", - "xterm-headless": "4.19.0-beta.43", + "xterm-addon-webgl": "0.12.0-beta.36", + "xterm-headless": "4.19.0-beta.47", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, @@ -229,4 +229,4 @@ "elliptic": "^6.5.3", "nwmatcher": "^1.4.4" } -} \ No newline at end of file +} diff --git a/remote/package.json b/remote/package.json index 80e835671cebb..75e83acbde5b0 100644 --- a/remote/package.json +++ b/remote/package.json @@ -24,12 +24,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.43", - "xterm-addon-search": "0.9.0-beta.35", + "xterm": "4.19.0-beta.47", + "xterm-addon-search": "0.9.0-beta.37", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.34", - "xterm-headless": "4.19.0-beta.43", + "xterm-addon-webgl": "0.12.0-beta.36", + "xterm-headless": "4.19.0-beta.47", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index cdbbf667adfef..ece7c79fe5ca3 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -10,9 +10,9 @@ "tas-client-umd": "0.1.5", "vscode-oniguruma": "1.6.1", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.43", - "xterm-addon-search": "0.9.0-beta.35", + "xterm": "4.19.0-beta.47", + "xterm-addon-search": "0.9.0-beta.37", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.34" + "xterm-addon-webgl": "0.12.0-beta.36" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 8316dc85a0e3a..97609688b6dd0 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -113,22 +113,22 @@ vscode-textmate@7.0.1: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-7.0.1.tgz#8118a32b02735dccd14f893b495fa5389ad7de79" integrity sha512-zQ5U/nuXAAMsh691FtV0wPz89nSkHbs+IQV8FDk+wew9BlSDhf4UmWGlWJfTR2Ti6xZv87Tj5fENzKf6Qk7aLw== -xterm-addon-search@0.9.0-beta.35: - version "0.9.0-beta.35" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.35.tgz#524ee3be855c1e8db234c6795bdb44bb6baff8fd" - integrity sha512-hTDqAhqlhBvz3dtdK1Tg5Al2U3HquSHpV1xCX+bbOmbgprAxUrSQxslUPDD69CTazzTyif3L19M08hccRyr1Ug== +xterm-addon-search@0.9.0-beta.37: + version "0.9.0-beta.37" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.37.tgz#84a020f03b2cacc5afac78ca6ff2f1eb86fb6710" + integrity sha512-bfeFgKJkDYyIgqpWiV1oWYqDiTo+SHTeIPEbpOfxDr97pc3PtDF1Tyd79PrvJNfoxaV3VMUo//UEOy4D+KY9OQ== xterm-addon-unicode11@0.4.0-beta.3: version "0.4.0-beta.3" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.34: - version "0.12.0-beta.34" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.34.tgz#51cac31cc7a78377be5d481b624ee82948360de1" - integrity sha512-TTIwun+a45oDN54sHhdUxsEx6VflgF2p9YGqS5+gVzpvPrEqP6GoDr6XFCDsZcSqi0ZT2FNGAKWlh7XSxsKQQw== +xterm-addon-webgl@0.12.0-beta.36: + version "0.12.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.36.tgz#460f80829a78c979a448d5b764699af3f0366ff1" + integrity sha512-sgX7OHSGZQZE5b4xtPqd/5NEcll0Z+00tnTVxKZlXf5XEENcG0tnBF4I4f+k9K3cmjE1UIUVG2yYPrqWlYCdpA== -xterm@4.19.0-beta.43: - version "4.19.0-beta.43" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.43.tgz#f2113c8ce303d22c5cfad1a4a119b81c103285fa" - integrity sha512-eQ3fzkUApGdl4/rrhzK4OIdMb3+qO0c2iCZIMbeP9SqqDltZnhWncz+3lGa0tnxKizVoUV9kmGaP7orsQ/IavQ== +xterm@4.19.0-beta.47: + version "4.19.0-beta.47" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.47.tgz#f5931201346a485e3bcb29d24e83583727e55707" + integrity sha512-c8EcCSWCHnFYNdRMTnJIpYTV9J2Ze7kdB1GhTcPO2ipMEsqaP/u6Ca8rgBWwV9HeEVfe4rGRqWW2qcSy4U9hMQ== diff --git a/remote/yarn.lock b/remote/yarn.lock index a6201c6d4338d..f023194fa2689 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -914,10 +914,10 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -xterm-addon-search@0.9.0-beta.35: - version "0.9.0-beta.35" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.35.tgz#524ee3be855c1e8db234c6795bdb44bb6baff8fd" - integrity sha512-hTDqAhqlhBvz3dtdK1Tg5Al2U3HquSHpV1xCX+bbOmbgprAxUrSQxslUPDD69CTazzTyif3L19M08hccRyr1Ug== +xterm-addon-search@0.9.0-beta.37: + version "0.9.0-beta.37" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.37.tgz#84a020f03b2cacc5afac78ca6ff2f1eb86fb6710" + integrity sha512-bfeFgKJkDYyIgqpWiV1oWYqDiTo+SHTeIPEbpOfxDr97pc3PtDF1Tyd79PrvJNfoxaV3VMUo//UEOy4D+KY9OQ== xterm-addon-serialize@0.7.0-beta.12: version "0.7.0-beta.12" @@ -929,20 +929,20 @@ xterm-addon-unicode11@0.4.0-beta.3: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.34: - version "0.12.0-beta.34" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.34.tgz#51cac31cc7a78377be5d481b624ee82948360de1" - integrity sha512-TTIwun+a45oDN54sHhdUxsEx6VflgF2p9YGqS5+gVzpvPrEqP6GoDr6XFCDsZcSqi0ZT2FNGAKWlh7XSxsKQQw== +xterm-addon-webgl@0.12.0-beta.36: + version "0.12.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.36.tgz#460f80829a78c979a448d5b764699af3f0366ff1" + integrity sha512-sgX7OHSGZQZE5b4xtPqd/5NEcll0Z+00tnTVxKZlXf5XEENcG0tnBF4I4f+k9K3cmjE1UIUVG2yYPrqWlYCdpA== -xterm-headless@4.19.0-beta.43: - version "4.19.0-beta.43" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.43.tgz#12fe4abe624265240a7de8a922bfc4fd28c5f92a" - integrity sha512-4T8TlWy5u+sS23aPtd8gBHJ0BVljbNQRPMFHzLigDNOMCwc4uWa9JsxYmKteKifcG5aMm11ALPUTxWZCgpATww== +xterm-headless@4.19.0-beta.47: + version "4.19.0-beta.47" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.47.tgz#9d5af8145c42b9a6241a040bb244b877c149761b" + integrity sha512-chVTURPMNDEerQIsN4lIRotpXCfJsTHioZGQxDBNdktZO1gMXGvNxDjzsbsJg5ikLN5llz/HLDfXZrjCeb3elg== -xterm@4.19.0-beta.43: - version "4.19.0-beta.43" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.43.tgz#f2113c8ce303d22c5cfad1a4a119b81c103285fa" - integrity sha512-eQ3fzkUApGdl4/rrhzK4OIdMb3+qO0c2iCZIMbeP9SqqDltZnhWncz+3lGa0tnxKizVoUV9kmGaP7orsQ/IavQ== +xterm@4.19.0-beta.47: + version "4.19.0-beta.47" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.47.tgz#f5931201346a485e3bcb29d24e83583727e55707" + integrity sha512-c8EcCSWCHnFYNdRMTnJIpYTV9J2Ze7kdB1GhTcPO2ipMEsqaP/u6Ca8rgBWwV9HeEVfe4rGRqWW2qcSy4U9hMQ== yallist@^4.0.0: version "4.0.0" diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 509db6cc5a605..3bf9f12b5309a 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -27,7 +27,7 @@ import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeServic import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { TERMINAL_FOREGROUND_COLOR, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, ansiColorIdentifiers, TERMINAL_SELECTION_BACKGROUND_COLOR, TERMINAL_FIND_MATCH_BACKGROUND_COLOR, TERMINAL_FIND_MATCH_HIGHLIGHT_BACKGROUND_COLOR, TERMINAL_FIND_MATCH_BORDER_COLOR, TERMINAL_OVERVIEW_RULER_FIND_MATCH_FOREGROUND_COLOR, TERMINAL_FIND_MATCH_HIGHLIGHT_BORDER_COLOR, TERMINAL_OVERVIEW_RULER_CURSOR_FOREGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; +import { TERMINAL_FOREGROUND_COLOR, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, ansiColorIdentifiers, TERMINAL_SELECTION_BACKGROUND_COLOR, TERMINAL_FIND_MATCH_BACKGROUND_COLOR, TERMINAL_FIND_MATCH_HIGHLIGHT_BACKGROUND_COLOR, TERMINAL_FIND_MATCH_BORDER_COLOR, TERMINAL_OVERVIEW_RULER_FIND_MATCH_FOREGROUND_COLOR, TERMINAL_FIND_MATCH_HIGHLIGHT_BORDER_COLOR, TERMINAL_OVERVIEW_RULER_CURSOR_FOREGROUND_COLOR, TERMINAL_SELECTION_FOREGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { Color } from 'vs/base/common/color'; import { ShellIntegrationAddon } from 'vs/platform/terminal/common/xterm/shellIntegrationAddon'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -567,6 +567,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { const cursorColor = theme.getColor(TERMINAL_CURSOR_FOREGROUND_COLOR) || foregroundColor; const cursorAccentColor = theme.getColor(TERMINAL_CURSOR_BACKGROUND_COLOR) || backgroundColor; const selectionColor = theme.getColor(TERMINAL_SELECTION_BACKGROUND_COLOR); + const selectionForegroundColor = theme.getColor(TERMINAL_SELECTION_FOREGROUND_COLOR) || undefined; return { background: backgroundColor ? backgroundColor.toString() : undefined, @@ -574,6 +575,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { cursor: cursorColor ? cursorColor.toString() : undefined, cursorAccent: cursorAccentColor ? cursorAccentColor.toString() : undefined, selection: selectionColor ? selectionColor.toString() : undefined, + selectionForeground: selectionForegroundColor?.toString(), black: theme.getColor(ansiColorIdentifiers[0])?.toString(), red: theme.getColor(ansiColorIdentifiers[1])?.toString(), green: theme.getColor(ansiColorIdentifiers[2])?.toString(), diff --git a/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts b/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts index bdfbe2ad4d06e..44061cca1430b 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts @@ -29,6 +29,12 @@ export const TERMINAL_SELECTION_BACKGROUND_COLOR = registerColor('terminal.selec hcDark: editorSelectionBackground, hcLight: editorSelectionBackground }, nls.localize('terminal.selectionBackground', 'The selection background color of the terminal.')); +export const TERMINAL_SELECTION_FOREGROUND_COLOR = registerColor('terminal.selectionForeground', { + light: null, + dark: null, + hcDark: '#000000', + hcLight: '#ffffff' +}, nls.localize('terminal.selectionForeground', 'The selection foreground color of the terminal. When this is null the selection foreground will be retained and have the minimum contrast ratio feature applied.')); export const TERMINAL_COMMAND_DECORATION_DEFAULT_BACKGROUND_COLOR = registerColor('terminalCommandDecoration.defaultBackground', { light: '#00000040', dark: '#ffffff40', diff --git a/yarn.lock b/yarn.lock index c4c6630ffe387..cee415886c141 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12194,10 +12194,10 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm-addon-search@0.9.0-beta.35: - version "0.9.0-beta.35" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.35.tgz#524ee3be855c1e8db234c6795bdb44bb6baff8fd" - integrity sha512-hTDqAhqlhBvz3dtdK1Tg5Al2U3HquSHpV1xCX+bbOmbgprAxUrSQxslUPDD69CTazzTyif3L19M08hccRyr1Ug== +xterm-addon-search@0.9.0-beta.37: + version "0.9.0-beta.37" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.37.tgz#84a020f03b2cacc5afac78ca6ff2f1eb86fb6710" + integrity sha512-bfeFgKJkDYyIgqpWiV1oWYqDiTo+SHTeIPEbpOfxDr97pc3PtDF1Tyd79PrvJNfoxaV3VMUo//UEOy4D+KY9OQ== xterm-addon-serialize@0.7.0-beta.12: version "0.7.0-beta.12" @@ -12209,20 +12209,20 @@ xterm-addon-unicode11@0.4.0-beta.3: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.34: - version "0.12.0-beta.34" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.34.tgz#51cac31cc7a78377be5d481b624ee82948360de1" - integrity sha512-TTIwun+a45oDN54sHhdUxsEx6VflgF2p9YGqS5+gVzpvPrEqP6GoDr6XFCDsZcSqi0ZT2FNGAKWlh7XSxsKQQw== +xterm-addon-webgl@0.12.0-beta.36: + version "0.12.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.36.tgz#460f80829a78c979a448d5b764699af3f0366ff1" + integrity sha512-sgX7OHSGZQZE5b4xtPqd/5NEcll0Z+00tnTVxKZlXf5XEENcG0tnBF4I4f+k9K3cmjE1UIUVG2yYPrqWlYCdpA== -xterm-headless@4.19.0-beta.43: - version "4.19.0-beta.43" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.43.tgz#12fe4abe624265240a7de8a922bfc4fd28c5f92a" - integrity sha512-4T8TlWy5u+sS23aPtd8gBHJ0BVljbNQRPMFHzLigDNOMCwc4uWa9JsxYmKteKifcG5aMm11ALPUTxWZCgpATww== +xterm-headless@4.19.0-beta.47: + version "4.19.0-beta.47" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.47.tgz#9d5af8145c42b9a6241a040bb244b877c149761b" + integrity sha512-chVTURPMNDEerQIsN4lIRotpXCfJsTHioZGQxDBNdktZO1gMXGvNxDjzsbsJg5ikLN5llz/HLDfXZrjCeb3elg== -xterm@4.19.0-beta.43: - version "4.19.0-beta.43" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.43.tgz#f2113c8ce303d22c5cfad1a4a119b81c103285fa" - integrity sha512-eQ3fzkUApGdl4/rrhzK4OIdMb3+qO0c2iCZIMbeP9SqqDltZnhWncz+3lGa0tnxKizVoUV9kmGaP7orsQ/IavQ== +xterm@4.19.0-beta.47: + version "4.19.0-beta.47" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.47.tgz#f5931201346a485e3bcb29d24e83583727e55707" + integrity sha512-c8EcCSWCHnFYNdRMTnJIpYTV9J2Ze7kdB1GhTcPO2ipMEsqaP/u6Ca8rgBWwV9HeEVfe4rGRqWW2qcSy4U9hMQ== y18n@^3.2.1: version "3.2.2" From 3678e5fd8a773f0f3b66bf418875475b988c38ae Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 18 May 2022 09:58:02 -0700 Subject: [PATCH 142/942] Clean up terminal xterm theme setting function --- .../contrib/terminal/browser/xterm/xtermTerminal.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 3bf9f12b5309a..6849cc1ff8493 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -566,15 +566,15 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { } const cursorColor = theme.getColor(TERMINAL_CURSOR_FOREGROUND_COLOR) || foregroundColor; const cursorAccentColor = theme.getColor(TERMINAL_CURSOR_BACKGROUND_COLOR) || backgroundColor; - const selectionColor = theme.getColor(TERMINAL_SELECTION_BACKGROUND_COLOR); + const selectionBackgroundColor = theme.getColor(TERMINAL_SELECTION_BACKGROUND_COLOR); const selectionForegroundColor = theme.getColor(TERMINAL_SELECTION_FOREGROUND_COLOR) || undefined; return { - background: backgroundColor ? backgroundColor.toString() : undefined, - foreground: foregroundColor ? foregroundColor.toString() : undefined, - cursor: cursorColor ? cursorColor.toString() : undefined, - cursorAccent: cursorAccentColor ? cursorAccentColor.toString() : undefined, - selection: selectionColor ? selectionColor.toString() : undefined, + background: backgroundColor?.toString(), + foreground: foregroundColor?.toString(), + cursor: cursorColor?.toString(), + cursorAccent: cursorAccentColor?.toString(), + selection: selectionBackgroundColor?.toString(), selectionForeground: selectionForegroundColor?.toString(), black: theme.getColor(ansiColorIdentifiers[0])?.toString(), red: theme.getColor(ansiColorIdentifiers[1])?.toString(), From ca280a1685a34207a8bcf4826f8294d824b3caf7 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Wed, 18 May 2022 10:15:11 -0700 Subject: [PATCH 143/942] Update menubar button styles (Fix #149834) (#149835) * Use border radius/padding for classic menubar * Removed unused line-height rule --- src/vs/base/browser/ui/menu/menubar.css | 4 +++- .../workbench/browser/parts/titlebar/media/titlebarpart.css | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/menu/menubar.css b/src/vs/base/browser/ui/menu/menubar.css index d2a7014671824..b9dd13780e9db 100644 --- a/src/vs/base/browser/ui/menu/menubar.css +++ b/src/vs/base/browser/ui/menu/menubar.css @@ -9,7 +9,8 @@ display: flex; flex-shrink: 1; box-sizing: border-box; - height: 30px; + height: 100%; + padding: 4px 0; overflow: hidden; flex-wrap: wrap; } @@ -23,6 +24,7 @@ align-items: center; box-sizing: border-box; padding: 0px 8px; + border-radius: 5px; cursor: default; -webkit-app-region: no-drag; zoom: 1; diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 35b374ece47d5..8b516b544a75f 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -42,7 +42,7 @@ .monaco-workbench.windows .part.titlebar>.titlebar-container, .monaco-workbench.linux .part.titlebar>.titlebar-container { height: 30px; - line-height: 30px; + line-height: 22px; justify-content: left; } From ff975bc211f6546ae2da0a7e8c87d73fc080fa46 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 May 2022 10:39:40 -0700 Subject: [PATCH 144/942] Update list of enabled APIs (#149838) We use `notebookWorkspaceEdit` even though it's not enforced --- extensions/ipynb/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index 32802630b847d..901f65e56e170 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -9,7 +9,8 @@ "vscode": "^1.57.0" }, "enabledApiProposals": [ - "notebookEditor" + "notebookEditor", + "notebookWorkspaceEdit" ], "activationEvents": [ "*" From 6bd7605ae151f75d4bf865f10a6a6fcab7d3c4e9 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 May 2022 10:47:37 -0700 Subject: [PATCH 145/942] Enable strictNullChecks for implicit projects (#149844) Mistakenly enabled checkJS instead of strictNullChecks --- extensions/typescript-language-features/package.json | 4 ++-- .../typescript-language-features/src/utils/configuration.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index e5ca635d50cd9..14618b75330a0 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -686,7 +686,7 @@ }, "js/ts.implicitProjectConfig.checkJs": { "type": "boolean", - "default": true, + "default": false, "markdownDescription": "%configuration.implicitProjectConfig.checkJs%", "scope": "window" }, @@ -705,7 +705,7 @@ }, "js/ts.implicitProjectConfig.strictNullChecks": { "type": "boolean", - "default": false, + "default": true, "markdownDescription": "%configuration.implicitProjectConfig.strictNullChecks%", "scope": "window" }, diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index 34355b5e3e5ab..2690f7438489f 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -82,7 +82,7 @@ export class ImplicitProjectConfiguration { private static readCheckJs(configuration: vscode.WorkspaceConfiguration): boolean { return configuration.get('js/ts.implicitProjectConfig.checkJs') - ?? configuration.get('javascript.implicitProjectConfig.checkJs', true); + ?? configuration.get('javascript.implicitProjectConfig.checkJs', false); } private static readExperimentalDecorators(configuration: vscode.WorkspaceConfiguration): boolean { @@ -91,7 +91,7 @@ export class ImplicitProjectConfiguration { } private static readImplicitStrictNullChecks(configuration: vscode.WorkspaceConfiguration): boolean { - return configuration.get('js/ts.implicitProjectConfig.strictNullChecks', false); + return configuration.get('js/ts.implicitProjectConfig.strictNullChecks', true); } private static readImplicitStrictFunctionTypes(configuration: vscode.WorkspaceConfiguration): boolean { From 7cf37cbb25a35c7e05d9f907dc15ed950e94fb04 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Wed, 18 May 2022 10:53:18 -0700 Subject: [PATCH 146/942] add ExtensionVersion filter in assignment service (#149845) --- src/vs/platform/assignment/common/assignment.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vs/platform/assignment/common/assignment.ts b/src/vs/platform/assignment/common/assignment.ts index 7ca130fe9f3a7..9f9b72aea17d7 100644 --- a/src/vs/platform/assignment/common/assignment.ts +++ b/src/vs/platform/assignment/common/assignment.ts @@ -31,6 +31,7 @@ https://experimentation.visualstudio.com/Analysis%20and%20Experimentation/_git/A "X-VSCode-Build": "build", "X-MSEdge-ClientId": "clientid", "X-VSCode-ExtensionName": "extensionname", +"X-VSCode-ExtensionVersion": "extensionversion", "X-VSCode-TargetPopulation": "targetpopulation", "X-VSCode-Language": "language" */ @@ -65,6 +66,11 @@ export enum Filters { */ ExtensionName = 'X-VSCode-ExtensionName', + /** + * The version of the extension. + */ + ExtensionVersion = 'X-VSCode-ExtensionVersion', + /** * The language in use by VS Code */ @@ -97,6 +103,8 @@ export class AssignmentFilterProvider implements IExperimentationFilterProvider return platform.language; case Filters.ExtensionName: return 'vscode-core'; // always return vscode-core for exp service + case Filters.ExtensionVersion: + return '999999.0'; // always return a very large number for cross-extension experimentation case Filters.TargetPopulation: return this.targetPopulation; default: From 49f9aecd0aa2aef56324716f943bae65053f0302 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 18 May 2022 11:11:31 -0700 Subject: [PATCH 147/942] Fix terminal theme tests --- src/vs/platform/theme/test/common/testThemeService.ts | 2 +- .../terminal/test/browser/xterm/xtermTerminal.test.ts | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/theme/test/common/testThemeService.ts b/src/vs/platform/theme/test/common/testThemeService.ts index 6655be6e3609f..a7911a49048bc 100644 --- a/src/vs/platform/theme/test/common/testThemeService.ts +++ b/src/vs/platform/theme/test/common/testThemeService.ts @@ -14,7 +14,7 @@ export class TestColorTheme implements IColorTheme { public readonly label = 'test'; constructor( - private colors: { [id: string]: string } = {}, + private colors: { [id: string]: string | undefined } = {}, public type = ColorScheme.DARK, public readonly semanticHighlighting = false ) { } diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts index c9bbe8dedafe2..eaec5127e5672 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts @@ -16,7 +16,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IViewDescriptor, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Emitter } from 'vs/base/common/event'; -import { TERMINAL_BACKGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; +import { TERMINAL_BACKGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR, TERMINAL_SELECTION_FOREGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { WebglAddon } from 'xterm-addon-webgl'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; @@ -146,6 +146,7 @@ suite('XtermTerminal', () => { [TERMINAL_CURSOR_FOREGROUND_COLOR]: '#000300', [TERMINAL_CURSOR_BACKGROUND_COLOR]: '#000400', [TERMINAL_SELECTION_BACKGROUND_COLOR]: '#000500', + [TERMINAL_SELECTION_FOREGROUND_COLOR]: undefined, 'terminal.ansiBlack': '#010000', 'terminal.ansiRed': '#020000', 'terminal.ansiGreen': '#030000', @@ -170,6 +171,7 @@ suite('XtermTerminal', () => { cursor: '#000300', cursorAccent: '#000400', selection: '#000500', + selectionForeground: undefined, black: '#010000', green: '#030000', red: '#020000', @@ -193,6 +195,7 @@ suite('XtermTerminal', () => { [TERMINAL_CURSOR_FOREGROUND_COLOR]: '#00030f', [TERMINAL_CURSOR_BACKGROUND_COLOR]: '#00040f', [TERMINAL_SELECTION_BACKGROUND_COLOR]: '#00050f', + [TERMINAL_SELECTION_FOREGROUND_COLOR]: '#00060f', 'terminal.ansiBlack': '#01000f', 'terminal.ansiRed': '#02000f', 'terminal.ansiGreen': '#03000f', @@ -216,6 +219,7 @@ suite('XtermTerminal', () => { cursor: '#00030f', cursorAccent: '#00040f', selection: '#00050f', + selectionForeground: '#00060f', black: '#01000f', green: '#03000f', red: '#02000f', From 64e09597f108f58777167874c76600cd55ceb685 Mon Sep 17 00:00:00 2001 From: Roj Date: Wed, 18 May 2022 21:13:32 +0300 Subject: [PATCH 148/942] Handle multiline commit messages when creating PR (#149426) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This makes the body of the pull request non-empty when the commit message consists more than one line. - The GitHub web client fills the body the same way, too, when you create a pull request. - Some commit messages consist more than one line, like this one, which is giving more detail about the commit, and can be used as a shortcut to not write the same text when opening a pull request and on GitHub. - Without these changes, the pull request title will be messed up if the commit message is similar to this. Co-authored-by: João Moreno --- extensions/github/src/pushErrorHandler.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/extensions/github/src/pushErrorHandler.ts b/extensions/github/src/pushErrorHandler.ts index 37f138caffb8e..5569d4fb480b5 100644 --- a/extensions/github/src/pushErrorHandler.ts +++ b/extensions/github/src/pushErrorHandler.ts @@ -102,13 +102,14 @@ async function handlePushError(repository: Repository, remote: Remote, refspec: let title = `Update ${remoteName}`; const head = repository.state.HEAD?.name; + let body: string | undefined; + if (head) { const commit = await repository.getCommit(head); - title = commit.message.replace(/\n.*$/m, ''); + title = commit.message.split('\n')[0]; + body = commit.message.slice(title.length + 1).trim(); } - let body: string | undefined; - const templates = await findPullRequestTemplates(repository.rootUri); if (templates.length > 0) { templates.sort((a, b) => a.path.localeCompare(b.path)); From d346ca8c37a0aab9d4f7b735cfd5b9fc92e4996e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 18 May 2022 22:34:18 +0200 Subject: [PATCH 149/942] - separate deprecated and prerelease migration extensions in the control file - Merge deprecated and prerelease migration extensions - Support deprecated extensions with settings - Do not auto migrate deprecated extensions - Support disallowing install for deprecated extensions --- .../abstractExtensionManagementService.ts | 11 ++--- .../common/extensionGalleryService.ts | 44 +++++++++++++++++-- .../common/extensionManagement.ts | 17 +++++-- .../common/unsupportedExtensionsMigration.ts | 10 +++-- .../extensions/browser/extensionEditor.ts | 2 +- .../extensions/browser/extensionsActions.ts | 24 +++++----- .../extensions/browser/extensionsList.ts | 2 +- .../extensions/browser/extensionsWidgets.ts | 4 +- .../browser/extensionsWorkbenchService.ts | 12 +++-- .../contrib/extensions/common/extensions.ts | 4 +- .../extensionRecommendationsService.test.ts | 2 +- .../extensionsActions.test.ts | 4 +- .../electron-browser/extensionsViews.test.ts | 2 +- .../extensionsWorkbenchService.test.ts | 4 +- .../browser/webExtensionsScannerService.ts | 10 ++--- .../common/extensionManagementService.ts | 2 +- 16 files changed, 101 insertions(+), 53 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index a73af96a7c894..94e9a31abc7f8 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -10,7 +10,6 @@ import { CancellationError, getErrorMessage } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; -import { isBoolean } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { @@ -364,9 +363,11 @@ export abstract class AbstractExtensionManagementService extends Disposable impl throw new ExtensionManagementError(nls.localize('malicious extension', "Can't install '{0}' extension since it was reported to be problematic.", extension.identifier.id), ExtensionManagementErrorCode.Malicious); } - const deprecated = report.deprecated ? report.deprecated[extension.identifier.id.toLowerCase()] : undefined; - if (deprecated && !isBoolean(deprecated)) { - throw new ExtensionManagementError(nls.localize('unsupported prerelease extension', "Can't install '{0}' extension because it is deprecated. Use '{1}' extension instead.", extension.identifier.id, deprecated.displayName), ExtensionManagementErrorCode.UnsupportedPreRelease); + const deprecated = report.deprecated[extension.identifier.id.toLowerCase()]; + if (deprecated?.disallowInstall) { + const message = deprecated.extension ? nls.localize('unsupported extension with alternative', "Can't install '{0}' extension because it is deprecated. Use {1} extension instead.", extension.identifier.id, deprecated.extension.displayName) + : nls.localize('unsupported extension without alternative and no message', "Can't install '{0}' extension because it is deprecated.", extension.identifier.id); + throw new ExtensionManagementError(message, ExtensionManagementErrorCode.Deprecated); } if (!await this.canInstall(extension)) { @@ -581,7 +582,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl return manifest; } catch (err) { this.logService.trace('ExtensionManagementService.refreshControlCache - failed to get extension control manifest'); - return { malicious: [] }; + return { malicious: [], deprecated: {} }; } } diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index b75a76752b81d..a65562115811d 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -16,7 +16,7 @@ import { URI } from 'vs/base/common/uri'; import { IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { getFallbackTargetPlarforms, getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionInfo, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IExtensionsControlManifest, isNotWebExtensionInWebTargetPlatform, isTargetPlatformCompatible, ITranslation, SortBy, SortOrder, StatisticType, toTargetPlatform, WEB_EXTENSION_TAG, IExtensionQueryOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { getFallbackTargetPlarforms, getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionInfo, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IExtensionsControlManifest, isNotWebExtensionInWebTargetPlatform, isTargetPlatformCompatible, ITranslation, SortBy, SortOrder, StatisticType, toTargetPlatform, WEB_EXTENSION_TAG, IExtensionQueryOptions, IDeprecationInfo } from 'vs/platform/extensionManagement/common/extensionManagement'; import { adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, getGalleryExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator'; @@ -537,7 +537,20 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller interface IRawExtensionsControlManifest { malicious: string[]; - deprecated?: IStringDictionary; + migrateToPreRelease?: IStringDictionary<{ + id: string; + displayName: string; + migrateStorage?: boolean; + engine?: string; + }>; + deprecated?: IStringDictionary; } abstract class AbstractExtensionGalleryService implements IExtensionGalleryService { @@ -1138,7 +1151,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi } if (!this.extensionsControlUrl) { - return { malicious: [] }; + return { malicious: [], deprecated: {} }; } const context = await this.requestService.request({ type: 'GET', url: this.extensionsControlUrl }, CancellationToken.None); @@ -1148,13 +1161,36 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi const result = await asJson(context); const malicious: IExtensionIdentifier[] = []; + const deprecated: IStringDictionary = {}; if (result) { for (const id of result.malicious) { malicious.push({ id }); } + if (result.migrateToPreRelease) { + for (const [unsupportedPreReleaseExtensionId, preReleaseExtensionInfo] of Object.entries(result.migrateToPreRelease)) { + if (!preReleaseExtensionInfo.engine || isEngineValid(preReleaseExtensionInfo.engine, this.productService.version, this.productService.date)) { + deprecated[unsupportedPreReleaseExtensionId.toLowerCase()] = { + disallowInstall: true, + extension: { + id: preReleaseExtensionInfo.id, + displayName: preReleaseExtensionInfo.displayName, + autoMigrate: { storage: !!preReleaseExtensionInfo.migrateStorage }, + preRelease: true + } + }; + } + } + } + if (result.deprecated) { + for (const [deprecatedExtensionId, deprecationInfo] of Object.entries(result.deprecated)) { + if (deprecationInfo) { + deprecated[deprecatedExtensionId.toLowerCase()] = isBoolean(deprecationInfo) ? {} : deprecationInfo; + } + } + } } - return { malicious, deprecated: result?.deprecated }; + return { malicious, deprecated }; } } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 061de6580a902..d041814268bb6 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -282,9 +282,20 @@ export const enum StatisticType { Uninstall = 'uninstall' } +export interface IDeprecationInfo { + readonly disallowInstall?: boolean; + readonly extension?: { + readonly id: string; + readonly displayName: string; + readonly autoMigrate?: { readonly storage: boolean }; + readonly preRelease?: boolean; + }; + readonly settings?: readonly string[]; +} + export interface IExtensionsControlManifest { - malicious: IExtensionIdentifier[]; - deprecated?: IStringDictionary; + readonly malicious: IExtensionIdentifier[]; + readonly deprecated: IStringDictionary; } export const enum InstallOperation { @@ -349,7 +360,7 @@ export interface DidUninstallExtensionEvent { export enum ExtensionManagementErrorCode { Unsupported = 'Unsupported', - UnsupportedPreRelease = 'UnsupportedPreRelease', + Deprecated = 'Deprecated', Malicious = 'Malicious', Incompatible = 'Incompatible', IncompatiblePreRelease = 'IncompatiblePreRelease', diff --git a/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts b/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts index 87eaf1447a6ce..57aadd8fc1d70 100644 --- a/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts +++ b/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; -import { isBoolean } from 'vs/base/common/types'; import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions, getExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; @@ -26,10 +25,13 @@ export async function migrateUnsupportedExtensions(extensionManagementService: I } const installed = await extensionManagementService.getInstalled(ExtensionType.User); for (const [unsupportedExtensionId, deprecated] of Object.entries(extensionsControlManifest.deprecated)) { - if (isBoolean(deprecated)) { + if (!deprecated?.extension) { + continue; + } + const { id: preReleaseExtensionId, autoMigrate, preRelease } = deprecated.extension; + if (!autoMigrate) { continue; } - const { id: preReleaseExtensionId, migrateStorage, preRelease } = deprecated; const unsupportedExtension = installed.find(i => areSameExtensions(i.identifier, { id: unsupportedExtensionId })); // Unsupported Extension is not installed if (!unsupportedExtension) { @@ -57,7 +59,7 @@ export async function migrateUnsupportedExtensions(extensionManagementService: I await extensionEnablementService.disableExtension(preReleaseExtension.identifier); logService.info(`Disabled the pre-release extension '${preReleaseExtension.identifier.id}' because the unsupported extension '${unsupportedExtension.identifier.id}' is disabled`); } - if (migrateStorage) { + if (autoMigrate.storage) { extensionStorageService.addToMigrationList(getExtensionId(unsupportedExtension.manifest.publisher, unsupportedExtension.manifest.name), getExtensionId(preReleaseExtension.manifest.publisher, preReleaseExtension.manifest.name)); logService.info(`Added pre-release extension to the storage migration list`); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 2f1244e6ebd0e..b3b6109f4d3d0 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -658,7 +658,7 @@ export class ExtensionEditor extends EditorPane { } }; reset(template.recommendation); - if (extension.deprecated || extension.state === ExtensionState.Installed) { + if (extension.deprecationInfo || extension.state === ExtensionState.Installed) { return; } updateRecommendationText(false); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index d3b1573086ed6..e578b8359cbf1 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -64,7 +64,6 @@ import { escapeMarkdownSyntaxTokens, IMarkdownString, MarkdownString } from 'vs/ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; import { flatten } from 'vs/base/common/arrays'; -import { isBoolean } from 'vs/base/common/types'; import { fromNow } from 'vs/base/common/date'; export class PromptExtensionInstallFailureAction extends Action { @@ -104,7 +103,7 @@ export class PromptExtensionInstallFailureAction extends Action { return; } - if ([ExtensionManagementErrorCode.Incompatible, ExtensionManagementErrorCode.IncompatibleTargetPlatform, ExtensionManagementErrorCode.Malicious, ExtensionManagementErrorCode.ReleaseVersionNotFound, ExtensionManagementErrorCode.UnsupportedPreRelease].includes(this.error.name)) { + if ([ExtensionManagementErrorCode.Incompatible, ExtensionManagementErrorCode.IncompatibleTargetPlatform, ExtensionManagementErrorCode.Malicious, ExtensionManagementErrorCode.ReleaseVersionNotFound, ExtensionManagementErrorCode.Deprecated].includes(this.error.name)) { await this.dialogService.show(Severity.Info, getErrorMessage(this.error)); return; } @@ -276,11 +275,11 @@ export abstract class AbstractInstallAction extends ExtensionAction { return; } - if (this.extension.deprecated && isBoolean(this.extension.deprecated)) { + if (this.extension.deprecationInfo) { const result = await this.dialogService.confirm({ type: 'warning', - title: localize('install confirmation', "Are you sure you want to install '{0}'?", this.extension.displayName), - message: localize('deprecated message', "This extension is no longer being maintained and is deprecated."), + message: localize('install confirmation', "Are you sure you want to install '{0}'?", this.extension.displayName), + detail: localize('deprecated message', "This extension is no longer being maintained and is deprecated."), primaryButton: localize('install anyway', "Install Anyway"), }); if (!result.confirmed) { @@ -2172,14 +2171,15 @@ export class ExtensionStatusAction extends ExtensionAction { return; } - if (this.extension.deprecated) { - if (isBoolean(this.extension.deprecated)) { - this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('unsupported tooltip', "This extension is no longer being maintained and is deprecated.")) }, true); + if (this.extension.deprecationInfo) { + if (this.extension.deprecationInfo.extension) { + const link = `[${this.extension.deprecationInfo.extension.displayName}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.deprecationInfo.extension.id]))}`)})`; + this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate extension tooltip', "This extension has been deprecated. Use {0} extension instead.", link)) }, true); + } else if (this.extension.deprecationInfo.settings) { + const link = `[${localize('settings', "settings")}](${URI.parse(`command:workbench.action.openSettings?${encodeURIComponent(JSON.stringify([this.extension.deprecationInfo.settings.map(setting => `@id:${setting}`).join(' ')]))}`)})`; + this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate settings tooltip', "This extension is deprecated and has become a native feature in VS Code. Configure these {0} instead.", link)) }, true); } else { - const link = `[${this.extension.deprecated.displayName}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.deprecated.id]))}`)})`; - if (this.extension.state !== ExtensionState.Installed) { - this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('unsupported prerelease switch tooltip', "This extension has been deprecated. Use {0} instead.", link)) }, true); - } + this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated tooltip', "This extension is no longer being maintained and is deprecated.")) }, true); } return; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 4755c67258111..9b5c1397ee459 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -188,7 +188,7 @@ export class Renderer implements IPagedRenderer { const updateEnablement = async () => { let isDisabled = false; if (extension.state === ExtensionState.Uninstalled) { - isDisabled = !!extension.deprecated || !(await this.extensionsWorkbenchService.canInstall(extension)); + isDisabled = !!extension.deprecationInfo || !(await this.extensionsWorkbenchService.canInstall(extension)); } else if (extension.local && !isLanguagePackExtension(extension.local.manifest)) { const runningExtensions = await this.extensionService.getExtensions(); const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, extension.identifier))[0]; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index e4592383fe646..1760db3204468 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -185,7 +185,7 @@ export class RecommendationWidget extends ExtensionWidget { render(): void { this.clear(); - if (!this.extension || this.extension.state === ExtensionState.Installed || this.extension.deprecated) { + if (!this.extension || this.extension.state === ExtensionState.Installed || this.extension.deprecationInfo) { return; } const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); @@ -549,7 +549,7 @@ export class ExtensionHoverWidget extends ExtensionWidget { if (extension.state === ExtensionState.Installed) { return undefined; } - if (extension.deprecated) { + if (extension.deprecationInfo) { return undefined; } const recommendation = this.extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 7fda957ef4649..ece35b058d130 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -15,7 +15,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, InstallExtensionEvent, DidUninstallExtensionEvent, IExtensionIdentifier, InstallOperation, InstallOptions, WEB_EXTENSION_TAG, InstallExtensionResult, - IExtensionsControlManifest, InstallVSIXOptions, IExtensionInfo, IExtensionQueryOptions + IExtensionsControlManifest, InstallVSIXOptions, IExtensionInfo, IExtensionQueryOptions, IDeprecationInfo } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService, DefaultIconPath } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, groupByExtension, ExtensionKey, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -198,7 +198,7 @@ export class Extension implements IExtension { } public isMalicious: boolean = false; - public deprecated: boolean | { id: string; displayName: string } = false; + public deprecationInfo: IDeprecationInfo | undefined; get installCount(): number | undefined { return this.gallery ? this.gallery.installCount : undefined; @@ -399,10 +399,8 @@ ${this.description} class Extensions extends Disposable { static updateExtensionFromControlManifest(extension: Extension, extensionsControlManifest: IExtensionsControlManifest): void { - const isMalicious = extensionsControlManifest.malicious.some(identifier => areSameExtensions(extension.identifier, identifier)); - extension.isMalicious = isMalicious; - const deprecated = extensionsControlManifest.deprecated ? extensionsControlManifest.deprecated[extension.identifier.id.toLowerCase()] : undefined; - extension.deprecated = deprecated ?? false; + extension.isMalicious = extensionsControlManifest.malicious.some(identifier => areSameExtensions(extension.identifier, identifier)); + extension.deprecationInfo = extensionsControlManifest.deprecated ? extensionsControlManifest.deprecated[extension.identifier.id.toLowerCase()] : undefined; } private readonly _onChange: Emitter<{ extension: Extension; operation?: InstallOperation } | undefined> = this._register(new Emitter<{ extension: Extension; operation?: InstallOperation } | undefined>()); @@ -1166,7 +1164,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return false; } - if (extension.deprecated && !isBoolean(extension.deprecated)) { + if (extension.deprecationInfo?.disallowInstall) { return false; } diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index 23744cd4f9b36..47be2a6e355df 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -6,7 +6,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { IPager } from 'vs/base/common/paging'; -import { IQueryOptions, ILocalExtension, IGalleryExtension, IExtensionIdentifier, InstallOptions, InstallVSIXOptions, IExtensionInfo, IExtensionQueryOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IQueryOptions, ILocalExtension, IGalleryExtension, IExtensionIdentifier, InstallOptions, InstallVSIXOptions, IExtensionInfo, IExtensionQueryOptions, IDeprecationInfo } from 'vs/platform/extensionManagement/common/extensionManagement'; import { EnablementState, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -78,7 +78,7 @@ export interface IExtension { readonly local?: ILocalExtension; gallery?: IGalleryExtension; readonly isMalicious: boolean; - readonly deprecated: boolean | { id: string; displayName: string }; + readonly deprecationInfo?: IDeprecationInfo; } export const SERVICE_ID = 'extensionsWorkbenchService'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index 54ddc028ea9e1..add1a477c3f08 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -218,7 +218,7 @@ suite('ExtensionRecommendationsService Test', () => { onDidUninstallExtension: didUninstallEvent.event, async getInstalled() { return []; }, async canInstall() { return true; }, - async getExtensionsControlManifest() { return { malicious: [] }; }, + async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, async getTargetPlatform() { return getTargetPlatform(platform, arch); } }); instantiationService.stub(IExtensionService, >{ diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 7e5b0d2a156f7..93b1ba0e871c8 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -100,7 +100,7 @@ async function setupTest() { onUninstallExtension: uninstallEvent.event, onDidUninstallExtension: didUninstallEvent.event, async getInstalled() { return []; }, - async getExtensionsControlManifest() { return { malicious: [] }; }, + async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata) { local.identifier.uuid = metadata.id; local.publisherDisplayName = metadata.publisherDisplayName; @@ -2424,7 +2424,7 @@ function createExtensionManagementService(installed: ILocalExtension[] = []): IE return local; }, async getTargetPlatform() { return getTargetPlatform(platform, arch); }, - async getExtensionsControlManifest() { return { malicious: [] }; }, + async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, }; } diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index 39f0b2ca026c1..1212a0ed3b276 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -98,7 +98,7 @@ suite('ExtensionsListView Tests', () => { onDidUninstallExtension: didUninstallEvent.event, async getInstalled() { return []; }, async canInstall() { return true; }, - async getExtensionsControlManifest() { return { malicious: [] }; }, + async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, async getTargetPlatform() { return getTargetPlatform(platform, arch); } }); instantiationService.stub(IRemoteAgentService, RemoteAgentService); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 09723bab66893..0c7d58166885d 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -95,7 +95,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { onUninstallExtension: uninstallEvent.event, onDidUninstallExtension: didUninstallEvent.event, async getInstalled() { return []; }, - async getExtensionsControlManifest() { return { malicious: [] }; }, + async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata) { local.identifier.uuid = metadata.id; local.publisherDisplayName = metadata.publisherDisplayName; @@ -1489,7 +1489,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { return local; }, getTargetPlatform: async () => getTargetPlatform(platform, arch), - async getExtensionsControlManifest() { return { malicious: [] }; }, + async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, }; } }); diff --git a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts index aa1eb99bc0e0e..1124b843b01a4 100644 --- a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts @@ -21,7 +21,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls'; import { localize } from 'vs/nls'; import * as semver from 'vs/base/common/semver/semver'; -import { isBoolean, isString } from 'vs/base/common/types'; +import { isString } from 'vs/base/common/types'; import { getErrorMessage } from 'vs/base/common/errors'; import { ResourceMap } from 'vs/base/common/map'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; @@ -140,11 +140,11 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten this.logService.info(`Checking additional builtin extensions: Ignoring '${extension.id}' because it is reported to be malicious.`); continue; } - const deprecated = extensionsControlManifest.deprecated ? extensionsControlManifest.deprecated[extension.id.toLowerCase()] : undefined; - if (deprecated && !isBoolean(deprecated)) { - const preReleaseExtensionId = deprecated.id; + const deprecationInfo = extensionsControlManifest.deprecated[extension.id.toLowerCase()]; + if (deprecationInfo?.extension?.autoMigrate) { + const preReleaseExtensionId = deprecationInfo.extension.id; this.logService.info(`Checking additional builtin extensions: '${extension.id}' is deprecated, instead using '${preReleaseExtensionId}'`); - result.push({ id: preReleaseExtensionId, preRelease: !!deprecated.preRelease }); + result.push({ id: preReleaseExtensionId, preRelease: !!extension.preRelease }); } else { result.push(extension); } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index cc9bcdd104543..46c26ac179502 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -394,7 +394,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench if (this.extensionManagementServerService.webExtensionManagementServer) { return this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.getExtensionsControlManifest(); } - return Promise.resolve({ malicious: [] }); + return Promise.resolve({ malicious: [], deprecated: {} }); } private getServer(extension: ILocalExtension): IExtensionManagementServer | null { From daf0d5e551a79a69b4482a2abaa9cbc83775e967 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 May 2022 14:37:08 -0700 Subject: [PATCH 150/942] Add skipPaths option for markdown link validation (#149859) The new `markdown.experimental.validate.fileLinks.skipPaths` setting lets you specify a list of paths (as globs) that should not be validation This is useful since markdown is used in a range of environments, and sometimes you may need to link to paths that don't exist on disk but will exist on deployment A few other changes here: - Adds a quick fix that adds paths to `skipPaths` - Rename existing settings to use the `.enabled` prefix --- .../markdown-language-features/package.json | 22 +++-- .../package.nls.json | 7 +- .../src/extension.ts | 2 +- .../src/languageFeatures/diagnostics.ts | 88 +++++++++++++++++-- .../languageFeatures/documentLinkProvider.ts | 24 ++++- .../src/test/diagnostic.test.ts | 61 +++++++++++++ .../markdown-language-features/yarn.lock | 10 +++ 7 files changed, 194 insertions(+), 20 deletions(-) diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 1af496d0fce58..b1d4d1bc52976 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -420,10 +420,10 @@ "description": "%configuration.markdown.experimental.validate.enabled.description%", "default": false }, - "markdown.experimental.validate.referenceLinks": { + "markdown.experimental.validate.referenceLinks.enabled": { "type": "string", "scope": "resource", - "markdownDescription": "%configuration.markdown.experimental.validate.referenceLinks.description%", + "markdownDescription": "%configuration.markdown.experimental.validate.referenceLinks.enabled.description%", "default": "warning", "enum": [ "ignore", @@ -431,10 +431,10 @@ "error" ] }, - "markdown.experimental.validate.headerLinks": { + "markdown.experimental.validate.headerLinks.enabled": { "type": "string", "scope": "resource", - "markdownDescription": "%configuration.markdown.experimental.validate.headerLinks.description%", + "markdownDescription": "%configuration.markdown.experimental.validate.headerLinks.enabled.description%", "default": "warning", "enum": [ "ignore", @@ -442,16 +442,24 @@ "error" ] }, - "markdown.experimental.validate.fileLinks": { + "markdown.experimental.validate.fileLinks.enabled": { "type": "string", "scope": "resource", - "markdownDescription": "%configuration.markdown.experimental.validate.fileLinks.description%", + "markdownDescription": "%configuration.markdown.experimental.validate.fileLinks.enabled.description%", "default": "warning", "enum": [ "ignore", "warning", "error" ] + }, + "markdown.experimental.validate.fileLinks.skipPaths": { + "type": "array", + "scope": "resource", + "markdownDescription": "%configuration.markdown.experimental.validate.fileLinks.skipPaths.description%", + "items": { + "type": "string" + } } } }, @@ -504,6 +512,7 @@ "markdown-it": "^12.3.2", "markdown-it-front-matter": "^0.2.1", "morphdom": "^2.6.1", + "picomatch": "^2.3.1", "vscode-languageserver-textdocument": "^1.0.4", "vscode-nls": "^5.0.0", "vscode-uri": "^3.0.3" @@ -512,6 +521,7 @@ "@types/dompurify": "^2.3.1", "@types/lodash.throttle": "^4.1.3", "@types/markdown-it": "12.2.3", + "@types/picomatch": "^2.3.0", "@types/vscode-notebook-renderer": "^1.60.0", "@types/vscode-webview": "^1.57.0", "lodash.throttle": "^4.1.1" diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index 2d9ba25800383..b2958199b91ff 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -30,8 +30,9 @@ "configuration.markdown.suggest.paths.enabled.description": "Enable/disable path suggestions for markdown links", "configuration.markdown.editor.drop.enabled": "Enable/disable dropping into the markdown editor to insert shift. Requires enabling `#workbenck.experimental.editor.dropIntoEditor.enabled#`.", "configuration.markdown.experimental.validate.enabled.description": "Enable/disable all error reporting in Markdown files.", - "configuration.markdown.experimental.validate.referenceLinks.description": "Validate reference links in Markdown files, e.g. `[link][ref]`. Requires enabling `#markdown.experimental.validate.enabled#`.", - "configuration.markdown.experimental.validate.headerLinks.description": "Validate links to headers in Markdown files, e.g. `[link](#header)`. Requires enabling `#markdown.experimental.validate.enabled#`.", - "configuration.markdown.experimental.validate.fileLinks.description": "Validate links to other files in Markdown files, e.g. `[link](/path/to/file.md)`. This checks that the target files exists. Requires enabling `#markdown.experimental.validate.enabled#`.", + "configuration.markdown.experimental.validate.referenceLinks.enabled.description": "Validate reference links in Markdown files, e.g. `[link][ref]`. Requires enabling `#markdown.experimental.validate.enabled#`.", + "configuration.markdown.experimental.validate.headerLinks.enabled.description": "Validate links to headers in Markdown files, e.g. `[link](#header)`. Requires enabling `#markdown.experimental.validate.enabled#`.", + "configuration.markdown.experimental.validate.fileLinks.enabled.description": "Validate links to other files in Markdown files, e.g. `[link](/path/to/file.md)`. This checks that the target files exists. Requires enabling `#markdown.experimental.validate.enabled#`.", + "configuration.markdown.experimental.validate.fileLinks.skipPaths.description": "Configure glob patterns for links to treat as valid, even if they don't exist in the workspace. For example `/about` would make the link `[about](/about)` valid, while the glob `/assets/**/*.svg` would let you link to any `.svg` asset under the `assets` directory", "workspaceTrust": "Required for loading styles configured in the workspace." } diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 4938c1004bf7d..f088f91391a72 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -76,7 +76,7 @@ function registerMarkdownLanguageFeatures( vscode.languages.registerRenameProvider(selector, new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier)), vscode.languages.registerDefinitionProvider(selector, new MdDefinitionProvider(referencesProvider)), MdPathCompletionProvider.register(selector, engine, linkProvider), - registerDiagnostics(engine, workspaceContents, linkProvider), + registerDiagnostics(selector, engine, workspaceContents, linkProvider, commandManager), registerDropIntoEditor(selector), registerFindFileReferences(commandManager, referencesProvider), ); diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index f53a83d670e51..eb4c436afd57b 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -5,6 +5,7 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +import * as picomatch from 'picomatch'; import { MarkdownEngine } from '../markdownEngine'; import { TableOfContents } from '../tableOfContents'; import { Delayer } from '../util/async'; @@ -14,6 +15,7 @@ import { Limiter } from '../util/limiter'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; import { InternalHref, LinkDefinitionSet, MdLink, MdLinkProvider, MdLinkSource } from './documentLinkProvider'; import { tryFindMdDocumentForLink } from './references'; +import { CommandManager } from '../commandManager'; const localize = nls.loadMessageBundle(); @@ -37,6 +39,7 @@ export interface DiagnosticOptions { readonly validateReferences: DiagnosticLevel; readonly validateOwnHeaders: DiagnosticLevel; readonly validateFilePaths: DiagnosticLevel; + readonly skipPaths: readonly string[]; } function toSeverity(level: DiagnosticLevel): vscode.DiagnosticSeverity | undefined { @@ -56,7 +59,13 @@ class VSCodeDiagnosticConfiguration extends Disposable implements DiagnosticConf super(); this._register(vscode.workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('markdown.experimental.validate.enabled')) { + if ( + e.affectsConfiguration('markdown.experimental.validate.enabled') + || e.affectsConfiguration('markdown.experimental.validate.referenceLinks.enabled') + || e.affectsConfiguration('markdown.experimental.validate.headerLinks.enabled') + || e.affectsConfiguration('markdown.experimental.validate.fileLinks.enabled') + || e.affectsConfiguration('markdown.experimental.validate.fileLinks.skipPaths') + ) { this._onDidChange.fire(); } })); @@ -66,9 +75,10 @@ class VSCodeDiagnosticConfiguration extends Disposable implements DiagnosticConf const config = vscode.workspace.getConfiguration('markdown', resource); return { enabled: config.get('experimental.validate.enabled', false), - validateReferences: config.get('experimental.validate.referenceLinks', DiagnosticLevel.ignore), - validateOwnHeaders: config.get('experimental.validate.headerLinks', DiagnosticLevel.ignore), - validateFilePaths: config.get('experimental.validate.fileLinks', DiagnosticLevel.ignore), + validateReferences: config.get('experimental.validate.referenceLinks.enabled', DiagnosticLevel.ignore), + validateOwnHeaders: config.get('experimental.validate.headerLinks.enabled', DiagnosticLevel.ignore), + validateFilePaths: config.get('experimental.validate.fileLinks.enabled', DiagnosticLevel.ignore), + skipPaths: config.get('experimental.validate.fileLinks.skipPaths', []), }; } } @@ -206,6 +216,16 @@ class LinkWatcher extends Disposable { } } +class FileDoesNotExistDiagnostic extends vscode.Diagnostic { + + public readonly path: string; + + constructor(range: vscode.Range, message: string, severity: vscode.DiagnosticSeverity, path: string) { + super(range, message, severity); + this.path = path; + } +} + export class DiagnosticManager extends Disposable { private readonly collection: vscode.DiagnosticCollection; @@ -459,9 +479,11 @@ export class DiagnosticComputer { } if (!hrefDoc && !await this.workspaceContents.pathExists(path)) { - const msg = localize('invalidPathLink', 'File does not exist at path: {0}', path.toString(true)); + const msg = localize('invalidPathLink', 'File does not exist at path: {0}', path.fsPath); for (const link of links) { - diagnostics.push(new vscode.Diagnostic(link.source.hrefRange, msg, severity)); + if (!options.skipPaths.some(glob => picomatch.isMatch(link.source.pathText, glob))) { + diagnostics.push(new FileDoesNotExistDiagnostic(link.source.hrefRange, msg, severity, link.source.pathText)); + } } } else if (hrefDoc) { // Validate each of the links to headers in the file @@ -482,12 +504,64 @@ export class DiagnosticComputer { } } +class AddToSkipPathsQuickFixProvider implements vscode.CodeActionProvider { + + private static readonly _addToSkipPathsCommandId = '_markdown.addToSkipPaths'; + + private static readonly metadata: vscode.CodeActionProviderMetadata = { + providedCodeActionKinds: [ + vscode.CodeActionKind.QuickFix + ], + }; + + public static register(selector: vscode.DocumentSelector, commandManager: CommandManager): vscode.Disposable { + const reg = vscode.languages.registerCodeActionsProvider(selector, new AddToSkipPathsQuickFixProvider(), AddToSkipPathsQuickFixProvider.metadata); + const commandReg = commandManager.register({ + id: AddToSkipPathsQuickFixProvider._addToSkipPathsCommandId, + execute(resource: vscode.Uri, path: string) { + const settingId = 'experimental.validate.fileLinks.skipPaths'; + const config = vscode.workspace.getConfiguration('markdown', resource); + const paths = new Set(config.get(settingId, [])); + paths.add(path); + config.update(settingId, [...paths], vscode.ConfigurationTarget.WorkspaceFolder); + } + }); + return vscode.Disposable.from(reg, commandReg); + } + + provideCodeActions(document: vscode.TextDocument, _range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, _token: vscode.CancellationToken): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> { + const fixes: vscode.CodeAction[] = []; + + for (const diagnostic of context.diagnostics) { + if (diagnostic instanceof FileDoesNotExistDiagnostic) { + const fix = new vscode.CodeAction( + localize('skipPathsQuickFix.title', "Add '{0}' to paths that skip link validation.", diagnostic.path), + vscode.CodeActionKind.QuickFix); + + fix.command = { + command: AddToSkipPathsQuickFixProvider._addToSkipPathsCommandId, + title: '', + arguments: [document.uri, diagnostic.path] + }; + fixes.push(fix); + } + } + + return fixes; + } +} + export function register( + selector: vscode.DocumentSelector, engine: MarkdownEngine, workspaceContents: MdWorkspaceContents, linkProvider: MdLinkProvider, + commandManager: CommandManager, ): vscode.Disposable { const configuration = new VSCodeDiagnosticConfiguration(); const manager = new DiagnosticManager(new DiagnosticComputer(engine, workspaceContents, linkProvider), configuration); - return vscode.Disposable.from(configuration, manager); + return vscode.Disposable.from( + configuration, + manager, + AddToSkipPathsQuickFixProvider.register(selector, commandManager)); } diff --git a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts index 2a239a0449193..e58094d7b7f7c 100644 --- a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts @@ -93,7 +93,16 @@ function getWorkspaceFolder(document: SkinnyTextDocument) { } export interface MdLinkSource { + /** + * The original text of the link destination in code. + */ readonly text: string; + + /** + * The original text of just the link's path in code. + */ + readonly pathText: string; + readonly resource: vscode.Uri; readonly hrefRange: vscode.Range; readonly fragmentRange: vscode.Range | undefined; @@ -138,7 +147,7 @@ function extractDocumentLink( text: link, resource: document.uri, hrefRange: new vscode.Range(linkStart, linkEnd), - fragmentRange: getFragmentRange(link, linkStart, linkEnd), + ...getLinkSourceFragmentInfo(document, link, linkStart, linkEnd), } }; } catch { @@ -154,6 +163,14 @@ function getFragmentRange(text: string, start: vscode.Position, end: vscode.Posi return new vscode.Range(start.translate({ characterDelta: index + 1 }), end); } +function getLinkSourceFragmentInfo(document: SkinnyTextDocument, link: string, linkStart: vscode.Position, linkEnd: vscode.Position): { fragmentRange: vscode.Range | undefined; pathText: string } { + const fragmentRange = getFragmentRange(link, linkStart, linkEnd); + return { + pathText: document.getText(new vscode.Range(linkStart, fragmentRange ? fragmentRange.start.translate(0, -1) : linkEnd)), + fragmentRange, + }; +} + const angleBracketLinkRe = /^<(.*)>$/; /** @@ -314,7 +331,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider { text: link, resource: document.uri, hrefRange: new vscode.Range(linkStart, linkEnd), - fragmentRange: getFragmentRange(link, linkStart, linkEnd), + ...getLinkSourceFragmentInfo(document, link, linkStart, linkEnd), } }; } @@ -350,6 +367,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider { kind: 'link', source: { text: reference, + pathText: reference, resource: document.uri, hrefRange, fragmentRange: undefined, @@ -402,7 +420,7 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider { text: link, resource: document.uri, hrefRange, - fragmentRange: getFragmentRange(link, linkStart, linkEnd), + ...getLinkSourceFragmentInfo(document, link, linkStart, linkEnd), }, ref: { text: reference, range: refRange }, href: target, diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index a9f54978b7d9c..5d99d30bb523a 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -26,6 +26,7 @@ async function getComputedDiagnostics(doc: InMemoryDocument, workspaceContents: validateFilePaths: DiagnosticLevel.warning, validateOwnHeaders: DiagnosticLevel.warning, validateReferences: DiagnosticLevel.warning, + skipPaths: [], }, noopToken) ).diagnostics; } @@ -43,6 +44,7 @@ class MemoryDiagnosticConfiguration implements DiagnosticConfiguration { constructor( private readonly enabled: boolean = true, + private readonly skipPaths: string[] = [], ) { } getOptions(_resource: vscode.Uri): DiagnosticOptions { @@ -52,6 +54,7 @@ class MemoryDiagnosticConfiguration implements DiagnosticConfiguration { validateFilePaths: DiagnosticLevel.ignore, validateOwnHeaders: DiagnosticLevel.ignore, validateReferences: DiagnosticLevel.ignore, + skipPaths: this.skipPaths, }; } return { @@ -59,6 +62,7 @@ class MemoryDiagnosticConfiguration implements DiagnosticConfiguration { validateFilePaths: DiagnosticLevel.warning, validateOwnHeaders: DiagnosticLevel.warning, validateReferences: DiagnosticLevel.warning, + skipPaths: this.skipPaths, }; } } @@ -179,4 +183,61 @@ suite('markdown: Diagnostics', () => { const diagnostics = await getComputedDiagnostics(doc1, new InMemoryWorkspaceMarkdownDocuments([doc1])); assert.deepStrictEqual(diagnostics.length, 0); }); + + test('Should allow ignoring invalid file link using glob', async () => { + const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( + `[text](/no-such-file)`, + `![img](/no-such-file)`, + `[text]: /no-such-file`, + )); + + const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(true, ['/no-such-file'])); + const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); + assert.deepStrictEqual(diagnostics.length, 0); + }); + + test('skipPaths should allow skipping non-existent file', async () => { + const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( + `[text](/no-such-file#header)`, + )); + + const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(true, ['/no-such-file'])); + const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); + assert.deepStrictEqual(diagnostics.length, 0); + }); + + test('skipPaths should not consider link fragment', async () => { + const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( + `[text](/no-such-file#header)`, + )); + + const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(true, ['/no-such-file'])); + const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); + assert.deepStrictEqual(diagnostics.length, 0); + }); + + test('skipPaths should support globs', async () => { + const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( + `![i](/images/aaa.png)`, + `![i](/images/sub/bbb.png)`, + `![i](/images/sub/sub2/ccc.png)`, + )); + + const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(true, ['/images/**/*.png'])); + const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); + assert.deepStrictEqual(diagnostics.length, 0); + }); + + test('skipPaths should resolve relative to file', async () => { + const doc1 = new InMemoryDocument(workspacePath('sub', 'doc1.md'), joinLines( + `![i](images/aaa.png)`, + `![i](images/sub/bbb.png)`, + `![i](images/sub/sub2/ccc.png)`, + `![i](/images/sub/sub2/ccc.png)`, + )); + + const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(true, ['images/**/*.png'])); + const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); + assert.deepStrictEqual(diagnostics.length, 0); + }); }); diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 5268b01f46cbd..eaa76d9abbf54 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -39,6 +39,11 @@ resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== +"@types/picomatch@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" + integrity sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g== + "@types/trusted-types@*": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" @@ -117,6 +122,11 @@ morphdom@^2.6.1: resolved "https://registry.yarnpkg.com/morphdom/-/morphdom-2.6.1.tgz#e868e24f989fa3183004b159aed643e628b4306e" integrity sha512-Y8YRbAEP3eKykroIBWrjcfMw7mmwJfjhqdpSvoqinu8Y702nAwikpXcNFDiIkyvfCLxLM9Wu95RZqo4a9jFBaA== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" From a49e21c63b6e40a33206df55d7bb40beaee2f406 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 18 May 2022 15:37:22 -0700 Subject: [PATCH 151/942] Fix notebook output double spacing (#149842) Fixes #149563 --- extensions/notebook-renderers/src/textHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/notebook-renderers/src/textHelper.ts b/extensions/notebook-renderers/src/textHelper.ts index af53635710d0b..9daafb721f81a 100644 --- a/extensions/notebook-renderers/src/textHelper.ts +++ b/extensions/notebook-renderers/src/textHelper.ts @@ -25,7 +25,7 @@ function generateViewMoreElement(outputId: string) { } export function truncatedArrayOfString(id: string, outputs: string[], linesLimit: number, container: HTMLElement) { - let buffer = outputs.join('\n').split(/\r|\n|\r\n/g); + let buffer = outputs.join('\n').split(/\r\n|\r|\n/g); let lineCount = buffer.length; if (lineCount < linesLimit) { From 27903c28aa767fec440eeed72826af79152bb3b4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 May 2022 16:01:28 -0700 Subject: [PATCH 152/942] Remove unfinished test (#149864) Mistakenly checked this in --- .../src/test/diagnostic.test.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index 5d99d30bb523a..e58dabd983e90 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -227,17 +227,4 @@ suite('markdown: Diagnostics', () => { const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); assert.deepStrictEqual(diagnostics.length, 0); }); - - test('skipPaths should resolve relative to file', async () => { - const doc1 = new InMemoryDocument(workspacePath('sub', 'doc1.md'), joinLines( - `![i](images/aaa.png)`, - `![i](images/sub/bbb.png)`, - `![i](images/sub/sub2/ccc.png)`, - `![i](/images/sub/sub2/ccc.png)`, - )); - - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(true, ['images/**/*.png'])); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); - }); }); From 0d55d5017316ebe7c799b86cea03fc9d26687b7f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 May 2022 16:14:32 -0700 Subject: [PATCH 153/942] Expose outputItem in notebook markdown eve (#149870) For #121256 This change adds the current `ouputItem` to the notebook markdown renderer's environment Renders that extend our markdown renderer can use this to access output item metadata for example --- extensions/markdown-language-features/notebook/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/notebook/index.ts b/extensions/markdown-language-features/notebook/index.ts index 3fdb86bd8d74c..079ed928f3f57 100644 --- a/extensions/markdown-language-features/notebook/index.ts +++ b/extensions/markdown-language-features/notebook/index.ts @@ -207,7 +207,9 @@ export const activate: ActivationFunction = (ctx) => { previewNode.classList.remove('emptyMarkdownCell'); const markdownText = outputInfo.mime.startsWith('text/x-') ? `\`\`\`${outputInfo.mime.substr(7)}\n${text}\n\`\`\`` : (outputInfo.mime.startsWith('application/') ? `\`\`\`${outputInfo.mime.substr(12)}\n${text}\n\`\`\`` : text); - const unsanitizedRenderedMarkdown = markdownIt.render(markdownText); + const unsanitizedRenderedMarkdown = markdownIt.render(markdownText, { + outputItem: outputInfo, + }); previewNode.innerHTML = (ctx.workspace.isTrusted ? unsanitizedRenderedMarkdown : DOMPurify.sanitize(unsanitizedRenderedMarkdown, sanitizerOptions)) as string; From 73b2655e9b2d21388ecbd8bb22ed599bd1c0e5a3 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 19 May 2022 01:30:32 +0200 Subject: [PATCH 154/942] Lots of bug fixing & minor improvements. --- src/vs/base/common/arrays.ts | 30 +- .../mergeEditor/browser/editorGutter.ts | 141 ++++++ .../mergeEditor/browser/editorGutterWidget.ts | 100 ----- .../mergeEditor/browser/mergeEditor.ts | 401 ++++++++++-------- .../mergeEditor/browser/mergeEditorModel.ts | 247 +++++++---- .../contrib/mergeEditor/browser/model.ts | 231 ++++------ .../contrib/mergeEditor/browser/utils.ts | 169 ++++++++ 7 files changed, 812 insertions(+), 507 deletions(-) create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts delete mode 100644 src/vs/workbench/contrib/mergeEditor/browser/editorGutterWidget.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/utils.ts diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index e876d9d4304ae..826d3558c35f3 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -640,12 +640,38 @@ function getActualStartIndex(array: T[], start: number): number { return start < 0 ? Math.max(start + array.length, 0) : Math.min(start, array.length); } +/** + * When comparing two values, + * a negative number indicates that the first value is less than the second, + * a positive number indicates that the first value is greater than the second, + * and zero indicates that neither is the case. +*/ +export type CompareResult = number; + +export namespace CompareResult { + export function isLessThan(result: CompareResult): boolean { + return result < 0; + } + + export function isGreaterThan(result: CompareResult): boolean { + return result > 0; + } + + export function isNeitherLessOrGreaterThan(result: CompareResult): boolean { + return result === 0; + } + + export const greaterThan = 1; + export const lessThan = -1; + export const neitherLessOrGreaterThan = 0; +} + /** * A comparator `c` defines a total order `<=` on `T` as following: * `c(a, b) <= 0` iff `a` <= `b`. * We also have `c(a, b) == 0` iff `c(b, a) == 0`. */ -export type Comparator = (a: T, b: T) => number; +export type Comparator = (a: T, b: T) => CompareResult; export function compareBy(selector: (item: TItem) => TCompareBy, comparator: Comparator): Comparator { return (a, b) => comparator(selector(a), selector(b)); @@ -706,7 +732,7 @@ export class ArrayQueue { /** * Constructs a queue that is backed by the given array. Runtime is O(1). */ - constructor(private readonly items: T[]) { } + constructor(private readonly items: readonly T[]) { } get length(): number { return this.lastIdx - this.firstIdx + 1; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts b/src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts new file mode 100644 index 0000000000000..c439048920920 --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts @@ -0,0 +1,141 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { autorun, IReader, observableFromEvent, ObservableValue } from 'vs/workbench/contrib/audioCues/browser/observable'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; + +export class EditorGutter< + T extends IGutterItemInfo = IGutterItemInfo + > extends Disposable { + private readonly scrollTop = observableFromEvent( + this._editor.onDidScrollChange, + (e) => this._editor.getScrollTop() + ); + private readonly modelAttached = observableFromEvent( + this._editor.onDidChangeModel, + (e) => this._editor.hasModel() + ); + + private readonly viewZoneChanges = new ObservableValue(0, 'counter'); + + constructor( + private readonly _editor: CodeEditorWidget, + private readonly _domNode: HTMLElement, + private readonly itemProvider: IGutterItemProvider + ) { + super(); + this._domNode.className = 'gutter'; + this._register(autorun((reader) => this.render(reader), 'Render')); + + this._editor.onDidChangeViewZones(e => { + this.viewZoneChanges.set(this.viewZoneChanges.get() + 1, undefined); + }); + } + + private readonly views = new Map(); + + private render(reader: IReader): void { + if (!this.modelAttached.read(reader)) { + return; + } + this.viewZoneChanges.read(reader); + const scrollTop = this.scrollTop.read(reader); + + const visibleRanges = this._editor.getVisibleRanges(); + const unusedIds = new Set(this.views.keys()); + + if (visibleRanges.length > 0) { + const visibleRange = visibleRanges[0]; + + const visibleRange2 = new LineRange( + visibleRange.startLineNumber, + visibleRange.endLineNumber - visibleRange.startLineNumber + ); + + const gutterItems = this.itemProvider.getIntersectingGutterItems( + visibleRange2, + reader + ); + + const lineHeight = this._editor.getOptions().get(EditorOption.lineHeight); + + for (const gutterItem of gutterItems) { + if (!gutterItem.range.touches(visibleRange2)) { + continue; + } + + unusedIds.delete(gutterItem.id); + let view = this.views.get(gutterItem.id); + if (!view) { + const viewDomNode = document.createElement('div'); + viewDomNode.className = 'gutter-item'; + this._domNode.appendChild(viewDomNode); + const itemView = this.itemProvider.createView( + gutterItem, + viewDomNode + ); + view = new ManagedGutterItemView(itemView, viewDomNode); + this.views.set(gutterItem.id, view); + } else { + view.gutterItemView.update(gutterItem); + } + + const top = + this._editor.getTopForLineNumber(gutterItem.range.startLineNumber - 1) - + scrollTop + lineHeight; + + // +1 line and -lineHeight makes the height to cover view zones at the end of the range. + const bottom = + this._editor.getTopForLineNumber(gutterItem.range.endLineNumberExclusive + 1) - + scrollTop - lineHeight; + + const height = bottom - top; + + view.domNode.style.top = `${top}px`; + view.domNode.style.height = `${height}px`; + + view.gutterItemView.layout(top, height, 0, -1); + } + } + + for (const id of unusedIds) { + const view = this.views.get(id)!; + view.gutterItemView.dispose(); + this._domNode.removeChild(view.domNode); + this.views.delete(id); + } + } +} + +class ManagedGutterItemView { + constructor( + public readonly gutterItemView: IGutterItemView, + public readonly domNode: HTMLDivElement + ) { } +} + +export interface IGutterItemProvider { + getIntersectingGutterItems(range: LineRange, reader: IReader): TItem[]; + + createView(item: TItem, target: HTMLElement): IGutterItemView; +} + +export interface IGutterItemInfo { + id: string; + range: LineRange; + + // To accommodate view zones: + offsetInPx: number; + additionalHeightInPx: number; +} + +export interface IGutterItemView extends IDisposable { + update(item: T): void; + layout(top: number, height: number, viewTop: number, viewHeight: number): void; +} + diff --git a/src/vs/workbench/contrib/mergeEditor/browser/editorGutterWidget.ts b/src/vs/workbench/contrib/mergeEditor/browser/editorGutterWidget.ts deleted file mode 100644 index 9b3954eed488f..0000000000000 --- a/src/vs/workbench/contrib/mergeEditor/browser/editorGutterWidget.ts +++ /dev/null @@ -1,100 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IDisposable } from 'vs/base/common/lifecycle'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; - -export class EditorGutterWidget { - constructor( - private readonly _editor: ICodeEditor, - private readonly _domNode: HTMLElement, - private readonly itemProvider: IGutterItemProvider, - ) { - this._domNode.className = 'gutter'; - - this._editor.onDidScrollChange(() => { - this.render(); - }); - } - - private readonly views = new Map(); - - private render(): void { - const visibleRange = this._editor.getVisibleRanges()[0]; - const visibleRange2 = new LineRange( - visibleRange.startLineNumber, - visibleRange.endLineNumber - visibleRange.startLineNumber - ); - - const gutterItems = this.itemProvider.getIntersectingGutterItems(visibleRange2); - - const lineHeight = this._editor.getOptions().get(EditorOption.lineHeight); - - const unusedIds = new Set(this.views.keys()); - for (const gutterItem of gutterItems) { - if (!gutterItem.range.touches(visibleRange2)) { - continue; - } - - unusedIds.delete(gutterItem.id); - let view = this.views.get(gutterItem.id); - if (!view) { - const viewDomNode = document.createElement('div'); - viewDomNode.className = 'gutter-item'; - this._domNode.appendChild(viewDomNode); - const itemView = this.itemProvider.createView(gutterItem, viewDomNode); - view = new ManagedGutterItemView(itemView, viewDomNode); - this.views.set(gutterItem.id, view); - } - - const scrollTop = this._editor.getScrollTop(); - const top = this._editor.getTopForLineNumber(gutterItem.range.startLineNumber) - scrollTop; - const height = lineHeight * gutterItem.range.lineCount; - - view.domNode.style.top = `${top}px`; - view.domNode.style.height = `${height}px`; - - view.gutterItemView.layout(top, height, 0, -1); - } - - for (const id of unusedIds) { - const view = this.views.get(id)!; - view.gutterItemView.dispose(); - this._domNode.removeChild(view.domNode); - this.views.delete(id); - } - } -} - -class ManagedGutterItemView { - constructor( - public readonly gutterItemView: IGutterItemView, - public readonly domNode: HTMLDivElement - ) { } -} - -export interface IGutterItemProvider { - // onDidChange - getIntersectingGutterItems(range: LineRange): TItem[]; - - createView(item: TItem, target: HTMLElement): IGutterItemView; -} - -export interface IGutterItemInfo { - id: string; - range: LineRange; - - // To accommodate view zones: - offsetInPx: number; - additionalHeightInPx: number; -} - -export interface IGutterItemView extends IDisposable { - update(item: T): void; - layout(top: number, height: number, viewTop: number, viewHeight: number): void; -} - diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 3cb4562da9e9f..f46dce24ff15b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -15,13 +15,17 @@ import { Color } from 'vs/base/common/color'; import { BugIndicatingError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { deepClone } from 'vs/base/common/objects'; import { noBreakWhitespace } from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/mergeEditor'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { IEditorConfiguration } from 'vs/editor/common/config/editorConfiguration'; import { Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; import { localize } from 'vs/nls'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; @@ -36,11 +40,12 @@ import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorControl, IEditorOpenContext } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { autorun, derivedObservable, IObservable, ITransaction } from 'vs/workbench/contrib/audioCues/browser/observable'; +import { autorun, derivedObservable, IObservable, ITransaction, ObservableValue } from 'vs/workbench/contrib/audioCues/browser/observable'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; -import { ModifiedBaseRangeState, ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; +import { applyObservableDecorations, n, ReentrancyBarrier, setStyle } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; -import { EditorGutterWidget, IGutterItemInfo, IGutterItemView } from './editorGutterWidget'; +import { EditorGutter, IGutterItemInfo, IGutterItemView } from './editorGutter'; export const ctxIsMergeEditor = new RawContextKey('isMergeEditor', false); export const ctxUsesColumnLayout = new RawContextKey('mergeEditorUsesColumnLayout', false); @@ -51,11 +56,11 @@ export class MergeEditor extends EditorPane { private readonly _sessionDisposables = new DisposableStore(); - private _grid!: Grid; + private _grid!: Grid; - private readonly input1View = this.instantiation.createInstance(CodeEditorView, { readonly: true }); - private readonly input2View = this.instantiation.createInstance(CodeEditorView, { readonly: true }); - private readonly inputResultView = this.instantiation.createInstance(CodeEditorView, { readonly: false }); + private readonly input1View = this.instantiation.createInstance(InputCodeEditorView, 1, { readonly: true }); + private readonly input2View = this.instantiation.createInstance(InputCodeEditorView, 2, { readonly: true }); + private readonly inputResultView = this.instantiation.createInstance(ResultCodeEditorView, { readonly: false }); private readonly _ctxIsMergeEditor: IContextKey; private readonly _ctxUsesColumnLayout: IContextKey; @@ -68,6 +73,7 @@ export class MergeEditor extends EditorPane { @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, @IThemeService themeService: IThemeService, + @ITextResourceConfigurationService private readonly textResourceConfigurationService: ITextResourceConfigurationService, ) { super(MergeEditor.ID, telemetryService, themeService, storageService); @@ -75,6 +81,7 @@ export class MergeEditor extends EditorPane { this._ctxUsesColumnLayout = ctxUsesColumnLayout.bindTo(_contextKeyService); const reentrancyBarrier = new ReentrancyBarrier(); + this._store.add(this.input1View.editor.onDidScrollChange(c => { if (c.scrollTopChanged) { reentrancyBarrier.runExclusively(() => { @@ -100,6 +107,7 @@ export class MergeEditor extends EditorPane { } })); + // TODO@jrieken make this proper: add menu id and allow extensions to contribute const toolbarMenu = this._menuService.createMenu(MenuId.MergeToolbar, this._contextKeyService); const toolbarMenuDisposables = new DisposableStore(); @@ -128,6 +136,11 @@ export class MergeEditor extends EditorPane { super.dispose(); } + // TODO use this method & make it private + getEditorOptions(resource: URI): IEditorConfiguration { + return deepClone(this.textResourceConfigurationService.getValue(resource)); + } + protected createEditor(parent: HTMLElement): void { parent.classList.add('merge-editor'); @@ -138,14 +151,14 @@ export class MergeEditor extends EditorPane { { size: 38, groups: [{ - data: this.input1View + data: this.input1View.view }, { - data: this.input2View + data: this.input2View.view }] }, { size: 62, - data: this.inputResultView + data: this.inputResultView.view }, ] }, { @@ -170,113 +183,48 @@ export class MergeEditor extends EditorPane { this._sessionDisposables.clear(); const model = await input.resolve(); - this.input1View.setModel(model.input1, localize('yours', 'Yours'), model.input1Detail, model.input1Description); - this.input2View.setModel(model.input2, localize('theirs', 'Theirs',), model.input2Detail, model.input1Description); - this.inputResultView.setModel(model.result, localize('result', 'Result',), this._labelService.getUriLabel(model.result.uri, { relative: true }), undefined); + this.input1View.setModel(model, model.input1, localize('yours', 'Yours'), model.input1Detail, model.input1Description); + this.input2View.setModel(model, model.input2, localize('theirs', 'Theirs',), model.input2Detail, model.input1Description); + this.inputResultView.setModel(model, model.result, localize('result', 'Result',), this._labelService.getUriLabel(model.result.uri, { relative: true }), undefined); - let input1Decorations = new Array(); - let input2Decorations = new Array(); + // TODO: Update editor options! + const input1ViewZoneIds: string[] = []; + const input2ViewZoneIds: string[] = []; for (const m of model.modifiedBaseRanges) { - if (!m.input1Range.isEmpty) { - input1Decorations.push({ - range: new Range(m.input1Range.startLineNumber, 1, m.input1Range.endLineNumberExclusive - 1, 1), - options: { - isWholeLine: true, - className: 'merge-accept-foo', - description: 'foo2' - } - }); - } - - if (!m.input2Range.isEmpty) { - input2Decorations.push({ - range: new Range(m.input2Range.startLineNumber, 1, m.input2Range.endLineNumberExclusive - 1, 1), - options: { - isWholeLine: true, - className: 'merge-accept-foo', - description: 'foo2' - } - }); - } - const max = Math.max(m.input1Range.lineCount, m.input2Range.lineCount, 1); this.input1View.editor.changeViewZones(a => { - a.addZone({ + input1ViewZoneIds.push(a.addZone({ afterLineNumber: m.input1Range.endLineNumberExclusive - 1, heightInLines: max - m.input1Range.lineCount, domNode: $('div.diagonal-fill'), - }); + })); }); this.input2View.editor.changeViewZones(a => { - a.addZone({ + input2ViewZoneIds.push(a.addZone({ afterLineNumber: m.input2Range.endLineNumberExclusive - 1, heightInLines: max - m.input2Range.lineCount, domNode: $('div.diagonal-fill'), - }); + })); }); } - this.input1View.editor.deltaDecorations([], input1Decorations); - this.input2View.editor.deltaDecorations([], input2Decorations); - - new EditorGutterWidget(this.input1View.editor, this.input1View._gutterDiv, { - getIntersectingGutterItems: (range) => - model.modifiedBaseRanges - .filter((r) => r.input1Diffs.length > 0) - .map((baseRange, idx) => ({ - id: idx.toString(), - additionalHeightInPx: 0, - offsetInPx: 0, - range: baseRange.input1Range, - toggleState: derivedObservable( - 'toggle', - (reader) => model.getState(baseRange).read(reader)?.input1 - ), - setState(value, tx) { - model.setState( - baseRange, - ( - model.getState(baseRange).get() || - new ModifiedBaseRangeState(false, false, false) - ).withInput1(value), - tx - ); - }, - })), - createView: (item, target) => new ButtonView(item, target), - }); - - new EditorGutterWidget(this.input2View.editor, this.input2View._gutterDiv, { - getIntersectingGutterItems: (range) => - model.modifiedBaseRanges - .filter((r) => r.input2Diffs.length > 0) - .map((baseRange, idx) => ({ - id: idx.toString(), - additionalHeightInPx: 0, - offsetInPx: 0, - range: baseRange.input2Range, - baseRange, - toggleState: derivedObservable( - 'toggle', - (reader) => model.getState(baseRange).read(reader)?.input2 - ), - setState(value, tx) { - model.setState( - baseRange, - ( - model.getState(baseRange).get() || - new ModifiedBaseRangeState(false, false, false) - ).withInput2(value), - tx - ); - }, - })), - createView: (item, target) => new ButtonView(item, target), + this._sessionDisposables.add({ + dispose: () => { + this.input1View.editor.changeViewZones(a => { + for (const zone of input1ViewZoneIds) { + a.removeZone(zone); + } + }); + this.input2View.editor.changeViewZones(a => { + for (const zone of input2ViewZoneIds) { + a.removeZone(zone); + } + }); + } }); - } protected override setEditorVisible(visible: boolean): void { @@ -307,23 +255,170 @@ export class MergeEditor extends EditorPane { toggleLayout(): void { if (!this._usesColumnLayout) { - this._grid.moveView(this.inputResultView, Sizing.Distribute, this.input1View, Direction.Right); + this._grid.moveView(this.inputResultView.view, Sizing.Distribute, this.input1View.view, Direction.Right); } else { - this._grid.moveView(this.inputResultView, this._grid.height * .62, this.input1View, Direction.Down); - this._grid.moveView(this.input2View, Sizing.Distribute, this.input1View, Direction.Right); + this._grid.moveView(this.inputResultView.view, this._grid.height * .62, this.input1View.view, Direction.Down); + this._grid.moveView(this.input2View.view, Sizing.Distribute, this.input1View.view, Direction.Right); } this._usesColumnLayout = !this._usesColumnLayout; this._ctxUsesColumnLayout.set(this._usesColumnLayout); } } -interface ButtonViewData extends IGutterItemInfo { +interface ICodeEditorViewOptions { + readonly: boolean; +} + + +abstract class CodeEditorView extends Disposable { + private readonly _model = new ObservableValue(undefined, 'model'); + protected readonly model: IObservable = this._model; + + protected readonly htmlElements = n('div.code-view', [ + n('div.title', { $: 'title' }), + n('div.container', [ + n('div.gutter', { $: 'gutterDiv' }), + n('div', { $: 'editor' }), + ]), + ]); + + private readonly _onDidViewChange = new Emitter(); + + public readonly view: IView = { + element: this.htmlElements.root, + minimumWidth: 10, + maximumWidth: Number.MAX_SAFE_INTEGER, + minimumHeight: 10, + maximumHeight: Number.MAX_SAFE_INTEGER, + onDidChange: this._onDidViewChange.event, + + layout: (width: number, height: number, top: number, left: number) => { + setStyle(this.htmlElements.root, { width, height, top, left }); + this.editor.layout({ + width: width - this.htmlElements.gutterDiv.clientWidth, + height: height - this.htmlElements.title.clientHeight, + }); + } + + // preferredWidth?: number | undefined; + // preferredHeight?: number | undefined; + // priority?: LayoutPriority | undefined; + // snap?: boolean | undefined; + }; + + private readonly _title = new IconLabel(this.htmlElements.title, { supportIcons: true }); + private readonly _detail = new IconLabel(this.htmlElements.title, { supportIcons: true }); + + public readonly editor = this.instantiationService.createInstance( + CodeEditorWidget, + this.htmlElements.editor, + { + minimap: { enabled: false }, + readOnly: this._options.readonly, + glyphMargin: false, + lineNumbersMinChars: 2, + }, + { contributions: [] } + ); + + constructor( + private readonly _options: ICodeEditorViewOptions, + @IInstantiationService + private readonly instantiationService: IInstantiationService + ) { + super(); + } + + public setModel( + model: MergeEditorModel, + textModel: ITextModel, + title: string, + description: string | undefined, + detail: string | undefined + ): void { + this.editor.setModel(textModel); + this._title.setLabel(title, description); + this._detail.setLabel('', detail); + + this._model.set(model, undefined); + } +} + +class InputCodeEditorView extends CodeEditorView { + private readonly decorations = derivedObservable('decorations', reader => { + const model = this.model.read(reader); + if (!model) { + return []; + } + const result = new Array(); + for (const m of model.modifiedBaseRanges) { + const range = m.getInputRange(this.inputNumber); + if (!range.isEmpty) { + result.push({ + range: new Range(range.startLineNumber, 1, range.endLineNumberExclusive - 1, 1), + options: { + isWholeLine: true, + className: 'merge-accept-foo', + description: 'foo2' + } + }); + } + } + return result; + }); + + constructor( + public readonly inputNumber: 1 | 2, + options: ICodeEditorViewOptions, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(options, instantiationService); + + this._register(applyObservableDecorations(this.editor, this.decorations)); + + this._register( + new EditorGutter(this.editor, this.htmlElements.gutterDiv, { + getIntersectingGutterItems: (range, reader) => { + const model = this.model.read(reader); + if (!model) { return []; } + return model.modifiedBaseRanges + .filter((r) => r.getInputDiffs(this.inputNumber).length > 0) + .map((baseRange, idx) => ({ + id: idx.toString(), + additionalHeightInPx: 0, + offsetInPx: 0, + range: baseRange.getInputRange(this.inputNumber), + toggleState: derivedObservable('toggle', (reader) => + model + .getState(baseRange) + .read(reader) + .getInput(this.inputNumber) + ), + setState: (value, tx) => + model.setState( + baseRange, + model + .getState(baseRange) + .get() + .withInputValue(this.inputNumber, value), + tx + ), + })); + }, + createView: (item, target) => + new MergeConflictGutterItemView(item, target), + }) + ); + } +} + +interface MergeConflictData extends IGutterItemInfo { toggleState: IObservable; setState(value: boolean, tx: ITransaction | undefined): void; } -class ButtonView extends Disposable implements IGutterItemView { - constructor(item: ButtonViewData, target: HTMLElement) { +class MergeConflictGutterItemView extends Disposable implements IGutterItemView { + constructor(private item: MergeConflictData, target: HTMLElement) { super(); target.classList.add('merge-accept-gutter-marker'); @@ -334,93 +429,57 @@ class ButtonView extends Disposable implements IGutterItemView { this._register( autorun((reader) => { - const value = item.toggleState.read(reader); + const value = this.item.toggleState.read(reader); checkBox.checked = value === true; }, 'Update Toggle State') ); this._register(checkBox.onChange(() => { - item.setState(checkBox.checked, undefined); + this.item.setState(checkBox.checked, undefined); })); target.appendChild($('div.background', {}, noBreakWhitespace)); target.appendChild($('div.checkbox', {}, checkBox.domNode)); } + layout(top: number, height: number, viewTop: number, viewHeight: number): void { } - update(baseRange: ButtonViewData): void { + update(baseRange: MergeConflictData): void { + this.item = baseRange; } } -interface ICodeEditorViewOptions { - readonly: boolean; -} - -class CodeEditorView implements IView { - - // preferredWidth?: number | undefined; - // preferredHeight?: number | undefined; - - element: HTMLElement; - private readonly _titleElement: HTMLElement; - private readonly _editorElement: HTMLElement; - public readonly _gutterDiv: HTMLElement; - - minimumWidth: number = 10; - maximumWidth: number = Number.MAX_SAFE_INTEGER; - minimumHeight: number = 10; - maximumHeight: number = Number.MAX_SAFE_INTEGER; - // priority?: LayoutPriority | undefined; - // snap?: boolean | undefined; - - private readonly _onDidChange = new Emitter(); - readonly onDidChange = this._onDidChange.event; - - private readonly _title: IconLabel; - private readonly _detail: IconLabel; - - public readonly editor: CodeEditorWidget; - // private readonly gutter: EditorGutterWidget; - +class ResultCodeEditorView extends CodeEditorView { + private readonly decorations = derivedObservable('decorations', reader => { + const model = this.model.read(reader); + if (!model) { + return []; + } + const result = new Array(); + for (const m of model.resultDiffs.read(reader)) { + const range = m.modifiedRange; + if (!range.isEmpty) { + result.push({ + range: new Range(range.startLineNumber, 1, range.endLineNumberExclusive - 1, 1), + options: { + isWholeLine: true, + className: 'merge-accept-foo', + description: 'foo2' + } + }); + } + } + return result; + }); constructor( - private readonly _options: ICodeEditorViewOptions, - @IInstantiationService private readonly instantiationService: IInstantiationService + options: ICodeEditorViewOptions, + @IInstantiationService instantiationService: IInstantiationService ) { - this.element = $( - 'div.code-view', - {}, - this._titleElement = $('div.title'), - $('div.container', {}, - this._gutterDiv = $('div.gutter'), - this._editorElement = $('div'), - ), - ); - - this.editor = this.instantiationService.createInstance( - CodeEditorWidget, - this._editorElement, - { minimap: { enabled: false }, readOnly: this._options.readonly, glyphMargin: false, lineNumbersMinChars: 2 }, - { contributions: [] } - ); - - this._title = new IconLabel(this._titleElement, { supportIcons: true }); - this._detail = new IconLabel(this._titleElement, { supportIcons: true }); - } - - public setModel(model: ITextModel, title: string, description: string | undefined, detail: string | undefined): void { - this.editor.setModel(model); - this._title.setLabel(title, description); - this._detail.setLabel('', detail); - } + super(options, instantiationService); - layout(width: number, height: number, top: number, left: number): void { - this.element.style.width = `${width}px`; - this.element.style.height = `${height}px`; - this.element.style.top = `${top}px`; - this.element.style.left = `${left}px`; - this.editor.layout({ width: width - this._gutterDiv.clientWidth, height: height - this._titleElement.clientHeight }); + this._register(applyObservableDecorations(this.editor, this.decorations)); } } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts index ef5003afc4c8f..024aa737d426a 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter } from 'vs/base/common/event'; -import { compareBy, numberComparator } from 'vs/base/common/arrays'; +import { compareBy, CompareResult, equals, numberComparator } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { ModifiedBaseRange, LineEdit, LineDiff, ModifiedBaseRangeState, LineRange, ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { ModifiedBaseRange, LineEdit, LineDiff, ModifiedBaseRangeState, LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { leftJoin, ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils'; export class MergeEditorModelFactory { constructor( @@ -41,10 +42,17 @@ export class MergeEditorModelFactory { false, 1000 ); + const baseToResultDiffPromise = this._editorWorkerService.computeDiff( + base.uri, + result.uri, + false, + 1000 + ); - const [baseToInput1Diff, baseToInput2Diff] = await Promise.all([ + const [baseToInput1Diff, baseToInput2Diff, baseToResultDiff] = await Promise.all([ baseToInput1DiffPromise, baseToInput2DiffPromise, + baseToResultDiffPromise ]); const changesInput1 = @@ -55,6 +63,10 @@ export class MergeEditorModelFactory { baseToInput2Diff?.changes.map((c) => LineDiff.fromLineChange(c, base, input2) ) || []; + const changesResult = + baseToResultDiff?.changes.map((c) => + LineDiff.fromLineChange(c, base, result) + ) || []; return new MergeEditorModel( InternalSymbol, @@ -68,7 +80,8 @@ export class MergeEditorModelFactory { result, changesInput1, changesInput2, - this._editorWorkerService + changesResult, + this._editorWorkerService, ); } } @@ -76,7 +89,7 @@ export class MergeEditorModelFactory { const InternalSymbol: unique symbol = null!; export class MergeEditorModel extends EditorModel { - private resultEdits = new ResultEdits([], this.base, this.result, this.editorWorkerService); + private resultEdits: ResultEdits; constructor( _symbol: typeof InternalSymbol, @@ -88,77 +101,104 @@ export class MergeEditorModel extends EditorModel { readonly input2Detail: string | undefined, readonly input2Description: string | undefined, readonly result: ITextModel, - private readonly inputOneLinesDiffs: readonly LineDiff[], - private readonly inputTwoLinesDiffs: readonly LineDiff[], + private readonly input1LinesDiffs: readonly LineDiff[], + private readonly input2LinesDiffs: readonly LineDiff[], + resultDiffs: LineDiff[], private readonly editorWorkerService: IEditorWorkerService ) { super(); - result.setValue(base.getValue()); - + this.resultEdits = new ResultEdits(resultDiffs, this.base, this.result, this.editorWorkerService); this.resultEdits.onDidChange(() => { - transaction(tx => { - for (const [key, value] of this.modifiedBaseRangeStateStores) { - value.set(this.computeState(key), tx); - } - }); + this.recomputeState(); }); + this.recomputeState(); + } - /* - // Apply all non-conflicts diffs - const lineEditsArr: LineEdit[] = []; - for (const diff of this.mergeableDiffs) { - if (!diff.isConflicting) { - for (const d of diff.inputOneDiffs) { - lineEditsArr.push(d.getLineEdit()); - } - for (const d of diff.inputTwoDiffs) { - lineEditsArr.push(d.getLineEdit()); + private recomputeState(): void { + transaction(tx => { + const baseRangeWithStoreAndTouchingDiffs = leftJoin( + this.modifiedBaseRangeStateStores, + this.resultEdits.diffs.get(), + (baseRange, diff) => + baseRange[0].baseRange.touches(diff.originalRange) + ? CompareResult.neitherLessOrGreaterThan + : LineRange.compareByStart( + baseRange[0].baseRange, + diff.originalRange + ) + ); + + for (const row of baseRangeWithStoreAndTouchingDiffs) { + row.left[1].set(this.computeState(row.left[0], row.rights), tx); + } + }); + } + + public mergeNonConflictingDiffs(): void { + transaction((tx) => { + for (const m of this.modifiedBaseRanges) { + if (m.isConflicting) { + continue; } + this.setState( + m, + m.input1Diffs.length > 0 + ? ModifiedBaseRangeState.default.withInput1(true) + : ModifiedBaseRangeState.default.withInput2(true), + tx + ); } - } - new LineEdits(lineEditsArr).apply(result); - */ + }); } - public get resultDiffs(): readonly LineDiff[] { + public get resultDiffs(): IObservable { return this.resultEdits.diffs; } public readonly modifiedBaseRanges = ModifiedBaseRange.fromDiffs( this.base, this.input1, - this.inputOneLinesDiffs, + this.input1LinesDiffs, this.input2, - this.inputTwoLinesDiffs + this.input2LinesDiffs ); - private readonly modifiedBaseRangeStateStores = new Map>( - this.modifiedBaseRanges.map(s => ([s, new ObservableValue(new ModifiedBaseRangeState(false, false, false), 'State')])) + private readonly modifiedBaseRangeStateStores = new Map>( + this.modifiedBaseRanges.map(s => ([s, new ObservableValue(ModifiedBaseRangeState.default, 'State')])) ); - private computeState(baseRange: ModifiedBaseRange): ModifiedBaseRangeState | undefined { - const existingDiff = this.resultEdits.findConflictingDiffs( - baseRange.baseRange - ); - if (!existingDiff) { - return new ModifiedBaseRangeState(false, false, false); + private computeState(baseRange: ModifiedBaseRange, conflictingDiffs?: LineDiff[]): ModifiedBaseRangeState { + if (!conflictingDiffs) { + conflictingDiffs = this.resultEdits.findTouchingDiffs( + baseRange.baseRange + ); } - const input1Edit = baseRange.getInput1LineEdit(); - if (input1Edit && existingDiff.getLineEdit().equals(input1Edit)) { - return new ModifiedBaseRangeState(true, false, false); + if (conflictingDiffs.length === 0) { + return ModifiedBaseRangeState.default; + } + const conflictingEdits = conflictingDiffs.map((d) => d.getLineEdit()); + + function editsAgreeWithDiffs(diffs: readonly LineDiff[]): boolean { + return equals( + conflictingEdits, + diffs.map((d) => d.getLineEdit()), + (a, b) => a.equals(b) + ); } - const input2Edit = baseRange.getInput2LineEdit(); - if (input2Edit && existingDiff.getLineEdit().equals(input2Edit)) { - return new ModifiedBaseRangeState(false, true, false); + if (editsAgreeWithDiffs(baseRange.input1Diffs)) { + return ModifiedBaseRangeState.default.withInput1(true); + } + if (editsAgreeWithDiffs(baseRange.input2Diffs)) { + return ModifiedBaseRangeState.default.withInput2(true); } - return undefined; + return ModifiedBaseRangeState.conflicting; } - public getState(baseRange: ModifiedBaseRange): IObservable { + public getState(baseRange: ModifiedBaseRange): IObservable { const existingState = this.modifiedBaseRangeStateStores.get(baseRange); if (!existingState) { throw new BugIndicatingError('object must be from this instance'); @@ -177,22 +217,26 @@ export class MergeEditorModel extends EditorModel { } existingState.set(state, transaction); - const existingDiff = this.resultEdits.findConflictingDiffs( + const conflictingDiffs = this.resultEdits.findTouchingDiffs( baseRange.baseRange ); - if (existingDiff) { - this.resultEdits.removeDiff(existingDiff); + if (conflictingDiffs) { + this.resultEdits.removeDiffs(conflictingDiffs, transaction); } - const edit = state.input1 - ? baseRange.getInput1LineEdit() + const diff = state.input1 + ? baseRange.input1CombinedDiff : state.input2 - ? baseRange.getInput2LineEdit() + ? baseRange.input2CombinedDiff : undefined; - if (edit) { - this.resultEdits.applyEditRelativeToOriginal(edit); + if (diff) { + this.resultEdits.applyEditRelativeToOriginal(diff.getLineEdit(), transaction); } } + + public getResultRange(baseRange: LineRange): LineRange { + return this.resultEdits.getResultRange(baseRange); + } } class ResultEdits { @@ -201,12 +245,13 @@ class ResultEdits { public readonly onDidChange = this.onDidChangeEmitter.event; constructor( - private _diffs: LineDiff[], + diffs: LineDiff[], private readonly baseTextModel: ITextModel, private readonly resultTextModel: ITextModel, private readonly _editorWorkerService: IEditorWorkerService ) { - this._diffs.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator)); + diffs.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator)); + this._diffs.set(diffs, undefined); resultTextModel.onDidChangeContent(e => { this.barrier.runExclusively(() => { @@ -220,7 +265,7 @@ class ResultEdits { e?.changes.map((c) => LineDiff.fromLineChange(c, baseTextModel, resultTextModel) ) || []; - this._diffs = diffs; + this._diffs.set(diffs, undefined); this.onDidChangeEmitter.fire(undefined); }); @@ -228,47 +273,55 @@ class ResultEdits { }); } - public get diffs(): readonly LineDiff[] { - return this._diffs; - } + private readonly _diffs = new ObservableValue([], 'diffs'); - public removeDiff(diffToRemove: LineDiff): void { - const len = this._diffs.length; - this._diffs = this._diffs.filter((d) => d !== diffToRemove); - if (len === this._diffs.length) { - throw new BugIndicatingError(); - } + public readonly diffs: IObservable = this._diffs; - this.barrier.runExclusivelyOrThrow(() => { - diffToRemove.getReverseLineEdit().apply(this.resultTextModel); - }); + public removeDiffs(diffToRemoves: LineDiff[], transaction: ITransaction | undefined): void { + diffToRemoves.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator)); + diffToRemoves.reverse(); + + let diffs = this._diffs.get(); - this._diffs = this._diffs.map((d) => - d.modifiedRange.isAfter(diffToRemove.modifiedRange) - ? new LineDiff( - d.originalTextModel, - d.originalRange, - d.modifiedTextModel, - d.modifiedRange.delta( - diffToRemove.originalRange.lineCount - diffToRemove.modifiedRange.lineCount + for (const diffToRemove of diffToRemoves) { + // TODO improve performance + const len = diffs.length; + diffs = diffs.filter((d) => d !== diffToRemove); + if (len === diffs.length) { + throw new BugIndicatingError(); + } + + this.barrier.runExclusivelyOrThrow(() => { + diffToRemove.getReverseLineEdit().apply(this.resultTextModel); + }); + + diffs = diffs.map((d) => + d.modifiedRange.isAfter(diffToRemove.modifiedRange) + ? new LineDiff( + d.originalTextModel, + d.originalRange, + d.modifiedTextModel, + d.modifiedRange.delta( + diffToRemove.originalRange.lineCount - diffToRemove.modifiedRange.lineCount + ) ) - ) - : d - ); + : d + ); + } + + this._diffs.set(diffs, transaction); } /** * Edit must be conflict free. */ - public applyEditRelativeToOriginal(edit: LineEdit): void { + public applyEditRelativeToOriginal(edit: LineEdit, transaction: ITransaction | undefined): void { let firstAfter = false; let delta = 0; const newDiffs = new Array(); - for (let i = 0; i < this._diffs.length; i++) { - const diff = this._diffs[i]; - + for (const diff of this._diffs.get()) { if (diff.originalRange.touches(edit.range)) { - throw new BugIndicatingError(); + throw new BugIndicatingError('Edit must be conflict free.'); } else if (diff.originalRange.isAfter(edit.range)) { if (!firstAfter) { firstAfter = true; @@ -306,16 +359,30 @@ class ResultEdits { new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) )); } - this._diffs = newDiffs; + this._diffs.set(newDiffs, transaction); this.barrier.runExclusivelyOrThrow(() => { new LineEdit(edit.range.delta(delta), edit.newLines).apply(this.resultTextModel); }); } - // TODO return many! - public findConflictingDiffs(rangeInOriginalTextModel: LineRange): LineDiff | undefined { - // TODO binary search - return this.diffs.find(d => d.originalRange.touches(rangeInOriginalTextModel)); + public findTouchingDiffs(baseRange: LineRange): LineDiff[] { + return this.diffs.get().filter(d => d.originalRange.touches(baseRange)); + } + + public getResultRange(baseRange: LineRange): LineRange { + let startOffset = 0; + let lengthOffset = 0; + for (const diff of this.diffs.get()) { + if (diff.originalRange.endLineNumberExclusive <= baseRange.startLineNumber) { + startOffset += diff.resultingDeltaFromOriginalToModified; + } else if (diff.originalRange.startLineNumber <= baseRange.endLineNumberExclusive) { + lengthOffset += diff.resultingDeltaFromOriginalToModified; + } else { + break; + } + } + + return new LineRange(baseRange.startLineNumber + startOffset, baseRange.lineCount + lengthOffset); } } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model.ts b/src/vs/workbench/contrib/mergeEditor/browser/model.ts index 5d21dc1718088..9a045854182c7 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ArrayQueue, compareBy, equals, numberComparator } from 'vs/base/common/arrays'; +import { Comparator, compareBy, equals, numberComparator } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; import { Range } from 'vs/editor/common/core/range'; import { ILineChange } from 'vs/editor/common/diff/diffComputer'; @@ -40,7 +40,9 @@ export class LineEdits { } export class LineRange { - public static hull(ranges: LineRange[]): LineRange | undefined { + public static readonly compareByStart: Comparator = compareBy(l => l.startLineNumber, numberComparator); + + public static join(ranges: LineRange[]): LineRange | undefined { if (ranges.length === 0) { return undefined; } @@ -63,6 +65,10 @@ export class LineRange { } } + public join(other: LineRange): LineRange { + return new LineRange(Math.min(this.startLineNumber, other.startLineNumber), Math.max(this.endLineNumberExclusive, other.endLineNumberExclusive) - this.startLineNumber); + } + public get endLineNumberExclusive(): number { return this.startLineNumber + this.lineCount; } @@ -131,9 +137,9 @@ export class LineDiff { return new LineDiff( lineDiffs[0].originalTextModel, - LineRange.hull(lineDiffs.map((d) => d.originalRange))!, + LineRange.join(lineDiffs.map((d) => d.originalRange))!, lineDiffs[0].modifiedTextModel, - LineRange.hull(lineDiffs.map((d) => d.modifiedRange))!, + LineRange.join(lineDiffs.map((d) => d.modifiedRange))!, ); } @@ -141,7 +147,7 @@ export class LineDiff { if (lineDiffs.length === 0) { return []; } - const originalRange = LineRange.hull(lineDiffs.map((d) => d.originalRange))!; + const originalRange = LineRange.join(lineDiffs.map((d) => d.originalRange))!; return lineDiffs.map(l => { const startDelta = originalRange.startLineNumber - l.originalRange.startLineNumber; const endDelta = originalRange.endLineNumberExclusive - l.originalRange.endLineNumberExclusive; @@ -231,7 +237,7 @@ export class ModifiedBaseRange { * This method computes strongly connected components of that graph while maintaining the side of each diff. */ public static fromDiffs( - originalTextModel: ITextModel, + baseTextModel: ITextModel, input1TextModel: ITextModel, diffs1: readonly LineDiff[], input2TextModel: ITextModel, @@ -242,90 +248,52 @@ export class ModifiedBaseRange { numberComparator ); - const queueDiffs1 = new ArrayQueue( - diffs1.slice().sort(compareByStartLineNumber) - ); - const queueDiffs2 = new ArrayQueue( - diffs2.slice().sort(compareByStartLineNumber) - ); + const diffs = diffs1 + .map((diff) => ({ source: 0 as 0 | 1, diff })) + .concat(diffs2.map((diff) => ({ source: 1 as const, diff }))); + + diffs.sort(compareBy(d => d.diff, compareByStartLineNumber)); + + const currentDiffs = [ + new Array(), + new Array(), + ]; + let deltaFromBaseToInput = [0, 0]; const result = new Array(); - while (true) { - const lastDiff1 = queueDiffs1.peekLast(); - const lastDiff2 = queueDiffs2.peekLast(); - - if ( - lastDiff1 && - (!lastDiff2 || - lastDiff1.originalRange.startLineNumber >= - lastDiff2.originalRange.startLineNumber) - ) { - queueDiffs1.removeLast(); - - const otherConflictingWith = - queueDiffs2.takeFromEndWhile((d) => d.conflicts(lastDiff1)) || []; - - const singleLinesDiff = LineDiff.hull(otherConflictingWith); - - const moreConflictingWith = - (singleLinesDiff && - queueDiffs1.takeFromEndWhile((d) => - d.conflicts(singleLinesDiff) - )) || - []; - moreConflictingWith.push(lastDiff1); - - result.push( - new ModifiedBaseRange( - originalTextModel, - input1TextModel, - moreConflictingWith, - queueDiffs1.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, - input2TextModel, - otherConflictingWith, - queueDiffs2.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, - ) - ); - } else if (lastDiff2) { - queueDiffs2.removeLast(); - - const otherConflictingWith = - queueDiffs1.takeFromEndWhile((d) => d.conflicts(lastDiff2)) || []; - - const singleLinesDiff = LineDiff.hull(otherConflictingWith); - - const moreConflictingWith = - (singleLinesDiff && - queueDiffs2.takeFromEndWhile((d) => - d.conflicts(singleLinesDiff) - )) || - []; - moreConflictingWith.push(lastDiff2); - - result.push( - new ModifiedBaseRange( - originalTextModel, - input1TextModel, - otherConflictingWith, - queueDiffs1.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, - input2TextModel, - moreConflictingWith, - queueDiffs2.peekLast()?.resultingDeltaFromOriginalToModified ?? 0, - ) - ); - } else { - break; - } + function pushAndReset() { + result.push(new ModifiedBaseRange( + baseTextModel, + input1TextModel, + currentDiffs[0], + deltaFromBaseToInput[0], + input2TextModel, + currentDiffs[1], + deltaFromBaseToInput[1], + )); + currentDiffs[0] = []; + currentDiffs[1] = []; } - result.reverse(); + let currentRange: LineRange | undefined; + + for (const diff of diffs) { + const range = diff.diff.originalRange; + if (currentRange && !currentRange.touches(range)) { + pushAndReset(); + } + deltaFromBaseToInput[diff.source] = diff.diff.resultingDeltaFromOriginalToModified; + currentRange = currentRange ? currentRange.join(range) : range; + currentDiffs[diff.source].push(diff.diff); + } + pushAndReset(); return result; } - private readonly input1FullDiff = LineDiff.hull(this.input1Diffs); - private readonly input2FullDiff = LineDiff.hull(this.input2Diffs); + public readonly input1CombinedDiff = LineDiff.hull(this.input1Diffs); + public readonly input2CombinedDiff = LineDiff.hull(this.input2Diffs); public readonly baseRange: LineRange; public readonly input1Range: LineRange; @@ -335,31 +303,31 @@ export class ModifiedBaseRange { public readonly baseTextModel: ITextModel, public readonly input1TextModel: ITextModel, public readonly input1Diffs: readonly LineDiff[], - public readonly input1DeltaLineCount: number, + input1DeltaLineCount: number, public readonly input2TextModel: ITextModel, public readonly input2Diffs: readonly LineDiff[], - public readonly input2DeltaLineCount: number, + input2DeltaLineCount: number, ) { if (this.input1Diffs.length === 0 && this.input2Diffs.length === 0) { throw new BugIndicatingError('must have at least one diff'); } const input1Diff = - this.input1FullDiff || + this.input1CombinedDiff || new LineDiff( baseTextModel, - this.input2FullDiff!.originalRange, + this.input2CombinedDiff!.originalRange, input1TextModel, - this.input2FullDiff!.originalRange.delta(input1DeltaLineCount) + this.input2CombinedDiff!.originalRange.delta(input1DeltaLineCount) ); const input2Diff = - this.input2FullDiff || + this.input2CombinedDiff || new LineDiff( baseTextModel, - this.input1FullDiff!.originalRange, + this.input1CombinedDiff!.originalRange, input1TextModel, - this.input1FullDiff!.originalRange.delta(input2DeltaLineCount) + this.input1CombinedDiff!.originalRange.delta(input2DeltaLineCount) ); const results = LineDiff.alignOriginalRange([input1Diff, input2Diff]); @@ -368,54 +336,57 @@ export class ModifiedBaseRange { this.input2Range = results[1].modifiedRange; } - public get isConflicting(): boolean { - return this.input1Diffs.length > 0 && this.input2Diffs.length > 0; + public getInputRange(inputNumber: 1 | 2): LineRange { + return inputNumber === 1 ? this.input1Range : this.input2Range; } - public getInput1LineEdit(): LineEdit | undefined { - if (this.input1Diffs.length === 0) { - return undefined; - } - //new LineDiff(this.baseTextModel, this.tota) - if (this.input1Diffs.length === 1) { - return this.input1Diffs[0].getLineEdit(); - } else { - throw new Error('Method not implemented.'); - } + public getInputDiffs(inputNumber: 1 | 2): readonly LineDiff[] { + return inputNumber === 1 ? this.input1Diffs : this.input2Diffs; } - public getInput2LineEdit(): LineEdit | undefined { - if (this.input2Diffs.length === 0) { - return undefined; - } - if (this.input2Diffs.length === 1) { - return this.input2Diffs[0].getLineEdit(); - } else { - throw new Error('Method not implemented.'); - } + public get isConflicting(): boolean { + return this.input1Diffs.length > 0 && this.input2Diffs.length > 0; } } export class ModifiedBaseRangeState { - constructor( + public static readonly default = new ModifiedBaseRangeState(false, false, false, false); + public static readonly conflicting = new ModifiedBaseRangeState(false, false, false, true); + + private constructor( public readonly input1: boolean, public readonly input2: boolean, - public readonly input2First: boolean + public readonly input2First: boolean, + public readonly conflicting: boolean, ) { } + public getInput(inputNumber: 1 | 2): boolean { + if (inputNumber === 1) { + return this.input1; + } else { + return this.input2; + } + } + + public withInputValue(inputNumber: 1 | 2, value: boolean): ModifiedBaseRangeState { + return inputNumber === 1 ? this.withInput1(value) : this.withInput2(value); + } + public withInput1(value: boolean): ModifiedBaseRangeState { return new ModifiedBaseRangeState( value, - this.input2, - value && this.isEmpty ? false : this.input2First + false, + value && this.isEmpty ? false : this.input2First, + false, ); } public withInput2(value: boolean): ModifiedBaseRangeState { return new ModifiedBaseRangeState( - this.input1, + false, value, - value && this.isEmpty ? true : this.input2First + value && this.isEmpty ? true : this.input2First, + false ); } @@ -445,31 +416,3 @@ export class ModifiedBaseRangeState { return arr.join(','); } } - -export class ReentrancyBarrier { - private isActive = false; - - public runExclusively(fn: () => void): void { - if (this.isActive) { - return; - } - this.isActive = true; - try { - fn(); - } finally { - this.isActive = false; - } - } - - public runExclusivelyOrThrow(fn: () => void): void { - if (this.isActive) { - throw new BugIndicatingError(); - } - this.isActive = true; - try { - fn(); - } finally { - this.isActive = false; - } - } -} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts new file mode 100644 index 0000000000000..cf5aaa569d7e3 --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CompareResult, ArrayQueue } from 'vs/base/common/arrays'; +import { BugIndicatingError } from 'vs/base/common/errors'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { IModelDeltaDecoration } from 'vs/editor/common/model'; +import { IObservable, autorun } from 'vs/workbench/contrib/audioCues/browser/observable'; +import { IDisposable } from 'xterm'; + +export class ReentrancyBarrier { + private isActive = false; + + public runExclusively(fn: () => void): void { + if (this.isActive) { + return; + } + this.isActive = true; + try { + fn(); + } finally { + this.isActive = false; + } + } + + public runExclusivelyOrThrow(fn: () => void): void { + if (this.isActive) { + throw new BugIndicatingError(); + } + this.isActive = true; + try { + fn(); + } finally { + this.isActive = false; + } + } +} + +export function n(tag: TTag): never; +export function n( + tag: TTag, + children: T +): (ArrayToObj & Record<'root', TagToElement>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; +export function n( + tag: TTag, + attributes: { $: TId } +): Record>; +export function n( + tag: TTag, + attributes: { $: TId }, + children: T +): (ArrayToObj & Record>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; +export function n(tag: string, ...args: [] | [attributes: { $: string } | Record, children?: any[]] | [children: any[]]): Record { + let attributes: Record; + let children: (Record | HTMLElement)[] | undefined; + + if (Array.isArray(args[0])) { + attributes = {}; + children = args[0]; + } else { + attributes = args[0] as any || {}; + children = args[1]; + } + + const [tagName, className] = tag.split('.'); + const el = document.createElement(tagName); + if (className) { + el.className = className; + } + + const result: Record = {}; + + if (children) { + for (const c of children) { + if (c instanceof HTMLElement) { + el.appendChild(c); + } else { + Object.assign(result, c); + el.appendChild(c.root); + } + } + } + + result['root'] = el; + + for (const [key, value] of Object.entries(attributes)) { + if (key === '$') { + result[value] = el; + continue; + } + el.setAttribute(key, value); + } + + return result; +} + +type RemoveHTMLElement = T extends HTMLElement ? never : T; + +type ArrayToObj = UnionToIntersection>; + + +type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; + +type HTMLElementsByTagName = { + div: HTMLDivElement; + span: HTMLSpanElement; + a: HTMLAnchorElement; +}; + +type TagToElement = T extends `${infer TStart}.${string}` + ? TStart extends keyof HTMLElementsByTagName + ? HTMLElementsByTagName[TStart] + : HTMLElement + : T extends keyof HTMLElementsByTagName + ? HTMLElementsByTagName[T] + : HTMLElement; + +export function setStyle( + element: HTMLElement, + style: { + width?: number | string; + height?: number | string; + left?: number | string; + top?: number | string; + } +): void { + Object.entries(style).forEach(([key, value]) => { + element.style.setProperty(key, toSize(value)); + }); +} + +function toSize(value: number | string): string { + return typeof value === 'number' ? `${value}px` : value; +} + +export function applyObservableDecorations(editor: CodeEditorWidget, decorations: IObservable): IDisposable { + const d = new DisposableStore(); + let decorationIds: string[] = []; + d.add(autorun(reader => { + const d = decorations.read(reader); + editor.changeDecorations(a => { + decorationIds = a.deltaDecorations(decorationIds, d); + }); + }, 'Update Decorations')); + d.add({ + dispose: () => { + editor.changeDecorations(a => { + decorationIds = a.deltaDecorations(decorationIds, []); + }); + } + }); + return d; +} + +export function* leftJoin( + left: Iterable, + right: readonly TRight[], + compare: (left: TLeft, right: TRight) => CompareResult, +): IterableIterator<{ left: TLeft; rights: TRight[] }> { + const rightQueue = new ArrayQueue(right); + for (const leftElement of left) { + rightQueue.takeWhile(rightElement => CompareResult.isGreaterThan(compare(leftElement, rightElement))); + const equals = rightQueue.takeWhile(rightElement => CompareResult.isNeitherLessOrGreaterThan(compare(leftElement, rightElement))); + yield { left: leftElement, rights: equals || [] }; + } +} From fbbcdc94114509f0268db654d85405a3a39fa972 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 18 May 2022 17:10:28 -0700 Subject: [PATCH 155/942] Add WebviewInitInfo type (#149877) * Add WebviewInitInfo type This cleanup PR adds a new `WebviewInitInfo` type that brings together the core information needed to create a webview I've then updated the various webview APIs to use this new type * Revert frameId -> origin change * frameId -> iframeId --- .../api/browser/mainThreadCodeInsets.ts | 11 ++++-- .../api/browser/mainThreadWebviewPanels.ts | 8 +++- .../customEditor/browser/customEditorInput.ts | 7 +++- .../browser/customEditorInputFactory.ts | 15 ++++--- .../extensions/browser/extensionEditor.ts | 13 +++++-- .../view/renderers/backLayerWebView.ts | 23 ++++++----- .../update/browser/releaseNotesEditor.ts | 22 ++++++----- .../contrib/webview/browser/overlayWebview.ts | 19 ++++----- .../contrib/webview/browser/webview.ts | 15 ++----- .../contrib/webview/browser/webviewElement.ts | 39 ++++++++++++++----- .../contrib/webview/browser/webviewService.ts | 22 +++-------- .../electron-sandbox/webviewElement.ts | 12 ++---- .../electron-sandbox/webviewService.ts | 12 ++---- .../browser/webviewEditorInputSerializer.ts | 10 +++-- .../browser/webviewWorkbenchService.ts | 31 +++++---------- .../webviewView/browser/webviewViewPane.ts | 12 +++--- .../browser/gettingStarted.ts | 4 +- 17 files changed, 147 insertions(+), 128 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index 9036d7a754a68..7bb7cee0895b0 100644 --- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -89,9 +89,14 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape { const disposables = new DisposableStore(); - const webview = this._webviewService.createWebviewElement('mainThreadCodeInsets_' + handle, { - enableFindWidget: false, - }, reviveWebviewContentOptions(options), { id: extensionId, location: URI.revive(extensionLocation) }); + const webview = this._webviewService.createWebviewElement({ + id: 'mainThreadCodeInsets_' + handle, + options: { + enableFindWidget: false, + }, + contentOptions: reviveWebviewContentOptions(options), + extension: { id: extensionId, location: URI.revive(extensionLocation) } + }); const webviewZone = new EditorWebviewZone(editor, line, height, webview); diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index 6659298ef1475..5737029a6a425 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -165,7 +165,13 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc const extension = reviveWebviewExtension(extensionData); - const webview = this._webviewWorkbenchService.createWebview(handle, this.webviewPanelViewType.fromExternal(viewType), initData.title, mainThreadShowOptions, reviveWebviewOptions(initData.panelOptions), reviveWebviewContentOptions(initData.webviewOptions), extension); + const webview = this._webviewWorkbenchService.createWebview({ + id: handle, + options: reviveWebviewOptions(initData.panelOptions), + contentOptions: reviveWebviewContentOptions(initData.webviewOptions), + extension + }, this.webviewPanelViewType.fromExternal(viewType), initData.title, mainThreadShowOptions); + this.addWebviewInput(handle, webview, { serializeBuffersForPostMessage: initData.serializeBuffersForPostMessage }); const payload = { diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 81dfae861af1f..e275174e00af5 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -38,7 +38,12 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput { const untitledString = accessor.get(IUntitledTextEditorService).getValue(resource); let untitledDocumentData = untitledString ? VSBuffer.fromString(untitledString) : undefined; const id = generateUuid(); - const webview = accessor.get(IWebviewService).createWebviewOverlay(id, { customClasses: options?.customClasses }, {}, undefined); + const webview = accessor.get(IWebviewService).createWebviewOverlay({ + id, + options: { customClasses: options?.customClasses }, + contentOptions: {}, + extension: undefined, + }); const input = instantiationService.createInstance(CustomEditorInput, resource, viewType, id, webview, { untitledDocumentData: untitledDocumentData, oldResource: options?.oldResource }); if (typeof group !== 'undefined') { input.updateGroup(group); diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts index 3304e1334a0d2..a4a7f7eea7387 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts @@ -108,11 +108,16 @@ export class CustomEditorInputSerializer extends WebviewEditorInputSerializer { } function reviveWebview(webviewService: IWebviewService, data: { id: string; state: any; webviewOptions: WebviewOptions; contentOptions: WebviewContentOptions; extension?: WebviewExtensionDescription }) { - const webview = webviewService.createWebviewOverlay(data.id, { - purpose: WebviewContentPurpose.CustomEditor, - enableFindWidget: data.webviewOptions.enableFindWidget, - retainContextWhenHidden: data.webviewOptions.retainContextWhenHidden - }, data.contentOptions, data.extension); + const webview = webviewService.createWebviewOverlay({ + id: data.id, + options: { + purpose: WebviewContentPurpose.CustomEditor, + enableFindWidget: data.webviewOptions.enableFindWidget, + retainContextWhenHidden: data.webviewOptions.retainContextWhenHidden + }, + contentOptions: data.contentOptions, + extension: data.extension, + }); webview.state = data.state; return webview; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 2f1244e6ebd0e..6ad78716620e4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -744,10 +744,15 @@ export class ExtensionEditor extends EditorPane { return Promise.resolve(null); } - const webview = this.contentDisposables.add(this.webviewService.createWebviewOverlay(generateUuid(), { - enableFindWidget: true, - tryRestoreScrollPosition: true, - }, {}, undefined)); + const webview = this.contentDisposables.add(this.webviewService.createWebviewOverlay({ + id: generateUuid(), + options: { + enableFindWidget: true, + tryRestoreScrollPosition: true, + }, + contentOptions: {}, + extension: undefined, + })); webview.initialScrollProgress = this.initialScrollProgress.get(webviewIndex) || 0; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index cc2eb8bf93a2f..af4606ffee6c9 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -884,15 +884,20 @@ var requirejs = (function() { ...workspaceFolders, ...this.getBuiltinLocalResourceRoots(), ]; - const webview = webviewService.createWebviewElement(this.id, { - purpose: WebviewContentPurpose.NotebookRenderer, - enableFindWidget: false, - transformCssVariables: transformWebviewThemeVars, - }, { - allowMultipleAPIAcquire: true, - allowScripts: true, - localResourceRoots: this.localResourceRootsCache, - }, undefined); + const webview = webviewService.createWebviewElement({ + id: this.id, + options: { + purpose: WebviewContentPurpose.NotebookRenderer, + enableFindWidget: false, + transformCssVariables: transformWebviewThemeVars, + }, + contentOptions: { + allowMultipleAPIAcquire: true, + allowScripts: true, + localResourceRoots: this.localResourceRootsCache, + }, + extension: undefined + }); webview.html = content; return webview; diff --git a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts index df20df258da4f..9517be6e2e0d1 100644 --- a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts @@ -79,18 +79,20 @@ export class ReleaseNotesManager { this._webviewWorkbenchService.revealWebview(this._currentReleaseNotes, activeEditorPane ? activeEditorPane.group : this._editorGroupService.activeGroup, false); } else { this._currentReleaseNotes = this._webviewWorkbenchService.createWebview( - generateUuid(), - 'releaseNotes', - title, - { group: ACTIVE_GROUP, preserveFocus: false }, - { - tryRestoreScrollPosition: true, - enableFindWidget: true, - }, { - localResourceRoots: [] + id: generateUuid(), + options: { + tryRestoreScrollPosition: true, + enableFindWidget: true, + }, + contentOptions: { + localResourceRoots: [] + }, + extension: undefined }, - undefined); + 'releaseNotes', + title, + { group: ACTIVE_GROUP, preserveFocus: false }); this._currentReleaseNotes.webview.onDidClickLink(uri => this.onDidClickLink(URI.parse(uri))); this._currentReleaseNotes.onWillDispose(() => { this._currentReleaseNotes = undefined; }); diff --git a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts index 057e5e10d4d5e..b1a228fc65c33 100644 --- a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts +++ b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts @@ -12,6 +12,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IWebviewService, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_ENABLED, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, IWebview, WebviewContentOptions, IWebviewElement, WebviewExtensionDescription, WebviewMessageReceivedEvent, WebviewOptions, IOverlayWebview } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewInitInfo } from 'vs/workbench/contrib/webview/browser/webviewElement'; /** * Webview that is absolutely positioned over another element and that can creates and destroys an underlying webview as needed. @@ -41,20 +42,20 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { private _findWidgetVisible: IContextKey | undefined; private _findWidgetEnabled: IContextKey | undefined; + public readonly id: string; + public constructor( - public readonly id: string, - initialOptions: WebviewOptions, - initialContentOptions: WebviewContentOptions, - extension: WebviewExtensionDescription | undefined, + initInfo: WebviewInitInfo, @ILayoutService private readonly _layoutService: ILayoutService, @IWebviewService private readonly _webviewService: IWebviewService, @IContextKeyService private readonly _baseContextKeyService: IContextKeyService ) { super(); - this._extension = extension; - this._options = initialOptions; - this._contentOptions = initialContentOptions; + this.id = initInfo.id; + this._extension = initInfo.extension; + this._options = initInfo.options; + this._contentOptions = initInfo.contentOptions; } public get isFocused() { @@ -99,8 +100,8 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { // Webviews cannot be reparented in the dom as it will destroy their contents. // Mount them to a high level node to avoid this. this._layoutService.container.appendChild(this._container); - } + return this._container; } @@ -185,7 +186,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { } if (!this._webview.value) { - const webview = this._webviewService.createWebviewElement(this.id, this._options, this._contentOptions, this.extension); + const webview = this._webviewService.createWebviewElement({ id: this.id, options: this._options, contentOptions: this._contentOptions, extension: this.extension }); this._webview.value = webview; webview.state = this._state; diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 3054fdda9fbd9..3d50fee82a08e 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -14,6 +14,7 @@ import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IWebviewPortMapping } from 'vs/platform/webview/common/webviewPortMapping'; +import { WebviewInitInfo } from 'vs/workbench/contrib/webview/browser/webviewElement'; /** * Set when the find widget in a webview in a webview is visible. @@ -53,12 +54,7 @@ export interface IWebviewService { /** * Create a basic webview dom element. */ - createWebviewElement( - id: string, - options: WebviewOptions, - contentOptions: WebviewContentOptions, - extension: WebviewExtensionDescription | undefined, - ): IWebviewElement; + createWebviewElement(initInfo: WebviewInitInfo): IWebviewElement; /** * Create a lazily created webview element that is overlaid on top of another element. @@ -66,12 +62,7 @@ export interface IWebviewService { * Allows us to avoid re-parenting the webview (which destroys its contents) when * moving webview around the workbench. */ - createWebviewOverlay( - id: string, - options: WebviewOptions, - contentOptions: WebviewContentOptions, - extension: WebviewExtensionDescription | undefined, - ): IOverlayWebview; + createWebviewOverlay(initInfo: WebviewInitInfo): IOverlayWebview; } export const enum WebviewContentPurpose { diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 6cd87f63f2812..fe286d9a83200 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -100,11 +100,28 @@ namespace WebviewState { export type State = typeof Ready | Initializing; } +export interface WebviewInitInfo { + readonly id: string; + + readonly options: WebviewOptions; + readonly contentOptions: WebviewContentOptions; + + readonly extension: WebviewExtensionDescription | undefined; +} + + export class WebviewElement extends Disposable implements IWebview, WebviewFindDelegate { + /** + * External identifier of this webview. + */ public readonly id: string; + /** + * Unique identifier of this webview iframe element. + */ private readonly iframeId: string; + private readonly encodedWebviewOriginPromise: Promise; private encodedWebviewOrigin: string | undefined; @@ -153,11 +170,12 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD private _disposed = false; + + public extension: WebviewExtensionDescription | undefined; + private readonly options: WebviewOptions; + constructor( - id: string, - private readonly options: WebviewOptions, - contentOptions: WebviewContentOptions, - public extension: WebviewExtensionDescription | undefined, + initInfo: WebviewInitInfo, protected readonly webviewThemeDataProvider: WebviewThemeDataProvider, @IConfigurationService configurationService: IConfigurationService, @IContextMenuService contextMenuService: IContextMenuService, @@ -174,13 +192,16 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD ) { super(); - this.id = id; + this.id = initInfo.id; this.iframeId = generateUuid(); this.encodedWebviewOriginPromise = parentOriginHash(window.origin, this.iframeId).then(id => this.encodedWebviewOrigin = id); + this.options = initInfo.options; + this.extension = initInfo.extension; + this.content = { html: '', - options: contentOptions, + options: initInfo.contentOptions, state: undefined }; @@ -190,7 +211,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD this._tunnelService )); - this._element = this.createElement(options, contentOptions); + this._element = this.createElement(initInfo.options, initInfo.contentOptions); const subscription = this._register(addDisposableListener(window, 'message', (e: MessageEvent) => { @@ -355,14 +376,14 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD this.startBlockingIframeDragEvents(); })); - if (options.enableFindWidget) { + if (initInfo.options.enableFindWidget) { this._webviewFindWidget = this._register(instantiationService.createInstance(WebviewFindWidget, this)); this.styledFindWidget(); } this.encodedWebviewOriginPromise.then(encodedWebviewOrigin => { if (!this._disposed) { - this.initElement(encodedWebviewOrigin, extension, options); + this.initElement(encodedWebviewOrigin, this.extension, this.options); } }); } diff --git a/src/vs/workbench/contrib/webview/browser/webviewService.ts b/src/vs/workbench/contrib/webview/browser/webviewService.ts index ef902f5d5e8da..1ca0b71be5295 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewService.ts @@ -7,8 +7,8 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; -import { IWebviewService, IWebview, WebviewContentOptions, IWebviewElement, WebviewExtensionDescription, WebviewOptions, IOverlayWebview } from 'vs/workbench/contrib/webview/browser/webview'; -import { WebviewElement } from 'vs/workbench/contrib/webview/browser/webviewElement'; +import { IOverlayWebview, IWebview, IWebviewElement, IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewElement, WebviewInitInfo } from 'vs/workbench/contrib/webview/browser/webviewElement'; import { OverlayWebview } from './overlayWebview'; export class WebviewService extends Disposable implements IWebviewService { @@ -43,24 +43,14 @@ export class WebviewService extends Disposable implements IWebviewService { private readonly _onDidChangeActiveWebview = this._register(new Emitter()); public readonly onDidChangeActiveWebview = this._onDidChangeActiveWebview.event; - createWebviewElement( - id: string, - options: WebviewOptions, - contentOptions: WebviewContentOptions, - extension: WebviewExtensionDescription | undefined, - ): IWebviewElement { - const webview = this._instantiationService.createInstance(WebviewElement, id, options, contentOptions, extension, this._webviewThemeDataProvider); + createWebviewElement(initInfo: WebviewInitInfo): IWebviewElement { + const webview = this._instantiationService.createInstance(WebviewElement, initInfo, this._webviewThemeDataProvider); this.registerNewWebview(webview); return webview; } - createWebviewOverlay( - id: string, - options: WebviewOptions, - contentOptions: WebviewContentOptions, - extension: WebviewExtensionDescription | undefined, - ): IOverlayWebview { - const webview = this._instantiationService.createInstance(OverlayWebview, id, options, contentOptions, extension); + createWebviewOverlay(initInfo: WebviewInitInfo): IOverlayWebview { + const webview = this._instantiationService.createInstance(OverlayWebview, initInfo); this.registerNewWebview(webview); return webview; } diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-sandbox/webviewElement.ts index 5d77ddbc1d92d..692eeb591baf3 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/webviewElement.ts @@ -23,8 +23,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; import { FindInFrameOptions, IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing'; -import { WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; -import { WebviewElement, WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/webviewElement'; +import { WebviewElement, WebviewInitInfo, WebviewMessageChannels } from 'vs/workbench/contrib/webview/browser/webviewElement'; import { WindowIgnoreMenuShortcutsManager } from 'vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -44,10 +43,7 @@ export class ElectronWebviewElement extends WebviewElement { protected override get platform() { return 'electron'; } constructor( - id: string, - options: WebviewOptions, - contentOptions: WebviewContentOptions, - extension: WebviewExtensionDescription | undefined, + initInfo: WebviewInitInfo, webviewThemeDataProvider: WebviewThemeDataProvider, @IContextMenuService contextMenuService: IContextMenuService, @ITunnelService tunnelService: ITunnelService, @@ -64,7 +60,7 @@ export class ElectronWebviewElement extends WebviewElement { @IInstantiationService instantiationService: IInstantiationService, @IAccessibilityService accessibilityService: IAccessibilityService, ) { - super(id, options, contentOptions, extension, webviewThemeDataProvider, + super(initInfo, webviewThemeDataProvider, configurationService, contextMenuService, menuService, notificationService, environmentService, fileService, logService, remoteAuthorityResolverService, telemetryService, tunnelService, instantiationService, accessibilityService); @@ -80,7 +76,7 @@ export class ElectronWebviewElement extends WebviewElement { this._webviewKeyboardHandler.didBlur(); })); - if (options.enableFindWidget) { + if (initInfo.options.enableFindWidget) { this._register(this.onDidHtmlChange((newContent) => { if (this._findStarted && this._cachedHtmlContent !== newContent) { this.stopFind(false); diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/webviewService.ts b/src/vs/workbench/contrib/webview/electron-sandbox/webviewService.ts index c63c3a656753d..20ec351f82761 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/webviewService.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/webviewService.ts @@ -3,19 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWebviewElement, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; +import { IWebviewElement } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewInitInfo } from 'vs/workbench/contrib/webview/browser/webviewElement'; import { WebviewService } from 'vs/workbench/contrib/webview/browser/webviewService'; import { ElectronWebviewElement } from 'vs/workbench/contrib/webview/electron-sandbox/webviewElement'; export class ElectronWebviewService extends WebviewService { - override createWebviewElement( - id: string, - options: WebviewOptions, - contentOptions: WebviewContentOptions, - extension: WebviewExtensionDescription | undefined, - ): IWebviewElement { - const webview = this._instantiationService.createInstance(ElectronWebviewElement, id, options, contentOptions, extension, this._webviewThemeDataProvider); + override createWebviewElement(initInfo: WebviewInitInfo): IWebviewElement { + const webview = this._instantiationService.createInstance(ElectronWebviewElement, initInfo, this._webviewThemeDataProvider); this.registerNewWebview(webview); return webview; } diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.ts index 2e49d30d1f417..abed8dcdb1642 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.ts @@ -74,14 +74,16 @@ export class WebviewEditorInputSerializer implements IEditorSerializer { ): WebviewInput { const data = this.fromJson(JSON.parse(serializedEditorInput)); return this._webviewWorkbenchService.reviveWebview({ - id: data.id, + webviewInitInfo: { + id: data.id, + options: data.webviewOptions, + contentOptions: data.contentOptions, + extension: data.extension, + }, viewType: data.viewType, title: data.title, iconPath: data.iconPath, state: data.state, - webviewOptions: data.webviewOptions, - contentOptions: data.contentOptions, - extension: data.extension, group: data.group }); } diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts index 45d5a74cdf65b..94ede834b02b3 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts @@ -15,7 +15,8 @@ import { createDecorator, IInstantiationService } from 'vs/platform/instantiatio import { GroupIdentifier } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { IOverlayWebview, IWebviewService, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; +import { IOverlayWebview, IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewInitInfo } from 'vs/workbench/contrib/webview/browser/webviewElement'; import { WebviewIconManager, WebviewIcons } from 'vs/workbench/contrib/webviewPanel/browser/webviewIconManager'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; @@ -34,24 +35,18 @@ export interface IWebviewWorkbenchService { readonly iconManager: WebviewIconManager; createWebview( - id: string, + webviewInitInfo: WebviewInitInfo, viewType: string, title: string, showOptions: ICreateWebViewShowOptions, - webviewOptions: WebviewOptions, - contentOptions: WebviewContentOptions, - extension: WebviewExtensionDescription | undefined, ): WebviewInput; reviveWebview(options: { - id: string; + webviewInitInfo: WebviewInitInfo; viewType: string; title: string; iconPath: WebviewIcons | undefined; state: any; - webviewOptions: WebviewOptions; - contentOptions: WebviewContentOptions; - extension: WebviewExtensionDescription | undefined; group: number | undefined; }): WebviewInput; @@ -218,16 +213,13 @@ export class WebviewEditorService extends Disposable implements IWebviewWorkbenc } public createWebview( - id: string, + webviewInitInfo: WebviewInitInfo, viewType: string, title: string, showOptions: ICreateWebViewShowOptions, - webviewOptions: WebviewOptions, - contentOptions: WebviewContentOptions, - extension: WebviewExtensionDescription | undefined, ): WebviewInput { - const webview = this._webviewService.createWebviewOverlay(id, webviewOptions, contentOptions, extension); - const webviewInput = this._instantiationService.createInstance(WebviewInput, id, viewType, title, webview, this.iconManager); + const webview = this._webviewService.createWebviewOverlay(webviewInitInfo); + const webviewInput = this._instantiationService.createInstance(WebviewInput, webviewInitInfo.id, viewType, title, webview, this.iconManager); this._editorService.openEditor(webviewInput, { pinned: true, preserveFocus: showOptions.preserveFocus, @@ -268,20 +260,17 @@ export class WebviewEditorService extends Disposable implements IWebviewWorkbenc } public reviveWebview(options: { - id: string; + webviewInitInfo: WebviewInitInfo; viewType: string; title: string; iconPath: WebviewIcons | undefined; state: any; - webviewOptions: WebviewOptions; - contentOptions: WebviewContentOptions; - extension: WebviewExtensionDescription | undefined; group: number | undefined; }): WebviewInput { - const webview = this._webviewService.createWebviewOverlay(options.id, options.webviewOptions, options.contentOptions, options.extension); + const webview = this._webviewService.createWebviewOverlay(options.webviewInitInfo); webview.state = options.state; - const webviewInput = this._instantiationService.createInstance(LazilyResolvedWebviewEditorInput, options.id, options.viewType, options.title, webview); + const webviewInput = this._instantiationService.createInstance(LazilyResolvedWebviewEditorInput, options.webviewInitInfo.id, options.viewType, options.title, webview); webviewInput.iconPath = options.iconPath; if (typeof options.group === 'number') { diff --git a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts index b09e7f519cfe1..124df428f3f7b 100644 --- a/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts +++ b/src/vs/workbench/contrib/webviewView/browser/webviewViewPane.ts @@ -167,12 +167,12 @@ export class WebviewViewPane extends ViewPane { this._activated = true; const webviewId = generateUuid(); - const webview = this.webviewService.createWebviewOverlay( - webviewId, - { purpose: WebviewContentPurpose.WebviewView }, - {}, - this.extensionId ? { id: this.extensionId } : undefined - ); + const webview = this.webviewService.createWebviewOverlay({ + id: webviewId, + options: { purpose: WebviewContentPurpose.WebviewView }, + contentOptions: {}, + extension: this.extensionId ? { id: this.extensionId } : undefined + }); webview.state = this.viewState[storageKeys.webviewState]; this._webview.value = webview; diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index 977b500b88dba..5fac78668bfe1 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -529,7 +529,7 @@ export class GettingStartedPage extends EditorPane { this.stepsContent.classList.remove('markdown'); const media = stepToExpand.media; - const webview = this.stepDisposables.add(this.webviewService.createWebviewElement(this.webviewID, {}, {}, undefined)); + const webview = this.stepDisposables.add(this.webviewService.createWebviewElement({ id: this.webviewID, options: {}, contentOptions: {}, extension: undefined })); webview.mountTo(this.stepMediaComponent); webview.html = await this.detailsRenderer.renderSVG(media.path); @@ -570,7 +570,7 @@ export class GettingStartedPage extends EditorPane { const media = stepToExpand.media; - const webview = this.stepDisposables.add(this.webviewService.createWebviewElement(this.webviewID, {}, { localResourceRoots: [media.root], allowScripts: true }, undefined)); + const webview = this.stepDisposables.add(this.webviewService.createWebviewElement({ id: this.webviewID, options: {}, contentOptions: { localResourceRoots: [media.root], allowScripts: true }, extension: undefined })); webview.mountTo(this.stepMediaComponent); const rawHTML = await this.detailsRenderer.renderMarkdown(media.path, media.base); From ba2dd825803b5f95d5dcce927afc3a4882ed0f84 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 18 May 2022 17:14:29 -0700 Subject: [PATCH 156/942] update loader (#149878) --- src/vs/css.js | 4 ---- src/vs/loader.js | 12 +++++++++--- src/vs/nls.js | 9 ++++++++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/vs/css.js b/src/vs/css.js index a719c310ec3d3..8a0f9912902a8 100644 --- a/src/vs/css.js +++ b/src/vs/css.js @@ -16,10 +16,6 @@ 'use strict'; var CSSLoaderPlugin; (function (CSSLoaderPlugin) { - /** - * Known issue: - * - In IE there is no way to know if the CSS file loaded successfully or not. - */ var BrowserCSSLoader = /** @class */ (function () { function BrowserCSSLoader() { this._pendingLoads = 0; diff --git a/src/vs/loader.js b/src/vs/loader.js index d66392e23a052..69554c5fac491 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -277,8 +277,10 @@ var AMDLoader; return; } if (err.phase === 'factory') { - console.error('The factory method of "' + err.moduleId + '" has thrown an exception'); + console.error('The factory function of "' + err.moduleId + '" has thrown an exception'); console.error(err); + console.error('Here are the modules that depend on it:'); + console.error(err.neededBy); return; } } @@ -1183,7 +1185,7 @@ var AMDLoader; producedError: null }; }; - Module.prototype.complete = function (recorder, config, dependenciesValues) { + Module.prototype.complete = function (recorder, config, dependenciesValues, inversedependenciesProvider) { this._isComplete = true; var producedError = null; if (this._callback) { @@ -1204,6 +1206,7 @@ var AMDLoader; var err = AMDLoader.ensureError(producedError); err.phase = 'factory'; err.moduleId = this.strId; + err.neededBy = inversedependenciesProvider(this.id); this.error = err; config.onError(err); } @@ -1815,7 +1818,10 @@ var AMDLoader; dependenciesValues[i] = null; } } - module.complete(recorder, this._config, dependenciesValues); + var inversedependenciesProvider = function (moduleId) { + return (_this._inverseDependencies2[moduleId] || []).map(function (intModuleId) { return _this._moduleIdProvider.getStrModuleId(intModuleId); }); + }; + module.complete(recorder, this._config, dependenciesValues, inversedependenciesProvider); // Fetch and clear inverse dependencies var inverseDeps = this._inverseDependencies2[module.id]; this._inverseDependencies2[module.id] = null; diff --git a/src/vs/nls.js b/src/vs/nls.js index f969d159f4afc..793f0d6eecb6f 100644 --- a/src/vs/nls.js +++ b/src/vs/nls.js @@ -114,6 +114,7 @@ var NLSLoaderPlugin; }; NLSPlugin.prototype.load = function (name, req, load, config) { var _this = this; + var _a; config = config || {}; if (!name || name.length === 0) { load({ @@ -148,7 +149,13 @@ var NLSLoaderPlugin; }); } else { - req([name + suffix], messagesLoaded_1); + var base = (_a = pluginConfig.baseUrl) !== null && _a !== void 0 ? _a : ''; + req([base + name + suffix], messagesLoaded_1, function (err) { + var _a; + // We have an error. Load the English default strings instead. + console.warn("Falling back to default strings. Unable to load translations because of: " + ((_a = err.message) !== null && _a !== void 0 ? _a : err)); + req([name + '.nls'], messagesLoaded_1); + }); } } }; From 60e199cda4988e09fd78b1eeb9dce5e9e87e8a1d Mon Sep 17 00:00:00 2001 From: Robo Date: Thu, 19 May 2022 14:34:26 +0900 Subject: [PATCH 157/942] chore: bump electron@17.4.4 (#149896) --- .yarnrc | 2 +- .../linux/product-build-linux-client.yml | 10 ---------- build/azure-pipelines/win32/product-build-win32.yml | 8 -------- cgmanifest.json | 4 ++-- package.json | 2 +- yarn.lock | 8 ++++---- 6 files changed, 8 insertions(+), 26 deletions(-) diff --git a/.yarnrc b/.yarnrc index 95494af520111..f2811eb170a75 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,4 +1,4 @@ disturl "https://electronjs.org/headers" -target "17.4.3" +target "17.4.4" runtime "electron" build_from_source "true" diff --git a/build/azure-pipelines/linux/product-build-linux-client.yml b/build/azure-pipelines/linux/product-build-linux-client.yml index 4e6f8f13d0ec0..7dba0eef378b2 100644 --- a/build/azure-pipelines/linux/product-build-linux-client.yml +++ b/build/azure-pipelines/linux/product-build-linux-client.yml @@ -122,16 +122,6 @@ steps: - script: | set -e export npm_config_arch=$(NPM_ARCH) - # node-gyp@9.0.0 shipped with node@16.15.0 starts using config.gypi - # from the custom headers path if dist-url option was set instead of - # using the config value from the process. Electron builds with pointer compression - # enabled for x64 and arm64, but incorrectly ships a single copy of config.gypi - # with v8_enable_pointer_compression option always set for all target architectures. - # We use the force_process_config option to use the config.gypi from the - # nodejs process executing npm for 32-bit architectures. - if [ "$NPM_ARCH" = "armv7l" ]; then - export npm_config_force_process_config="true" - fi if [ -z "$CC" ] || [ -z "$CXX" ]; then # Download clang based on chromium revision used by vscode diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 286283a31dc39..4b746afc2a108 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -103,14 +103,6 @@ steps: . build/azure-pipelines/win32/retry.ps1 $ErrorActionPreference = "Stop" $env:npm_config_arch="$(VSCODE_ARCH)" - # node-gyp@9.0.0 shipped with node@16.15.0 starts using config.gypi - # from the custom headers path if dist-url option was set instead of - # using the config value from the process. Electron builds with pointer compression - # enabled for x64 and arm64, but incorrectly ships a single copy of config.gypi - # with v8_enable_pointer_compression option always set for all target architectures. - # We use the force_process_config option to use the config.gypi from the - # nodejs process executing npm for 32-bit architectures. - if ('$(VSCODE_ARCH)' -eq 'ia32') { $env:npm_config_force_process_config="true" } $env:CHILD_CONCURRENCY="1" retry { exec { yarn --frozen-lockfile --check-files } } env: diff --git a/cgmanifest.json b/cgmanifest.json index a4a8dc1be7309..d153e97eb0feb 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "322f1c3f8907f2592eef5b5e03a97045e30df9e3" + "commitHash": "085a15fd95969f3c61a52b39d64a7048d306dabe" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "17.4.3" + "version": "17.4.4" }, { "component": { diff --git a/package.json b/package.json index 035efff2f3332..c9215abb13af9 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "cssnano": "^4.1.11", "debounce": "^1.0.0", "deemon": "^1.4.0", - "electron": "17.4.3", + "electron": "17.4.4", "eslint": "8.7.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^19.1.0", diff --git a/yarn.lock b/yarn.lock index cee415886c141..21813ce973708 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4296,10 +4296,10 @@ electron-to-chromium@^1.4.17: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.45.tgz#cf1144091d6683cbd45a231954a745f02fb24598" integrity sha512-czF9eYVuOmlY/vxyMQz2rGlNSjZpxNQYBe1gmQv7al171qOIhgyO9k7D5AKlgeTCSPKk+LHhj5ZyIdmEub9oNg== -electron@17.4.3: - version "17.4.3" - resolved "https://registry.yarnpkg.com/electron/-/electron-17.4.3.tgz#5f3c26cb211f9267d2becee717f34e3ce564a6bf" - integrity sha512-WQggyCgNUOzoOn+wJKe+xFhYy56gyrn/jIa/l7dyD3TxPb8lddSc86OAqPnP5EugcNXQ0yIu8b+SIE8duKozSw== +electron@17.4.4: + version "17.4.4" + resolved "https://registry.yarnpkg.com/electron/-/electron-17.4.4.tgz#a289fa5cff6a59ef83647517a295eca780d64a86" + integrity sha512-/CqXJwm1VLfhF7+QhCrPEoePcpGMdRh09A+sVHX+kgT1twrmNH8S+ZeMPYxX8EU0O0Eki3UfA5zA2ADWaCDq2Q== dependencies: "@electron/get" "^1.13.0" "@types/node" "^14.6.2" From 14925e336de15a41da04deacea2150dcff77ae16 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 19 May 2022 11:18:22 +0200 Subject: [PATCH 158/942] Add a basic check (#149832) * Add a basic check * Fix yaml error * Another attempt to fix the yaml * update actions versions * let -> const * Add hygiene and layering check * update name --- .github/workflows/basic.yml | 142 ++++++++++++++++++ .../test/browser/outlineModel.test.ts | 4 +- 2 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/basic.yml diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml new file mode 100644 index 0000000000000..270e952393882 --- /dev/null +++ b/.github/workflows/basic.yml @@ -0,0 +1,142 @@ +name: Basic checks + +on: + # push: + # branches: + # - main + # - release/* + pull_request: + branches: + - main + - release/* + +jobs: + main: + name: Compilation, Unit and Integration Tests + runs-on: ubuntu-latest + timeout-minutes: 40 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v3 + + # TODO: rename azure-pipelines/linux/xvfb.init to github-actions + - name: Setup Build Environment + run: | + sudo apt-get update + sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 + sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb + sudo chmod +x /etc/init.d/xvfb + sudo update-rc.d xvfb defaults + sudo service xvfb start + + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Compute node modules cache key + id: nodeModulesCacheKey + run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" + - name: Cache node modules + id: cacheNodeModules + uses: actions/cache@v3 + with: + path: "**/node_modules" + key: ${{ runner.os }}-cacheNodeModules21-${{ steps.nodeModulesCacheKey.outputs.value }} + restore-keys: ${{ runner.os }}-cacheNodeModules21- + - name: Get yarn cache directory path + id: yarnCacheDirPath + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: Cache yarn directory + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + uses: actions/cache@v3 + with: + path: ${{ steps.yarnCacheDirPath.outputs.dir }} + key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} + restore-keys: ${{ runner.os }}-yarnCacheDir- + - name: Execute yarn + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + run: yarn --frozen-lockfile --network-timeout 180000 + + - name: Compile and Download + run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions + + - name: Compile Integration Tests + run: yarn --cwd test/integration/browser compile + + - name: Run Unit Tests + id: electron-unit-tests + run: DISPLAY=:10 ./scripts/test.sh + + - name: Run Integration Tests (Electron) + id: electron-integration-tests + run: DISPLAY=:10 ./scripts/test-integration.sh + + hygiene: + name: Hygiene and Layering + runs-on: ubuntu-latest + timeout-minutes: 40 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Compute node modules cache key + id: nodeModulesCacheKey + run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" + - name: Cache node modules + id: cacheNodeModules + uses: actions/cache@v3 + with: + path: "**/node_modules" + key: ${{ runner.os }}-cacheNodeModules21-${{ steps.nodeModulesCacheKey.outputs.value }} + restore-keys: ${{ runner.os }}-cacheNodeModules21- + - name: Get yarn cache directory path + id: yarnCacheDirPath + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: Cache yarn directory + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + uses: actions/cache@v3 + with: + path: ${{ steps.yarnCacheDirPath.outputs.dir }} + key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} + restore-keys: ${{ runner.os }}-yarnCacheDir- + - name: Execute yarn + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + run: yarn --frozen-lockfile --network-timeout 180000 + + - name: Download Playwright + run: yarn playwright-install + + - name: Run Hygiene Checks + run: yarn gulp hygiene + + - name: Run Valid Layers Checks + run: yarn valid-layers-check + + - name: Compile /build/ + run: yarn --cwd build compile + + - name: Check clean git state + run: ./.github/workflows/check-clean-git-state.sh + + - name: Run eslint + run: yarn eslint + + - name: Run vscode-dts Compile Checks + run: yarn vscode-dts-compile-check + + - name: Run Trusted Types Checks + run: yarn tsec-compile-check diff --git a/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts b/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts index d801c2c772241..096713ed3b0ed 100644 --- a/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts +++ b/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts @@ -77,9 +77,9 @@ suite('OutlineModel', function () { }); assert.strictEqual(isCancelled, false); - let s1 = new CancellationTokenSource(); + const s1 = new CancellationTokenSource(); service.getOrCreate(model, s1.token); - let s2 = new CancellationTokenSource(); + const s2 = new CancellationTokenSource(); service.getOrCreate(model, s2.token); s1.cancel(); From 6ccc8509acb20e6c7b630acf86d9bb153564a9c3 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 19 May 2022 11:28:57 +0200 Subject: [PATCH 159/942] reduce roundtrps - send policies data to workbench and shared process --- .../sharedProcess/sharedProcessMain.ts | 4 +- .../policy/common/filePolicyService.ts | 35 ++-------- src/vs/platform/policy/common/policy.ts | 41 +++++++++++- src/vs/platform/policy/common/policyIpc.ts | 35 +++++----- .../policy/node/nativePolicyService.ts | 66 +++++++------------ .../electron-main/sharedProcess.ts | 5 +- .../sharedProcess/node/sharedProcess.ts | 4 ++ src/vs/platform/window/common/window.ts | 3 + .../platform/windows/electron-main/window.ts | 3 + .../electron-main/windowsMainService.ts | 5 +- .../electron-sandbox/desktop.main.ts | 4 +- 11 files changed, 104 insertions(+), 101 deletions(-) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 8a2cfbc36dcd1..243729c4e3723 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -101,7 +101,7 @@ import { IV8InspectProfilingService } from 'vs/platform/profiling/common/profili import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService'; import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; -import { IPolicyService } from 'vs/platform/policy/common/policy'; +import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; class SharedProcessMain extends Disposable { @@ -183,7 +183,7 @@ class SharedProcessMain extends Disposable { services.set(IMainProcessService, mainProcessService); // Policies - const policyService = new PolicyChannelClient(mainProcessService.getChannel('policy')); + const policyService = this.configuration.policiesData ? new PolicyChannelClient(this.configuration.policiesData, mainProcessService.getChannel('policy')) : new NullPolicyService(); services.set(IPolicyService, policyService); // Environment diff --git a/src/vs/platform/policy/common/filePolicyService.ts b/src/vs/platform/policy/common/filePolicyService.ts index 4dadb9a1716a7..71d2ed5fd8760 100644 --- a/src/vs/platform/policy/common/filePolicyService.ts +++ b/src/vs/platform/policy/common/filePolicyService.ts @@ -5,14 +5,13 @@ import { ThrottledDelayer } from 'vs/base/common/async'; import { IStringDictionary } from 'vs/base/common/collections'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; -import { Disposable } from 'vs/base/common/lifecycle'; import { isObject } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; -import { IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { AbstractPolicyService, IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; function keysDiff(a: Map, b: Map): string[] { const result: string[] = []; @@ -26,15 +25,7 @@ function keysDiff(a: Map, b: Map): string[] { return result; } -export class FilePolicyService extends Disposable implements IPolicyService { - - readonly _serviceBrand: undefined; - - private readonly policyNames: Set = new Set(); - private policies = new Map(); - - private readonly _onDidChange = new Emitter(); - readonly onDidChange = this._onDidChange.event; +export class FilePolicyService extends AbstractPolicyService implements IPolicyService { private readonly throttledDelayer = this._register(new ThrottledDelayer(500)); @@ -50,18 +41,8 @@ export class FilePolicyService extends Disposable implements IPolicyService { this._register(onDidChangePolicyFile(() => this.throttledDelayer.trigger(() => this.refresh()))); } - async registerPolicyDefinitions(policies: IStringDictionary): Promise> { - let hasNewPolicies = false; - for (const key of Object.keys(policies)) { - if (!this.policyNames.has(key)) { - hasNewPolicies = true; - this.policyNames.add(key); - } - } - if (hasNewPolicies) { - await this.refresh(); - } - return Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {}); + protected async initializePolicies(policyDefinitions: IStringDictionary): Promise { + await this.refresh(); } private async read(): Promise> { @@ -76,7 +57,7 @@ export class FilePolicyService extends Disposable implements IPolicyService { } for (const key of Object.keys(raw)) { - if (this.policyNames.has(key)) { + if (this.policyDefinitions[key]) { policies.set(key, raw[key]); } } @@ -98,8 +79,4 @@ export class FilePolicyService extends Disposable implements IPolicyService { this._onDidChange.fire(diff); } } - - getPolicyValue(name: PolicyName): PolicyValue | undefined { - return this.policies.get(name); - } } diff --git a/src/vs/platform/policy/common/policy.ts b/src/vs/platform/policy/common/policy.ts index 21ef3302bb09d..d02ddfb77ef18 100644 --- a/src/vs/platform/policy/common/policy.ts +++ b/src/vs/platform/policy/common/policy.ts @@ -4,11 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { IStringDictionary } from 'vs/base/common/collections'; -import { Event } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Iterable } from 'vs/base/common/iterator'; +import { Disposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export type PolicyName = string; -export type PolicyValue = string | boolean; +export type PolicyValue = string | number; export type PolicyDefinition = { type: 'string' | 'number' }; export const IPolicyService = createDecorator('policy'); @@ -17,8 +19,40 @@ export interface IPolicyService { readonly _serviceBrand: undefined; readonly onDidChange: Event; - registerPolicyDefinitions(policies: IStringDictionary): Promise>; + registerPolicyDefinitions(policyDefinitions: IStringDictionary): Promise>; getPolicyValue(name: PolicyName): PolicyValue | undefined; + serialize(): IStringDictionary<{ definition: PolicyDefinition; value: PolicyValue }> | undefined; +} + +export abstract class AbstractPolicyService extends Disposable implements IPolicyService { + readonly _serviceBrand: undefined; + + protected policyDefinitions: IStringDictionary = {}; + protected policies = new Map(); + + protected readonly _onDidChange = this._register(new Emitter()); + readonly onDidChange = this._onDidChange.event; + + async registerPolicyDefinitions(policyDefinitions: IStringDictionary): Promise> { + const size = Object.keys(this.policyDefinitions).length; + this.policyDefinitions = { ...policyDefinitions, ...this.policyDefinitions }; + + if (size !== Object.keys(this.policyDefinitions).length) { + await this.initializePolicies(policyDefinitions); + } + + return Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {}); + } + + getPolicyValue(name: PolicyName): PolicyValue | undefined { + return this.policies.get(name); + } + + serialize(): IStringDictionary<{ definition: PolicyDefinition; value: PolicyValue }> { + return Iterable.reduce<[PolicyName, PolicyDefinition], IStringDictionary<{ definition: PolicyDefinition; value: PolicyValue }>>(Object.entries(this.policyDefinitions), (r, [name, definition]) => ({ ...r, [name]: { definition, value: this.policies.get(name)! } }), {}); + } + + protected abstract initializePolicies(policyDefinitions: IStringDictionary): Promise; } export class NullPolicyService implements IPolicyService { @@ -26,4 +60,5 @@ export class NullPolicyService implements IPolicyService { readonly onDidChange = Event.None; async registerPolicyDefinitions() { return {}; } getPolicyValue() { return undefined; } + serialize() { return undefined; } } diff --git a/src/vs/platform/policy/common/policyIpc.ts b/src/vs/platform/policy/common/policyIpc.ts index 177b1d51ae69e..cdca636e999fe 100644 --- a/src/vs/platform/policy/common/policyIpc.ts +++ b/src/vs/platform/policy/common/policyIpc.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { IStringDictionary } from 'vs/base/common/collections'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { AbstractPolicyService, IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; export class PolicyChannel implements IServerChannel { @@ -29,7 +29,7 @@ export class PolicyChannel implements IServerChannel { call(_: unknown, command: string, arg?: any): Promise { switch (command) { - case 'initialize': return this.service.registerPolicyDefinitions(arg as IStringDictionary); + case 'registerPolicyDefinitions': return this.service.registerPolicyDefinitions(arg as IStringDictionary); } throw new Error(`Call not found: ${command}`); @@ -40,16 +40,17 @@ export class PolicyChannel implements IServerChannel { } } -export class PolicyChannelClient implements IPolicyService { +export class PolicyChannelClient extends AbstractPolicyService implements IPolicyService { - declare readonly _serviceBrand: undefined; - - private policies = new Map(); - - private readonly _onDidChange = new Emitter(); - readonly onDidChange: Event = this._onDidChange.event; - - constructor(private readonly channel: IChannel) { + constructor(policiesData: IStringDictionary<{ definition: PolicyDefinition; value: PolicyValue }>, private readonly channel: IChannel) { + super(); + for (const name in policiesData) { + const { definition, value } = policiesData[name]; + this.policyDefinitions[name] = definition; + if (value !== undefined) { + this.policies.set(name, value); + } + } this.channel.listen('onDidChange')(policies => { for (const name in policies) { const value = policies[name as keyof typeof policies]; @@ -65,17 +66,11 @@ export class PolicyChannelClient implements IPolicyService { }); } - async registerPolicyDefinitions(policies: IStringDictionary): Promise> { - const result = await this.channel.call<{ [name: PolicyName]: PolicyValue }>('initialize', policies); - + protected async initializePolicies(policyDefinitions: IStringDictionary): Promise { + const result = await this.channel.call<{ [name: PolicyName]: PolicyValue }>('registerPolicyDefinitions', policyDefinitions); for (const name in result) { this.policies.set(name, result[name]); } - - return result; } - getPolicyValue(name: PolicyName): PolicyValue | undefined { - return this.policies.get(name); - } } diff --git a/src/vs/platform/policy/node/nativePolicyService.ts b/src/vs/platform/policy/node/nativePolicyService.ts index 9011bbf1534c4..4d0e46d9d068c 100644 --- a/src/vs/platform/policy/node/nativePolicyService.ts +++ b/src/vs/platform/policy/node/nativePolicyService.ts @@ -3,23 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter } from 'vs/base/common/event'; -import { IPolicyService, PolicyDefinition, PolicyName, PolicyValue } from 'vs/platform/policy/common/policy'; +import { AbstractPolicyService, IPolicyService, PolicyDefinition } from 'vs/platform/policy/common/policy'; import { IStringDictionary } from 'vs/base/common/collections'; -import { Iterable } from 'vs/base/common/iterator'; import { Throttler } from 'vs/base/common/async'; import { createWatcher, Watcher } from 'vscode-policy-watcher'; -import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { MutableDisposable } from 'vs/base/common/lifecycle'; -export class NativePolicyService extends Disposable implements IPolicyService { - - readonly _serviceBrand: undefined; - - private policyDefinitions: IStringDictionary = {}; - private readonly policies = new Map(); - - private readonly _onDidChange = this._register(new Emitter()); - readonly onDidChange = this._onDidChange.event; +export class NativePolicyService extends AbstractPolicyService implements IPolicyService { private throttler = new Throttler(); private watcher = this._register(new MutableDisposable()); @@ -28,37 +18,27 @@ export class NativePolicyService extends Disposable implements IPolicyService { super(); } - async registerPolicyDefinitions(policyDefinitions: IStringDictionary): Promise> { - const size = Object.keys(this.policyDefinitions).length; - this.policyDefinitions = { ...policyDefinitions, ...this.policyDefinitions }; - - if (size !== Object.keys(this.policyDefinitions).length) { - await this.throttler.queue(() => new Promise((c, e) => { - try { - this.watcher.value = createWatcher(this.productName, policyDefinitions, update => { - for (const key in update) { - const value = update[key] as any; - - if (value === undefined) { - this.policies.delete(key); - } else { - this.policies.set(key, value); - } + protected async initializePolicies(policyDefinitions: IStringDictionary): Promise { + await this.throttler.queue(() => new Promise((c, e) => { + try { + this.watcher.value = createWatcher(this.productName, policyDefinitions, update => { + for (const key in update) { + const value = update[key] as any; + + if (value === undefined) { + this.policies.delete(key); + } else { + this.policies.set(key, value); } - - this._onDidChange.fire(Object.keys(update)); - c(); - }); - } catch (err) { - e(err); - } - })); - } - - return Iterable.reduce(this.policies.entries(), (r, [name, value]) => ({ ...r, [name]: value }), {}); + } + + this._onDidChange.fire(Object.keys(update)); + c(); + }); + } catch (err) { + e(err); + } + })); } - getPolicyValue(name: PolicyName): PolicyValue | undefined { - return this.policies.get(name); - } } diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index f7046a4d7565e..0ccd16e61e5af 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -22,6 +22,7 @@ import { ISharedProcessWorkerConfiguration } from 'vs/platform/sharedProcess/com import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { WindowError } from 'vs/platform/window/electron-main/window'; import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { IPolicyService } from 'vs/platform/policy/common/policy'; export class SharedProcess extends Disposable implements ISharedProcess { @@ -39,6 +40,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @ILogService private readonly logService: ILogService, + @IPolicyService private readonly policyService: IPolicyService, @IThemeMainService private readonly themeMainService: IThemeMainService, @IProtocolMainService private readonly protocolMainService: IProtocolMainService ) { @@ -242,7 +244,8 @@ export class SharedProcess extends Disposable implements ISharedProcess { userEnv: this.userEnv, args: this.environmentMainService.args, logLevel: this.logService.getLevel(), - product + product, + policiesData: this.policyService.serialize() }); // Load with config diff --git a/src/vs/platform/sharedProcess/node/sharedProcess.ts b/src/vs/platform/sharedProcess/node/sharedProcess.ts index 70c028b7a116d..5c0265c6c4f98 100644 --- a/src/vs/platform/sharedProcess/node/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/node/sharedProcess.ts @@ -3,9 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IStringDictionary } from 'vs/base/common/collections'; import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { LogLevel } from 'vs/platform/log/common/log'; +import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy'; export interface ISharedProcess { @@ -24,4 +26,6 @@ export interface ISharedProcessConfiguration extends ISandboxConfiguration { readonly logLevel: LogLevel; readonly backupWorkspacesPath: string; + + readonly policiesData?: IStringDictionary<{ definition: PolicyDefinition; value: PolicyValue }>; } diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index 2c9591a2beca1..afac7f93ce726 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IStringDictionary } from 'vs/base/common/collections'; import { PerformanceMark } from 'vs/base/common/performance'; import { isLinux, isMacintosh, isNative, isWeb } from 'vs/base/common/platform'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -12,6 +13,7 @@ import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { FileType } from 'vs/platform/files/common/files'; import { LogLevel } from 'vs/platform/log/common/log'; +import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; @@ -289,6 +291,7 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native filesToWait?: IPathsToWaitFor; os: IOSConfiguration; + policiesData?: IStringDictionary<{ definition: PolicyDefinition; value: PolicyValue }>; } /** diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index dd3fb88731fdb..3b8ba525827d8 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -39,6 +39,7 @@ import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, isSingleFolderW import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { IWindowState, ICodeWindow, ILoadEvent, WindowMode, WindowError, LoadReason, defaultWindowState } from 'vs/platform/window/electron-main/window'; import { Color } from 'vs/base/common/color'; +import { IPolicyService } from 'vs/platform/policy/common/policy'; export interface IWindowCreationOptions { state: IWindowState; @@ -149,6 +150,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { config: IWindowCreationOptions, @ILogService private readonly logService: ILogService, @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, + @IPolicyService private readonly policyService: IPolicyService, @IFileService private readonly fileService: IFileService, @IGlobalStorageMainService private readonly globalStorageMainService: IGlobalStorageMainService, @IStorageMainService private readonly storageMainService: IStorageMainService, @@ -870,6 +872,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } configuration.isInitialStartup = false; // since this is a reload + configuration.policiesData = this.policyService.serialize(); // set policies data again // Load config this.load(configuration, { isReload: true, disableExtensions: cli?.['disable-extensions'] }); diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index ec6d18489560b..81ecd35e990c0 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -51,6 +51,7 @@ import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electro import { ICodeWindow, UnloadReason } from 'vs/platform/window/electron-main/window'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { IPolicyService } from 'vs/platform/policy/common/policy'; //#region Helper Interfaces @@ -192,6 +193,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private readonly initialUserEnv: IProcessEnvironment, @ILogService private readonly logService: ILogService, @IStateMainService private readonly stateMainService: IStateMainService, + @IPolicyService private readonly policyService: IPolicyService, @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IBackupMainService private readonly backupMainService: IBackupMainService, @@ -1320,7 +1322,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic autoDetectHighContrast: windowConfig?.autoDetectHighContrast ?? true, autoDetectColorScheme: windowConfig?.autoDetectColorScheme ?? false, accessibilitySupport: app.accessibilitySupportEnabled, - colorScheme: this.themeMainService.getColorScheme() + colorScheme: this.themeMainService.getColorScheme(), + policiesData: this.policyService.serialize(), }; let window: ICodeWindow | undefined; diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 42ada97cea0d1..88955896dcef5 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -51,7 +51,7 @@ import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/electron-sandbox/diskFileSystemProvider'; import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; -import { IPolicyService } from 'vs/platform/policy/common/policy'; +import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; export class DesktopMain extends Disposable { @@ -158,7 +158,7 @@ export class DesktopMain extends Disposable { serviceCollection.set(IMainProcessService, mainProcessService); // Policies - const policyService = new PolicyChannelClient(mainProcessService.getChannel('policy')); + const policyService = this.configuration.policiesData ? new PolicyChannelClient(this.configuration.policiesData, mainProcessService.getChannel('policy')) : new NullPolicyService(); serviceCollection.set(IPolicyService, policyService); // Product From 7ce1b0f88502c32a6024b56494832f6bf255c1fb Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 19 May 2022 11:43:33 +0200 Subject: [PATCH 160/942] perf improvements --- .../configuration/common/configurationService.ts | 8 ++++---- .../configuration/common/configurations.ts | 16 ++++++++++++++-- .../browser/configurationService.ts | 8 ++++---- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationService.ts b/src/vs/platform/configuration/common/configurationService.ts index a1bd9906ee0af..99da40505669c 100644 --- a/src/vs/platform/configuration/common/configurationService.ts +++ b/src/vs/platform/configuration/common/configurationService.ts @@ -10,10 +10,10 @@ import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ConfigurationTarget, IConfigurationChange, IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; import { Configuration, ConfigurationChangeEvent, ConfigurationModel, UserSettings } from 'vs/platform/configuration/common/configurationModels'; -import { DefaultConfiguration, PolicyConfiguration } from 'vs/platform/configuration/common/configurations'; +import { DefaultConfiguration, IPolicyConfiguration, NullPolicyConfiguration, PolicyConfiguration } from 'vs/platform/configuration/common/configurations'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; -import { IPolicyService } from 'vs/platform/policy/common/policy'; +import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable { @@ -21,7 +21,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe private configuration: Configuration; private readonly defaultConfiguration: DefaultConfiguration; - private readonly policyConfiguration: PolicyConfiguration; + private readonly policyConfiguration: IPolicyConfiguration; private readonly userConfiguration: UserSettings; private readonly reloadConfigurationScheduler: RunOnceScheduler; @@ -36,7 +36,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe ) { super(); this.defaultConfiguration = this._register(new DefaultConfiguration()); - this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, policyService, logService)); + this.policyConfiguration = policyService instanceof NullPolicyService ? new NullPolicyConfiguration() : this._register(new PolicyConfiguration(this.defaultConfiguration, policyService, logService)); this.userConfiguration = this._register(new UserSettings(this.settingsResource, undefined, extUriBiasedIgnorePathCase, fileService)); this.configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, new ConfigurationModel()); diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index 6099234ef8eef..81133870a7aa7 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -5,7 +5,7 @@ import { coalesce } from 'vs/base/common/arrays'; import { IStringDictionary } from 'vs/base/common/collections'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { equals } from 'vs/base/common/objects'; import { isEmptyObject } from 'vs/base/common/types'; @@ -78,7 +78,19 @@ export class DefaultConfigurationModel extends ConfigurationModel { } } -export class PolicyConfiguration extends Disposable { +export interface IPolicyConfiguration { + readonly onDidChangeConfiguration: Event; + readonly configurationModel: ConfigurationModel; + initialize(): Promise; +} + +export class NullPolicyConfiguration implements IPolicyConfiguration { + readonly onDidChangeConfiguration = Event.None; + readonly configurationModel = new ConfigurationModel(); + async initialize() { return this.configurationModel; } +} + +export class PolicyConfiguration extends Disposable implements IPolicyConfiguration { private readonly _onDidChangeConfiguration = this._register(new Emitter()); readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event; diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 26f97a95502a9..d32d7e978bb13 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -13,7 +13,7 @@ import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/plat import { IWorkspaceContextService, Workspace as BaseWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkspaceFolder, toWorkspaceFolder, isWorkspaceFolder, IWorkspaceFoldersWillChangeEvent, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier, IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { ConfigurationModel, ConfigurationChangeEvent, AllKeysConfigurationChangeEvent, mergeChanges } from 'vs/platform/configuration/common/configurationModels'; import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange, ConfigurationTargetToString, IConfigurationUpdateOverrides, isConfigurationUpdateOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { PolicyConfiguration } from 'vs/platform/configuration/common/configurations'; +import { IPolicyConfiguration, NullPolicyConfiguration, PolicyConfiguration } from 'vs/platform/configuration/common/configurations'; import { Configuration } from 'vs/workbench/services/configuration/common/configurationModels'; import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService, RestrictedSettings } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -40,7 +40,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { isUndefined } from 'vs/base/common/types'; import { localize } from 'vs/nls'; -import { IPolicyService } from 'vs/platform/policy/common/policy'; +import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; class Workspace extends BaseWorkspace { initialized: boolean = false; @@ -57,7 +57,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat private _configuration: Configuration; private initialized: boolean = false; private readonly defaultConfiguration: DefaultConfiguration; - private readonly policyConfiguration: PolicyConfiguration; + private readonly policyConfiguration: IPolicyConfiguration; private localUserConfiguration: UserConfiguration; private remoteUserConfiguration: RemoteUserConfiguration | null = null; private workspaceConfiguration: WorkspaceConfiguration; @@ -114,7 +114,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat this.initRemoteUserConfigurationBarrier = new Barrier(); this.completeWorkspaceBarrier = new Barrier(); this.defaultConfiguration = this._register(new DefaultConfiguration(configurationCache, environmentService)); - this.policyConfiguration = this._register(new PolicyConfiguration(this.defaultConfiguration, policyService, logService)); + this.policyConfiguration = policyService instanceof NullPolicyService ? new NullPolicyConfiguration() : this._register(new PolicyConfiguration(this.defaultConfiguration, policyService, logService)); this.configurationCache = configurationCache; this.fileService = fileService; this.uriIdentityService = uriIdentityService; From e783fdc25e8edee0aed71f05f3bacafe962359ab Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 19 May 2022 12:48:53 +0200 Subject: [PATCH 161/942] css/html/json update to lsp 8 (#148715) --- .../client/src/browser/cssClientMain.ts | 16 +- .../client/src/cssClient.ts | 43 ++- .../client/src/node/cssClientMain.ts | 16 +- .../client/src/requests.ts | 4 +- extensions/css-language-features/package.json | 17 +- .../css-language-features/server/package.json | 4 +- .../server/src/cssServer.ts | 23 +- .../css-language-features/server/yarn.lock | 58 ++-- extensions/css-language-features/yarn.lock | 48 +-- .../client/src/browser/htmlClientMain.ts | 16 +- .../client/src/htmlClient.ts | 123 ++++--- .../client/src/node/htmlClientMain.ts | 14 +- .../client/src/requests.ts | 4 +- .../html-language-features/package.json | 6 +- .../server/package.json | 10 +- .../server/src/htmlServer.ts | 14 +- .../server/src/modes/cssMode.ts | 4 +- .../server/src/modes/javascriptMode.ts | 29 +- .../server/src/modes/languageModes.ts | 10 + .../html-language-features/server/yarn.lock | 75 ++-- extensions/html-language-features/yarn.lock | 68 ++-- .../client/src/browser/jsonClientMain.ts | 15 +- .../client/src/jsonClient.ts | 320 +++++++++--------- .../client/src/node/jsonClientMain.ts | 13 +- .../json-language-features/package.json | 4 +- .../server/package.json | 4 +- .../server/src/jsonServer.ts | 20 +- .../json-language-features/server/yarn.lock | 68 ++-- extensions/json-language-features/yarn.lock | 60 ++-- .../test/colorize-fixtures/test.html | 2 +- 30 files changed, 589 insertions(+), 519 deletions(-) diff --git a/extensions/css-language-features/client/src/browser/cssClientMain.ts b/extensions/css-language-features/client/src/browser/cssClientMain.ts index 0cfde9025f3a3..8fa2d81bd033a 100644 --- a/extensions/css-language-features/client/src/browser/cssClientMain.ts +++ b/extensions/css-language-features/client/src/browser/cssClientMain.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ExtensionContext, Uri } from 'vscode'; -import { LanguageClientOptions } from 'vscode-languageclient'; +import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient'; import { startClient, LanguageClientConstructor } from '../cssClient'; import { LanguageClient } from 'vscode-languageclient/browser'; @@ -15,8 +15,10 @@ declare const TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string }; }; +let client: BaseLanguageClient | undefined; + // this method is called when vs code is activated -export function activate(context: ExtensionContext) { +export async function activate(context: ExtensionContext) { const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/cssServerMain.js'); try { const worker = new Worker(serverMain.toString()); @@ -24,9 +26,17 @@ export function activate(context: ExtensionContext) { return new LanguageClient(id, name, clientOptions, worker); }; - startClient(context, newLanguageClient, { TextDecoder }); + client = await startClient(context, newLanguageClient, { TextDecoder }); } catch (e) { console.log(e); } } + +export async function deactivate(): Promise { + if (client) { + await client.stop(); + client = undefined; + } +} + diff --git a/extensions/css-language-features/client/src/cssClient.ts b/extensions/css-language-features/client/src/cssClient.ts index 282c347bdd890..6f6238465d8a9 100644 --- a/extensions/css-language-features/client/src/cssClient.ts +++ b/extensions/css-language-features/client/src/cssClient.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, TextDocument, CompletionContext, CancellationToken, ProviderResult, CompletionList, FormattingOptions, workspace } from 'vscode'; -import { Disposable, LanguageClientOptions, ProvideCompletionItemsSignature, NotificationType, CommonLanguageClient, DocumentRangeFormattingParams, DocumentRangeFormattingRequest } from 'vscode-languageclient'; +import { Disposable, LanguageClientOptions, ProvideCompletionItemsSignature, NotificationType, BaseLanguageClient, DocumentRangeFormattingParams, DocumentRangeFormattingRequest } from 'vscode-languageclient'; import * as nls from 'vscode-nls'; import { getCustomDataSource } from './customData'; import { RequestService, serveFileSystemRequests } from './requests'; @@ -15,7 +15,7 @@ namespace CustomDataChangedNotification { const localize = nls.loadMessageBundle(); -export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient; +export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient; export interface Runtime { TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string } }; @@ -39,7 +39,7 @@ interface CSSFormatSettings { const cssFormatSettingKeys: (keyof CSSFormatSettings)[] = ['newlineBetweenSelectors', 'newlineBetweenRules', 'spaceAroundSelectorSeparator', 'braceStyle', 'preserveNewLines', 'maxPreserveNewLines']; -export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) { +export async function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise { const customDataSource = getCustomDataSource(context.subscriptions); @@ -100,31 +100,25 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua // Create the language client and start the client. let client = newLanguageClient('css', localize('cssserver.name', 'CSS Language Server'), clientOptions); client.registerProposedFeatures(); - client.onReady().then(() => { + await client.start(); + + client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); + customDataSource.onDidChange(() => { client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); - customDataSource.onDidChange(() => { - client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); - }); + }); - // manually register / deregister format provider based on the `css/less/scss.format.enable` setting avoiding issues with late registration. See #71652. - for (const registration of formatterRegistrations) { - updateFormatterRegistration(registration); - context.subscriptions.push({ dispose: () => registration.provider?.dispose() }); - context.subscriptions.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration(registration.settingId) && updateFormatterRegistration(registration))); - } + // manually register / deregister format provider based on the `css/less/scss.format.enable` setting avoiding issues with late registration. See #71652. + for (const registration of formatterRegistrations) { + updateFormatterRegistration(registration); + context.subscriptions.push({ dispose: () => registration.provider?.dispose() }); + context.subscriptions.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration(registration.settingId) && updateFormatterRegistration(registration))); + } - serveFileSystemRequests(client, runtime); - }); + serveFileSystemRequests(client, runtime); - let disposable = client.start(); - // Push the disposable to the context's subscriptions so that the - // client can be deactivated on extension deactivation - context.subscriptions.push(disposable); - client.onReady().then(() => { - context.subscriptions.push(initCompletionProvider()); - }); + context.subscriptions.push(initCompletionProvider()); function initCompletionProvider(): Disposable { const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/; @@ -204,11 +198,10 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua } } } - console.log(JSON.stringify(params.options)); return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then( client.protocol2CodeConverter.asTextEdits, (error) => { - client.handleFailedRequest(DocumentRangeFormattingRequest.type, error, []); + client.handleFailedRequest(DocumentRangeFormattingRequest.type, undefined, error, []); return Promise.resolve([]); } ); @@ -216,4 +209,6 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua }); } } + + return client; } diff --git a/extensions/css-language-features/client/src/node/cssClientMain.ts b/extensions/css-language-features/client/src/node/cssClientMain.ts index b88838d89126d..dfbe121f82266 100644 --- a/extensions/css-language-features/client/src/node/cssClientMain.ts +++ b/extensions/css-language-features/client/src/node/cssClientMain.ts @@ -6,11 +6,14 @@ import { getNodeFSRequestService } from './nodeFs'; import { ExtensionContext, extensions } from 'vscode'; import { startClient, LanguageClientConstructor } from '../cssClient'; -import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node'; +import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient, BaseLanguageClient } from 'vscode-languageclient/node'; import { TextDecoder } from 'util'; + +let client: BaseLanguageClient | undefined; + // this method is called when vs code is activated -export function activate(context: ExtensionContext) { +export async function activate(context: ExtensionContext) { const clientMain = extensions.getExtension('vscode.css-language-features')?.packageJSON?.main || ''; const serverMain = `./server/${clientMain.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/cssServerMain`; @@ -30,5 +33,12 @@ export function activate(context: ExtensionContext) { return new LanguageClient(id, name, serverOptions, clientOptions); }; - startClient(context, newLanguageClient, { fs: getNodeFSRequestService(), TextDecoder }); + client = await startClient(context, newLanguageClient, { fs: getNodeFSRequestService(), TextDecoder }); +} + +export async function deactivate(): Promise { + if (client) { + await client.stop(); + client = undefined; + } } diff --git a/extensions/css-language-features/client/src/requests.ts b/extensions/css-language-features/client/src/requests.ts index 6aea3ab8fade3..f19918e57ef9e 100644 --- a/extensions/css-language-features/client/src/requests.ts +++ b/extensions/css-language-features/client/src/requests.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Uri, workspace } from 'vscode'; -import { RequestType, CommonLanguageClient } from 'vscode-languageclient'; +import { RequestType, BaseLanguageClient } from 'vscode-languageclient'; import { Runtime } from './cssClient'; export namespace FsContentRequest { @@ -18,7 +18,7 @@ export namespace FsReadDirRequest { export const type: RequestType = new RequestType('fs/readDir'); } -export function serveFileSystemRequests(client: CommonLanguageClient, runtime: Runtime) { +export function serveFileSystemRequests(client: BaseLanguageClient, runtime: Runtime) { client.onRequest(FsContentRequest.type, (param: { uri: string; encoding?: string }) => { const uri = Uri.parse(param.uri); if (uri.scheme === 'file' && runtime.fs) { diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index e9ea587d4db49..c4955243c8f22 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -336,7 +336,10 @@ "type": "string", "scope": "resource", "default": "collapse", - "enum": ["collapse", "expand"], + "enum": [ + "collapse", + "expand" + ], "markdownDescription": "%css.format.braceStyle.desc%" }, "css.format.preserveNewLines": { @@ -638,7 +641,10 @@ "type": "string", "scope": "resource", "default": "collapse", - "enum": ["collapse", "expand"], + "enum": [ + "collapse", + "expand" + ], "markdownDescription": "%scss.format.braceStyle.desc%" }, "scss.format.preserveNewLines": { @@ -941,7 +947,10 @@ "type": "string", "scope": "resource", "default": "collapse", - "enum": ["collapse", "expand"], + "enum": [ + "collapse", + "expand" + ], "markdownDescription": "%less.format.braceStyle.desc%" }, "less.format.preserveNewLines": { @@ -985,7 +994,7 @@ ] }, "dependencies": { - "vscode-languageclient": "^7.0.0", + "vscode-languageclient": "^8.0.1", "vscode-nls": "^5.0.0", "vscode-uri": "^3.0.3" }, diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 18c39540516bb..d2f0443e65b13 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -10,8 +10,8 @@ "main": "./out/node/cssServerMain", "browser": "./dist/browser/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^5.4.2", - "vscode-languageserver": "^7.0.0", + "vscode-css-languageservice": "^6.0.1", + "vscode-languageserver": "^8.0.1", "vscode-uri": "^3.0.3" }, "devDependencies": { diff --git a/extensions/css-language-features/server/src/cssServer.ts b/extensions/css-language-features/server/src/cssServer.ts index 40e05b13ab1e4..3eed39628793d 100644 --- a/extensions/css-language-features/server/src/cssServer.ts +++ b/extensions/css-language-features/server/src/cssServer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { - Connection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind, NotificationType, Disposable, TextDocumentIdentifier, Range, FormattingOptions, TextEdit + Connection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind, NotificationType, Disposable, TextDocumentIdentifier, Range, FormattingOptions, TextEdit, Diagnostic } from 'vscode-languageserver'; import { URI } from 'vscode-uri'; import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, TextDocument, Position, CSSFormatConfiguration } from 'vscode-css-languageservice'; @@ -64,6 +64,9 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) // After the server has started the client sends an initialize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities. connection.onInitialize((params: InitializeParams): InitializeResult => { + + const initializationOptions = params.initializationOptions as any || {}; + workspaceFolders = (params).workspaceFolders; if (!Array.isArray(workspaceFolders)) { workspaceFolders = []; @@ -72,7 +75,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } } - requestService = getRequestService(params.initializationOptions?.handledSchemas || ['file'], connection, runtime); + requestService = getRequestService(initializationOptions?.handledSchemas || ['file'], connection, runtime); function getClientCapability(name: string, def: T) { const keys = name.split('.'); @@ -88,7 +91,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const snippetSupport = !!getClientCapability('textDocument.completion.completionItem.snippetSupport', false); scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); - formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; + formatterMaxNumberOfEdits = initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; languageServices.css = getCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); languageServices.scss = getSCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); @@ -110,8 +113,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) colorProvider: {}, foldingRangeProvider: true, selectionRangeProvider: true, - documentRangeFormattingProvider: params.initializationOptions?.provideFormatter === true, - documentFormattingProvider: params.initializationOptions?.provideFormatter === true, + documentRangeFormattingProvider: initializationOptions?.provideFormatter === true, + documentFormattingProvider: initializationOptions?.provideFormatter === true, }; return { capabilities }; }); @@ -135,7 +138,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) let promise = documentSettings[textDocument.uri]; if (!promise) { const configRequestParam = { items: [{ scopeUri: textDocument.uri, section: textDocument.languageId }] }; - promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => s[0]); + promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => s[0] as LanguageSettings | undefined); documentSettings[textDocument.uri] = promise; } return promise; @@ -145,12 +148,12 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) // The settings have changed. Is send on server activation as well. connection.onDidChangeConfiguration(change => { - updateConfiguration(change.settings); + updateConfiguration(change.settings as any); }); - function updateConfiguration(settings: Settings) { + function updateConfiguration(settings: any) { for (const languageId in languageServices) { - languageServices[languageId].configure((settings as any)[languageId]); + languageServices[languageId].configure(settings[languageId]); } // reset all document settings documentSettings = {}; @@ -193,7 +196,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const settingsPromise = getDocumentSettings(textDocument); Promise.all([settingsPromise, dataProvidersReady]).then(async ([settings]) => { const stylesheet = stylesheets.get(textDocument); - const diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings); + const diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings) as Diagnostic[]; // Send the computed diagnostics to VSCode. connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); }, e => { diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index b7a44bc52c478..8749dcdebadde 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -12,50 +12,50 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== -vscode-css-languageservice@^5.4.2: - version "5.4.2" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.4.2.tgz#69ea74c000bd653dfc8e458a1720d28b9ffa5cfb" - integrity sha512-DT7+7vfdT2HDNjDoXWtYJ0lVDdeDEdbMNdK4PKqUl2MS8g7PWt7J5G9B6k9lYox8nOfhCEjLnoNC3UKHHCR1lg== +vscode-css-languageservice@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.0.1.tgz#ccf94944e094dcc5833d1b4ac276994b698e9283" + integrity sha512-81n/eeYuJwQdvpoy6IK1258PtPbO720fl13FcJ5YQECPyHMFkmld1qKHwPJkyLbLPfboqJPM53ys4xW8v+iBVw== dependencies: vscode-languageserver-textdocument "^1.0.4" - vscode-languageserver-types "^3.16.0" - vscode-nls "^5.0.0" + vscode-languageserver-types "^3.17.1" + vscode-nls "^5.0.1" vscode-uri "^3.0.3" -vscode-jsonrpc@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e" - integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg== +vscode-jsonrpc@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e" + integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ== -vscode-languageserver-protocol@3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821" - integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A== +vscode-languageserver-protocol@3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed" + integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg== dependencies: - vscode-jsonrpc "6.0.0" - vscode-languageserver-types "3.16.0" + vscode-jsonrpc "8.0.1" + vscode-languageserver-types "3.17.1" vscode-languageserver-textdocument@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.4.tgz#3cd56dd14cec1d09e86c4bb04b09a246cb3df157" integrity sha512-/xhqXP/2A2RSs+J8JNXpiiNVvvNM0oTosNVmQnunlKvq9o4mupHOBAnnzH0lwIPKazXKvAKsVp1kr+H/K4lgoQ== -vscode-languageserver-types@3.16.0, vscode-languageserver-types@^3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247" - integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== +vscode-languageserver-types@3.17.1, vscode-languageserver-types@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16" + integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ== -vscode-languageserver@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz#49b068c87cfcca93a356969d20f5d9bdd501c6b0" - integrity sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw== +vscode-languageserver@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.0.1.tgz#56bd7a01f5c88af075a77f1d220edcb30fc4bdc7" + integrity sha512-sn7SjBwWm3OlmLtgg7jbM0wBULppyL60rj8K5HF0ny/MzN+GzPBX1kCvYdybhl7UW63V5V5tRVnyB8iwC73lSQ== dependencies: - vscode-languageserver-protocol "3.16.0" + vscode-languageserver-protocol "3.17.1" -vscode-nls@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" - integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== +vscode-nls@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2" + integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== vscode-uri@^3.0.3: version "3.0.3" diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index 60ba7c2dcc3ee..76af92973f2c8 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -39,39 +39,39 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -semver@^7.3.4: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== +semver@^7.3.5: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" -vscode-jsonrpc@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e" - integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg== +vscode-jsonrpc@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e" + integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ== -vscode-languageclient@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz#b505c22c21ffcf96e167799757fca07a6bad0fb2" - integrity sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg== +vscode-languageclient@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.0.1.tgz#bf5535c4463a78daeaca0bcb4f5868aec86bb301" + integrity sha512-9XoE+HJfaWvu7Y75H3VmLo5WLCtsbxEgEhrLPqwt7eyoR49lUIyyrjb98Yfa50JCMqF2cePJAEVI6oe2o1sIhw== dependencies: minimatch "^3.0.4" - semver "^7.3.4" - vscode-languageserver-protocol "3.16.0" + semver "^7.3.5" + vscode-languageserver-protocol "3.17.1" -vscode-languageserver-protocol@3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821" - integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A== +vscode-languageserver-protocol@3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed" + integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg== dependencies: - vscode-jsonrpc "6.0.0" - vscode-languageserver-types "3.16.0" + vscode-jsonrpc "8.0.1" + vscode-languageserver-types "3.17.1" -vscode-languageserver-types@3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247" - integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== +vscode-languageserver-types@3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16" + integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ== vscode-nls@^5.0.0: version "5.0.0" diff --git a/extensions/html-language-features/client/src/browser/htmlClientMain.ts b/extensions/html-language-features/client/src/browser/htmlClientMain.ts index b69cca7585449..ab23520fe79f1 100644 --- a/extensions/html-language-features/client/src/browser/htmlClientMain.ts +++ b/extensions/html-language-features/client/src/browser/htmlClientMain.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, ExtensionContext, Uri } from 'vscode'; -import { LanguageClientOptions } from 'vscode-languageclient'; +import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient'; import { startClient, LanguageClientConstructor } from '../htmlClient'; import { LanguageClient } from 'vscode-languageclient/browser'; @@ -15,8 +15,10 @@ declare const TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string }; }; +let client: BaseLanguageClient | undefined; + // this method is called when vs code is activated -export function activate(context: ExtensionContext) { +export async function activate(context: ExtensionContext) { const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/htmlServerMain.js'); try { const worker = new Worker(serverMain.toString()); @@ -31,9 +33,17 @@ export function activate(context: ExtensionContext) { } }; - startClient(context, newLanguageClient, { TextDecoder, timer }); + client = await startClient(context, newLanguageClient, { TextDecoder, timer }); } catch (e) { console.log(e); } } + +export async function deactivate(): Promise { + if (client) { + await client.stop(); + client = undefined; + } +} + diff --git a/extensions/html-language-features/client/src/htmlClient.ts b/extensions/html-language-features/client/src/htmlClient.ts index 7baceece6fb6e..6c44ceb72a831 100644 --- a/extensions/html-language-features/client/src/htmlClient.ts +++ b/extensions/html-language-features/client/src/htmlClient.ts @@ -13,7 +13,7 @@ import { } from 'vscode'; import { LanguageClientOptions, RequestType, DocumentRangeFormattingParams, - DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, TextDocumentIdentifier, RequestType0, Range as LspRange, Position as LspPosition, NotificationType, CommonLanguageClient + DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, TextDocumentIdentifier, RequestType0, Range as LspRange, Position as LspPosition, NotificationType, BaseLanguageClient } from 'vscode-languageclient'; import { FileSystemProvider, serveFileSystemRequests } from './requests'; import { getCustomDataSource } from './customData'; @@ -72,7 +72,7 @@ export interface TelemetryReporter { }): void; } -export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient; +export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient; export interface Runtime { TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string } }; @@ -83,18 +83,17 @@ export interface Runtime { }; } -export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) { +export async function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise { - let toDispose = context.subscriptions; + const toDispose = context.subscriptions; - - let documentSelector = ['html', 'handlebars']; - let embeddedLanguages = { css: true, javascript: true }; + const documentSelector = ['html', 'handlebars']; + const embeddedLanguages = { css: true, javascript: true }; let rangeFormatting: Disposable | undefined = undefined; // Options to control the language client - let clientOptions: LanguageClientOptions = { + const clientOptions: LanguageClientOptions = { documentSelector, synchronize: { configurationSection: ['html', 'css', 'javascript'], // the settings to synchronize @@ -135,68 +134,66 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua let client = newLanguageClient('html', localize('htmlserver.name', 'HTML Language Server'), clientOptions); client.registerProposedFeatures(); - let disposable = client.start(); - toDispose.push(disposable); - client.onReady().then(() => { + await client.start(); - toDispose.push(serveFileSystemRequests(client, runtime)); + toDispose.push(serveFileSystemRequests(client, runtime)); - const customDataSource = getCustomDataSource(runtime, context.subscriptions); + const customDataSource = getCustomDataSource(runtime, context.subscriptions); + client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); + customDataSource.onDidChange(() => { client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); - customDataSource.onDidChange(() => { - client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); - }); - client.onRequest(CustomDataContent.type, customDataSource.getContent); + }); + client.onRequest(CustomDataContent.type, customDataSource.getContent); - const insertRequestor = (kind: 'autoQuote' | 'autoClose', document: TextDocument, position: Position): Promise => { - let param: AutoInsertParams = { - kind, - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), - position: client.code2ProtocolConverter.asPosition(position) - }; - return client.sendRequest(AutoInsertRequest.type, param); + const insertRequestor = (kind: 'autoQuote' | 'autoClose', document: TextDocument, position: Position): Promise => { + const param: AutoInsertParams = { + kind, + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), + position: client.code2ProtocolConverter.asPosition(position) }; - let disposable = activateAutoInsertion(insertRequestor, { html: true, handlebars: true }, runtime); - toDispose.push(disposable); - - disposable = client.onTelemetry(e => { - runtime.telemetry?.sendTelemetryEvent(e.key, e.data); - }); - toDispose.push(disposable); - - // manually register / deregister format provider based on the `html.format.enable` setting avoiding issues with late registration. See #71652. - updateFormatterRegistration(); - toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() }); - toDispose.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration(SettingIds.formatEnable) && updateFormatterRegistration())); - - client.sendRequest(SemanticTokenLegendRequest.type).then(legend => { - if (legend) { - const provider: DocumentSemanticTokensProvider & DocumentRangeSemanticTokensProvider = { - provideDocumentSemanticTokens(doc) { - const params: SemanticTokenParams = { - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(doc), - }; - return client.sendRequest(SemanticTokenRequest.type, params).then(data => { - return data && new SemanticTokens(new Uint32Array(data)); - }); - }, - provideDocumentRangeSemanticTokens(doc, range) { - const params: SemanticTokenParams = { - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(doc), - ranges: [client.code2ProtocolConverter.asRange(range)] - }; - return client.sendRequest(SemanticTokenRequest.type, params).then(data => { - return data && new SemanticTokens(new Uint32Array(data)); - }); - } - }; - toDispose.push(languages.registerDocumentSemanticTokensProvider(documentSelector, provider, new SemanticTokensLegend(legend.types, legend.modifiers))); - } - }); + return client.sendRequest(AutoInsertRequest.type, param); + }; + const disposable = activateAutoInsertion(insertRequestor, { html: true, handlebars: true }, runtime); + toDispose.push(disposable); + + const disposable2 = client.onTelemetry(e => { + runtime.telemetry?.sendTelemetryEvent(e.key, e.data); + }); + toDispose.push(disposable2); + + // manually register / deregister format provider based on the `html.format.enable` setting avoiding issues with late registration. See #71652. + updateFormatterRegistration(); + toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() }); + toDispose.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration(SettingIds.formatEnable) && updateFormatterRegistration())); + + client.sendRequest(SemanticTokenLegendRequest.type).then(legend => { + if (legend) { + const provider: DocumentSemanticTokensProvider & DocumentRangeSemanticTokensProvider = { + provideDocumentSemanticTokens(doc) { + const params: SemanticTokenParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(doc), + }; + return client.sendRequest(SemanticTokenRequest.type, params).then(data => { + return data && new SemanticTokens(new Uint32Array(data)); + }); + }, + provideDocumentRangeSemanticTokens(doc, range) { + const params: SemanticTokenParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(doc), + ranges: [client.code2ProtocolConverter.asRange(range)] + }; + return client.sendRequest(SemanticTokenRequest.type, params).then(data => { + return data && new SemanticTokens(new Uint32Array(data)); + }); + } + }; + toDispose.push(languages.registerDocumentSemanticTokensProvider(documentSelector, provider, new SemanticTokensLegend(legend.types, legend.modifiers))); + } }); + function updateFormatterRegistration() { const formatEnabled = workspace.getConfiguration().get(SettingIds.formatEnable); if (!formatEnabled && rangeFormatting) { @@ -219,7 +216,7 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then( client.protocol2CodeConverter.asTextEdits, (error) => { - client.handleFailedRequest(DocumentRangeFormattingRequest.type, error, []); + client.handleFailedRequest(DocumentRangeFormattingRequest.type, undefined, error, []); return Promise.resolve([]); } ); @@ -300,4 +297,6 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua } } + return client; + } diff --git a/extensions/html-language-features/client/src/node/htmlClientMain.ts b/extensions/html-language-features/client/src/node/htmlClientMain.ts index 4c7d24e397c2c..f460d0c15248c 100644 --- a/extensions/html-language-features/client/src/node/htmlClientMain.ts +++ b/extensions/html-language-features/client/src/node/htmlClientMain.ts @@ -6,16 +6,17 @@ import { getNodeFileFS } from './nodeFs'; import { Disposable, ExtensionContext } from 'vscode'; import { startClient, LanguageClientConstructor } from '../htmlClient'; -import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node'; +import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient, BaseLanguageClient } from 'vscode-languageclient/node'; import { TextDecoder } from 'util'; import * as fs from 'fs'; import TelemetryReporter from '@vscode/extension-telemetry'; let telemetry: TelemetryReporter | undefined; +let client: BaseLanguageClient | undefined; // this method is called when vs code is activated -export function activate(context: ExtensionContext) { +export async function activate(context: ExtensionContext) { let clientPackageJSON = getPackageInfo(context); telemetry = new TelemetryReporter(clientPackageJSON.name, clientPackageJSON.version, clientPackageJSON.aiKey); @@ -44,7 +45,14 @@ export function activate(context: ExtensionContext) { } }; - startClient(context, newLanguageClient, { fileFs: getNodeFileFS(), TextDecoder, telemetry, timer }); + client = await startClient(context, newLanguageClient, { fileFs: getNodeFileFS(), TextDecoder, telemetry, timer }); +} + +export async function deactivate(): Promise { + if (client) { + await client.stop(); + client = undefined; + } } interface IPackageInfo { diff --git a/extensions/html-language-features/client/src/requests.ts b/extensions/html-language-features/client/src/requests.ts index ba124e28cd7cf..8106f0442280f 100644 --- a/extensions/html-language-features/client/src/requests.ts +++ b/extensions/html-language-features/client/src/requests.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Uri, workspace, Disposable } from 'vscode'; -import { RequestType, CommonLanguageClient } from 'vscode-languageclient'; +import { RequestType, BaseLanguageClient } from 'vscode-languageclient'; import { Runtime } from './htmlClient'; export namespace FsStatRequest { @@ -15,7 +15,7 @@ export namespace FsReadDirRequest { export const type: RequestType = new RequestType('fs/readDir'); } -export function serveFileSystemRequests(client: CommonLanguageClient, runtime: Runtime): Disposable { +export function serveFileSystemRequests(client: BaseLanguageClient, runtime: Runtime): Disposable { const disposables = []; disposables.push(client.onRequest(FsReadDirRequest.type, (uriString: string) => { const uri = Uri.parse(uriString); diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index e7244d251c0f8..2f2dc4d106701 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -261,9 +261,9 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "0.4.10", - "vscode-languageclient": "^7.0.0", - "vscode-nls": "^5.0.0", + "@vscode/extension-telemetry": "0.5.1", + "vscode-languageclient": "^8.0.1", + "vscode-nls": "^5.0.1", "vscode-uri": "^3.0.3" }, "devDependencies": { diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 423a395ba96b5..affcf98e56680 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,11 +9,11 @@ }, "main": "./out/node/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^5.4.2", - "vscode-html-languageservice": "^4.2.5", - "vscode-languageserver": "^7.0.0", - "vscode-languageserver-textdocument": "^1.0.3", - "vscode-nls": "^5.0.0", + "vscode-css-languageservice": "^6.0.1", + "vscode-html-languageservice": "^5.0.0", + "vscode-languageserver": "^8.0.1", + "vscode-languageserver-textdocument": "^1.0.4", + "vscode-nls": "^5.0.1", "vscode-uri": "^3.0.3" }, "devDependencies": { diff --git a/extensions/html-language-features/server/src/htmlServer.ts b/extensions/html-language-features/server/src/htmlServer.ts index b3df5fc8e513f..319399a038c68 100644 --- a/extensions/html-language-features/server/src/htmlServer.ts +++ b/extensions/html-language-features/server/src/htmlServer.ts @@ -11,7 +11,7 @@ import { } from 'vscode-languageserver'; import { getLanguageModes, LanguageModes, Settings, TextDocument, Position, Diagnostic, WorkspaceFolder, ColorInformation, - Range, DocumentLink, SymbolInformation, TextDocumentIdentifier + Range, DocumentLink, SymbolInformation, TextDocumentIdentifier, isCompletionItemData } from './modes/languageModes'; import { format } from './modes/formatting'; @@ -129,7 +129,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) // After the server has started the client sends an initialize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities connection.onInitialize((params: InitializeParams): InitializeResult => { - const initializationOptions = params.initializationOptions; + const initializationOptions = params.initializationOptions as any || {}; workspaceFolders = (params).workspaceFolders; if (!Array.isArray(workspaceFolders)) { @@ -179,14 +179,14 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) scopedSettingsSupport = getClientCapability('workspace.configuration', false); workspaceFoldersSupport = getClientCapability('workspace.workspaceFolders', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); - formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; + formatterMaxNumberOfEdits = initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; const capabilities: ServerCapabilities = { textDocumentSync: TextDocumentSyncKind.Incremental, completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/'] } : undefined, hoverProvider: true, documentHighlightProvider: true, - documentRangeFormattingProvider: params.initializationOptions?.provideFormatter === true, - documentFormattingProvider: params.initializationOptions?.provideFormatter === true, + documentRangeFormattingProvider: initializationOptions?.provideFormatter === true, + documentFormattingProvider: initializationOptions?.provideFormatter === true, documentLinkProvider: { resolveProvider: false }, documentSymbolProvider: true, definitionProvider: true, @@ -226,7 +226,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) // The settings have changed. Is send on server activation as well. connection.onDidChangeConfiguration((change) => { - globalSettings = change.settings; + globalSettings = change.settings as Settings; documentSettings = {}; // reset all document settings documents.all().forEach(triggerValidation); @@ -331,7 +331,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) connection.onCompletionResolve((item, token) => { return runSafe(runtime, async () => { const data = item.data; - if (data && data.languageId && data.uri) { + if (isCompletionItemData(data)) { const mode = languageModes.getMode(data.languageId); const document = documents.get(data.uri); if (mode && mode.doResolve && document) { diff --git a/extensions/html-language-features/server/src/modes/cssMode.ts b/extensions/html-language-features/server/src/modes/cssMode.ts index 9757d99cd8862..6bc02acb510aa 100644 --- a/extensions/html-language-features/server/src/modes/cssMode.ts +++ b/extensions/html-language-features/server/src/modes/cssMode.ts @@ -5,7 +5,7 @@ import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache'; import { Stylesheet, LanguageService as CSSLanguageService } from 'vscode-css-languageservice'; -import { LanguageMode, Workspace, Color, TextDocument, Position, Range, CompletionList, DocumentContext } from './languageModes'; +import { LanguageMode, Workspace, Color, TextDocument, Position, Range, CompletionList, DocumentContext, Diagnostic } from './languageModes'; import { HTMLDocumentRegions, CSS_STYLE_RULE } from './embeddedSupport'; export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegions: LanguageModelCache, workspace: Workspace): LanguageMode { @@ -18,7 +18,7 @@ export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegio }, async doValidation(document: TextDocument, settings = workspace.settings) { let embedded = embeddedCSSDocuments.get(document); - return cssLanguageService.doValidation(embedded, cssStylesheets.get(embedded), settings && settings.css); + return (cssLanguageService.doValidation(embedded, cssStylesheets.get(embedded), settings && settings.css) as Diagnostic[]); }, async doComplete(document: TextDocument, position: Position, documentContext: DocumentContext, _settings = workspace.settings) { let embedded = embeddedCSSDocuments.get(document); diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index bbcba5e0f86ba..7f9e3f1d85a07 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -8,7 +8,7 @@ import { SymbolInformation, SymbolKind, CompletionItem, Location, SignatureHelp, SignatureInformation, ParameterInformation, Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions, FoldingRange, FoldingRangeKind, SelectionRange, - LanguageMode, Settings, SemanticTokenData, Workspace, DocumentContext + LanguageMode, Settings, SemanticTokenData, Workspace, DocumentContext, CompletionItemData, isCompletionItemData } from './languageModes'; import { getWordAtText, isWhitespaceOnly, repeat } from '../utils/strings'; import { HTMLDocumentRegions } from './embeddedSupport'; @@ -122,6 +122,11 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { + const data: CompletionItemData = { // data used for resolving item details (see 'doResolve') + languageId, + uri: document.uri, + offset: offset + }; return { uri: document.uri, position: position, @@ -129,23 +134,21 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { - const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - let details = jsLanguageService.getCompletionEntryDetails(jsDocument.uri, item.data.offset, item.label, undefined, undefined, undefined, undefined); - if (details) { - item.detail = ts.displayPartsToString(details.displayParts); - item.documentation = ts.displayPartsToString(details.documentation); - delete item.data; + if (isCompletionItemData(item.data)) { + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument); + let details = jsLanguageService.getCompletionEntryDetails(jsDocument.uri, item.data.offset, item.label, undefined, undefined, undefined, undefined); + if (details) { + item.detail = ts.displayPartsToString(details.displayParts); + item.documentation = ts.displayPartsToString(details.documentation); + delete item.data; + } } return item; }, diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index 3a8b9b797cc6a..59cc742b2b8f7 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -54,6 +54,16 @@ export interface SemanticTokenData { modifierSet: number; } +export type CompletionItemData = { + languageId: string; + uri: string; + offset: number; +}; + +export function isCompletionItemData(value: any): value is CompletionItemData { + return value && typeof value.languageId === 'string' && typeof value.uri === 'string' && typeof value.offset === 'number'; +} + export interface LanguageMode { getId(): string; getSelectionRange?: (document: TextDocument, position: Position) => Promise; diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index a5bfebda600be..f086cbf6898ed 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -12,65 +12,60 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== -vscode-css-languageservice@^5.4.2: - version "5.4.2" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.4.2.tgz#69ea74c000bd653dfc8e458a1720d28b9ffa5cfb" - integrity sha512-DT7+7vfdT2HDNjDoXWtYJ0lVDdeDEdbMNdK4PKqUl2MS8g7PWt7J5G9B6k9lYox8nOfhCEjLnoNC3UKHHCR1lg== +vscode-css-languageservice@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.0.1.tgz#ccf94944e094dcc5833d1b4ac276994b698e9283" + integrity sha512-81n/eeYuJwQdvpoy6IK1258PtPbO720fl13FcJ5YQECPyHMFkmld1qKHwPJkyLbLPfboqJPM53ys4xW8v+iBVw== dependencies: vscode-languageserver-textdocument "^1.0.4" - vscode-languageserver-types "^3.16.0" - vscode-nls "^5.0.0" + vscode-languageserver-types "^3.17.1" + vscode-nls "^5.0.1" vscode-uri "^3.0.3" -vscode-html-languageservice@^4.2.5: - version "4.2.5" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-4.2.5.tgz#c0cc8ff3d824d16388bbac187e1828749eccf006" - integrity sha512-dbr10KHabB9EaK8lI0XZW7SqOsTfrNyT3Nuj0GoPi4LjGKUmMiLtsqzfedIzRTzqY+w0FiLdh0/kQrnQ0tLxrw== +vscode-html-languageservice@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-5.0.0.tgz#c68613f836d7fcff125183d78e6f1f0ff326fa55" + integrity sha512-KJG13z54aLszskp3ETf8b1EKDypr2Sf5RUsfR6OXmKqEl2ZUfyIxsWz4gbJWjPzoJZx/bGH0ZXVwxJ1rg8OKRQ== dependencies: vscode-languageserver-textdocument "^1.0.4" - vscode-languageserver-types "^3.16.0" - vscode-nls "^5.0.0" + vscode-languageserver-types "^3.17.1" + vscode-nls "^5.0.1" vscode-uri "^3.0.3" -vscode-jsonrpc@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e" - integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg== +vscode-jsonrpc@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e" + integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ== -vscode-languageserver-protocol@3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821" - integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A== +vscode-languageserver-protocol@3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed" + integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg== dependencies: - vscode-jsonrpc "6.0.0" - vscode-languageserver-types "3.16.0" - -vscode-languageserver-textdocument@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.3.tgz#879f2649bfa5a6e07bc8b392c23ede2dfbf43eff" - integrity sha512-ynEGytvgTb6HVSUwPJIAZgiHQmPCx8bZ8w5um5Lz+q5DjP0Zj8wTFhQpyg8xaMvefDytw2+HH5yzqS+FhsR28A== + vscode-jsonrpc "8.0.1" + vscode-languageserver-types "3.17.1" vscode-languageserver-textdocument@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.4.tgz#3cd56dd14cec1d09e86c4bb04b09a246cb3df157" integrity sha512-/xhqXP/2A2RSs+J8JNXpiiNVvvNM0oTosNVmQnunlKvq9o4mupHOBAnnzH0lwIPKazXKvAKsVp1kr+H/K4lgoQ== -vscode-languageserver-types@3.16.0, vscode-languageserver-types@^3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247" - integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== +vscode-languageserver-types@3.17.1, vscode-languageserver-types@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16" + integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ== -vscode-languageserver@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz#49b068c87cfcca93a356969d20f5d9bdd501c6b0" - integrity sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw== +vscode-languageserver@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.0.1.tgz#56bd7a01f5c88af075a77f1d220edcb30fc4bdc7" + integrity sha512-sn7SjBwWm3OlmLtgg7jbM0wBULppyL60rj8K5HF0ny/MzN+GzPBX1kCvYdybhl7UW63V5V5tRVnyB8iwC73lSQ== dependencies: - vscode-languageserver-protocol "3.16.0" + vscode-languageserver-protocol "3.17.1" -vscode-nls@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" - integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== +vscode-nls@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2" + integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== vscode-uri@^3.0.3: version "3.0.3" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index d77421b701cfa..5157b15d87790 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== -"@vscode/extension-telemetry@0.4.10": - version "0.4.10" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.4.10.tgz#be960c05bdcbea0933866346cf244acad6cac910" - integrity sha512-XgyUoWWRQExTmd9DynIIUQo1NPex/zIeetdUAXeBjVuW9ioojM1TcDaSqOa/5QLC7lx+oEXwSU1r0XSBgzyz6w== +"@vscode/extension-telemetry@0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.5.1.tgz#20150976629663b3d33799a4ad25944a1535f7db" + integrity sha512-cvFq8drxdLRF8KN72WcV4lTEa9GqDiRwy9EbnYuoSCD9Jdk8zHFF49MmACC1qs4R9Ko/C1uMOmeLJmVi8EA0rQ== balanced-match@^1.0.0: version "1.0.0" @@ -44,44 +44,44 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -semver@^7.3.4: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== +semver@^7.3.5: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" -vscode-jsonrpc@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e" - integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg== +vscode-jsonrpc@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e" + integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ== -vscode-languageclient@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz#b505c22c21ffcf96e167799757fca07a6bad0fb2" - integrity sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg== +vscode-languageclient@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.0.1.tgz#bf5535c4463a78daeaca0bcb4f5868aec86bb301" + integrity sha512-9XoE+HJfaWvu7Y75H3VmLo5WLCtsbxEgEhrLPqwt7eyoR49lUIyyrjb98Yfa50JCMqF2cePJAEVI6oe2o1sIhw== dependencies: minimatch "^3.0.4" - semver "^7.3.4" - vscode-languageserver-protocol "3.16.0" + semver "^7.3.5" + vscode-languageserver-protocol "3.17.1" -vscode-languageserver-protocol@3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821" - integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A== +vscode-languageserver-protocol@3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed" + integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg== dependencies: - vscode-jsonrpc "6.0.0" - vscode-languageserver-types "3.16.0" - -vscode-languageserver-types@3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247" - integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== - -vscode-nls@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" - integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== + vscode-jsonrpc "8.0.1" + vscode-languageserver-types "3.17.1" + +vscode-languageserver-types@3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16" + integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ== + +vscode-nls@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2" + integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== vscode-uri@^3.0.3: version "3.0.3" diff --git a/extensions/json-language-features/client/src/browser/jsonClientMain.ts b/extensions/json-language-features/client/src/browser/jsonClientMain.ts index 9eb390b545c6b..e1fae6ffeb463 100644 --- a/extensions/json-language-features/client/src/browser/jsonClientMain.ts +++ b/extensions/json-language-features/client/src/browser/jsonClientMain.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ExtensionContext, Uri } from 'vscode'; -import { LanguageClientOptions } from 'vscode-languageclient'; +import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient'; import { startClient, LanguageClientConstructor, SchemaRequestService } from '../jsonClient'; import { LanguageClient } from 'vscode-languageclient/browser'; @@ -14,8 +14,10 @@ declare const Worker: { declare function fetch(uri: string, options: any): any; +let client: BaseLanguageClient | undefined; + // this method is called when vs code is activated -export function activate(context: ExtensionContext) { +export async function activate(context: ExtensionContext) { const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browser/jsonServerMain.js'); try { const worker = new Worker(serverMain.toString()); @@ -32,9 +34,16 @@ export function activate(context: ExtensionContext) { } }; - startClient(context, newLanguageClient, { schemaRequests }); + client = await startClient(context, newLanguageClient, { schemaRequests }); } catch (e) { console.log(e); } } + +export async function deactivate(): Promise { + if (client) { + await client.stop(); + client = undefined; + } +} diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index 5246257adc8ba..60225af8cef6a 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -16,9 +16,10 @@ import { import { LanguageClientOptions, RequestType, NotificationType, DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams, - DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, CommonLanguageClient + DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, BaseLanguageClient } from 'vscode-languageclient'; + import { hash } from './utils/hash'; import { createLanguageStatusItem } from './languageStatus'; @@ -56,7 +57,7 @@ namespace ResultLimitReachedNotification { export const type: NotificationType = new NotificationType('json/resultLimitReached'); } -interface Settings { +type Settings = { json?: { schemas?: JSONSchemaSettings[]; format?: { enable?: boolean }; @@ -67,13 +68,13 @@ interface Settings { proxy?: string; proxyStrictSSL?: boolean; }; -} +}; -export interface JSONSchemaSettings { +export type JSONSchemaSettings = { fileMatch?: string[]; url?: string; schema?: any; -} +}; namespace SettingIds { export const enableFormatter = 'json.format.enable'; @@ -94,7 +95,7 @@ export interface TelemetryReporter { }): void; } -export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient; +export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient; export interface Runtime { schemaRequests: SchemaRequestService; @@ -108,7 +109,7 @@ export interface SchemaRequestService { export const languageServerDescription = localize('jsonserver.name', 'JSON Language Server'); -export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) { +export async function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise { const toDispose = context.subscriptions; @@ -218,178 +219,177 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua const client = newLanguageClient('json', languageServerDescription, clientOptions); client.registerProposedFeatures(); - const disposable = client.start(); - toDispose.push(disposable); - client.onReady().then(() => { - isClientReady = true; - - const schemaDocuments: { [uri: string]: boolean } = {}; + const schemaDocuments: { [uri: string]: boolean } = {}; - // handle content request - client.onRequest(VSCodeContentRequest.type, (uriPath: string) => { - const uri = Uri.parse(uriPath); - if (uri.scheme === 'untitled') { - return Promise.reject(new ResponseError(3, localize('untitled.schema', 'Unable to load {0}', uri.toString()))); - } - if (uri.scheme !== 'http' && uri.scheme !== 'https') { - return workspace.openTextDocument(uri).then(doc => { - schemaDocuments[uri.toString()] = true; - return doc.getText(); - }, error => { - return Promise.reject(new ResponseError(2, error.toString())); - }); - } else if (schemaDownloadEnabled) { - if (runtime.telemetry && uri.authority === 'schema.management.azure.com') { - /* __GDPR__ - "json.schema" : { - "owner": "aeschli", - "comment": "Measure the use of the Azure resource manager schemas", - "schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The azure schema URL that was requested." } - } - */ - runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath }); - } - return runtime.schemaRequests.getContent(uriPath).catch(e => { - return Promise.reject(new ResponseError(4, e.toString())); - }); - } else { - return Promise.reject(new ResponseError(1, localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload))); + // handle content request + client.onRequest(VSCodeContentRequest.type, (uriPath: string) => { + const uri = Uri.parse(uriPath); + if (uri.scheme === 'untitled') { + return Promise.reject(new ResponseError(3, localize('untitled.schema', 'Unable to load {0}', uri.toString()))); + } + if (uri.scheme !== 'http' && uri.scheme !== 'https') { + return workspace.openTextDocument(uri).then(doc => { + schemaDocuments[uri.toString()] = true; + return doc.getText(); + }, error => { + return Promise.reject(new ResponseError(2, error.toString())); + }); + } else if (schemaDownloadEnabled) { + if (runtime.telemetry && uri.authority === 'schema.management.azure.com') { + /* __GDPR__ + "json.schema" : { + "owner": "aeschli", + "comment": "Measure the use of the Azure resource manager schemas", + "schemaURL" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The azure schema URL that was requested." } + } + */ + runtime.telemetry.sendTelemetryEvent('json.schema', { schemaURL: uriPath }); } - }); + return runtime.schemaRequests.getContent(uriPath).catch(e => { + return Promise.reject(new ResponseError(4, e.toString())); + }); + } else { + return Promise.reject(new ResponseError(1, localize('schemaDownloadDisabled', 'Downloading schemas is disabled through setting \'{0}\'', SettingIds.enableSchemaDownload))); + } + }); - const handleContentChange = (uriString: string) => { - if (schemaDocuments[uriString]) { - client.sendNotification(SchemaContentChangeNotification.type, uriString); - return true; - } - return false; - }; - const handleActiveEditorChange = (activeEditor?: TextEditor) => { - if (!activeEditor) { - return; - } + await client.start(); - const activeDocUri = activeEditor.document.uri.toString(); + isClientReady = true; - if (activeDocUri && fileSchemaErrors.has(activeDocUri)) { - schemaResolutionErrorStatusBarItem.show(); - } else { - schemaResolutionErrorStatusBarItem.hide(); - } - }; + const handleContentChange = (uriString: string) => { + if (schemaDocuments[uriString]) { + client.sendNotification(SchemaContentChangeNotification.type, uriString); + return true; + } + return false; + }; + const handleActiveEditorChange = (activeEditor?: TextEditor) => { + if (!activeEditor) { + return; + } - toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString()))); - toDispose.push(workspace.onDidCloseTextDocument(d => { - const uriString = d.uri.toString(); - if (handleContentChange(uriString)) { - delete schemaDocuments[uriString]; - } - fileSchemaErrors.delete(uriString); - })); - toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange)); - - const handleRetryResolveSchemaCommand = () => { - if (window.activeTextEditor) { - schemaResolutionErrorStatusBarItem.text = '$(watch)'; - const activeDocUri = window.activeTextEditor.document.uri.toString(); - client.sendRequest(ForceValidateRequest.type, activeDocUri).then((diagnostics) => { - const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError); - if (schemaErrorIndex !== -1) { - // Show schema resolution errors in status bar only; ref: #51032 - const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; - fileSchemaErrors.set(activeDocUri, schemaResolveDiagnostic.message); - } else { - schemaResolutionErrorStatusBarItem.hide(); - } - schemaResolutionErrorStatusBarItem.text = '$(alert)'; - }); - } - }; + const activeDocUri = activeEditor.document.uri.toString(); + + if (activeDocUri && fileSchemaErrors.has(activeDocUri)) { + schemaResolutionErrorStatusBarItem.show(); + } else { + schemaResolutionErrorStatusBarItem.hide(); + } + }; - toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand)); + toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString()))); + toDispose.push(workspace.onDidCloseTextDocument(d => { + const uriString = d.uri.toString(); + if (handleContentChange(uriString)) { + delete schemaDocuments[uriString]; + } + fileSchemaErrors.delete(uriString); + })); + toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange)); + const handleRetryResolveSchemaCommand = () => { + if (window.activeTextEditor) { + schemaResolutionErrorStatusBarItem.text = '$(watch)'; + const activeDocUri = window.activeTextEditor.document.uri.toString(); + client.sendRequest(ForceValidateRequest.type, activeDocUri).then((diagnostics) => { + const schemaErrorIndex = diagnostics.findIndex(isSchemaResolveError); + if (schemaErrorIndex !== -1) { + // Show schema resolution errors in status bar only; ref: #51032 + const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; + fileSchemaErrors.set(activeDocUri, schemaResolveDiagnostic.message); + } else { + schemaResolutionErrorStatusBarItem.hide(); + } + schemaResolutionErrorStatusBarItem.text = '$(alert)'; + }); + } + }; + + toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand)); + + client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context)); + + toDispose.push(extensions.onDidChange(_ => { client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context)); + })); - toDispose.push(extensions.onDidChange(_ => { - client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociations(context)); - })); + // manually register / deregister format provider based on the `json.format.enable` setting avoiding issues with late registration. See #71652. + updateFormatterRegistration(); + toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() }); - // manually register / deregister format provider based on the `json.format.enable` setting avoiding issues with late registration. See #71652. - updateFormatterRegistration(); - toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() }); + updateSchemaDownloadSetting(); - updateSchemaDownloadSetting(); + toDispose.push(workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(SettingIds.enableFormatter)) { + updateFormatterRegistration(); + } else if (e.affectsConfiguration(SettingIds.enableSchemaDownload)) { + updateSchemaDownloadSetting(); + } + })); - toDispose.push(workspace.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(SettingIds.enableFormatter)) { - updateFormatterRegistration(); - } else if (e.affectsConfiguration(SettingIds.enableSchemaDownload)) { - updateSchemaDownloadSetting(); + client.onNotification(ResultLimitReachedNotification.type, async message => { + const shouldPrompt = context.globalState.get(StorageIds.maxItemsExceededInformation) !== false; + if (shouldPrompt) { + const ok = localize('ok', "OK"); + const openSettings = localize('goToSetting', 'Open Settings'); + const neverAgain = localize('yes never again', "Don't Show Again"); + const pick = await window.showInformationMessage(`${message}\n${localize('configureLimit', 'Use setting \'{0}\' to configure the limit.', SettingIds.maxItemsComputed)}`, ok, openSettings, neverAgain); + if (pick === neverAgain) { + await context.globalState.update(StorageIds.maxItemsExceededInformation, false); + } else if (pick === openSettings) { + await commands.executeCommand('workbench.action.openSettings', SettingIds.maxItemsComputed); } - })); - - client.onNotification(ResultLimitReachedNotification.type, async message => { - const shouldPrompt = context.globalState.get(StorageIds.maxItemsExceededInformation) !== false; - if (shouldPrompt) { - const ok = localize('ok', "OK"); - const openSettings = localize('goToSetting', 'Open Settings'); - const neverAgain = localize('yes never again', "Don't Show Again"); - const pick = await window.showInformationMessage(`${message}\n${localize('configureLimit', 'Use setting \'{0}\' to configure the limit.', SettingIds.maxItemsComputed)}`, ok, openSettings, neverAgain); - if (pick === neverAgain) { - await context.globalState.update(StorageIds.maxItemsExceededInformation, false); - } else if (pick === openSettings) { - await commands.executeCommand('workbench.action.openSettings', SettingIds.maxItemsComputed); + } + }); + + toDispose.push(createLanguageStatusItem(documentSelector, (uri: string) => client.sendRequest(LanguageStatusRequest.type, uri))); + + function updateFormatterRegistration() { + const formatEnabled = workspace.getConfiguration().get(SettingIds.enableFormatter); + if (!formatEnabled && rangeFormatting) { + rangeFormatting.dispose(); + rangeFormatting = undefined; + } else if (formatEnabled && !rangeFormatting) { + rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, { + provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult { + const filesConfig = workspace.getConfiguration('files', document); + const fileFormattingOptions = { + trimTrailingWhitespace: filesConfig.get('trimTrailingWhitespace'), + trimFinalNewlines: filesConfig.get('trimFinalNewlines'), + insertFinalNewline: filesConfig.get('insertFinalNewline'), + }; + const params: DocumentRangeFormattingParams = { + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), + range: client.code2ProtocolConverter.asRange(range), + options: client.code2ProtocolConverter.asFormattingOptions(options, fileFormattingOptions) + }; + + return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then( + client.protocol2CodeConverter.asTextEdits, + (error) => { + client.handleFailedRequest(DocumentRangeFormattingRequest.type, undefined, error, []); + return Promise.resolve([]); + } + ); } - } - }); - - toDispose.push(createLanguageStatusItem(documentSelector, (uri: string) => client.sendRequest(LanguageStatusRequest.type, uri))); - - function updateFormatterRegistration() { - const formatEnabled = workspace.getConfiguration().get(SettingIds.enableFormatter); - if (!formatEnabled && rangeFormatting) { - rangeFormatting.dispose(); - rangeFormatting = undefined; - } else if (formatEnabled && !rangeFormatting) { - rangeFormatting = languages.registerDocumentRangeFormattingEditProvider(documentSelector, { - provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult { - const filesConfig = workspace.getConfiguration('files', document); - const fileFormattingOptions = { - trimTrailingWhitespace: filesConfig.get('trimTrailingWhitespace'), - trimFinalNewlines: filesConfig.get('trimFinalNewlines'), - insertFinalNewline: filesConfig.get('insertFinalNewline'), - }; - const params: DocumentRangeFormattingParams = { - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), - range: client.code2ProtocolConverter.asRange(range), - options: client.code2ProtocolConverter.asFormattingOptions(options, fileFormattingOptions) - }; - - return client.sendRequest(DocumentRangeFormattingRequest.type, params, token).then( - client.protocol2CodeConverter.asTextEdits, - (error) => { - client.handleFailedRequest(DocumentRangeFormattingRequest.type, error, []); - return Promise.resolve([]); - } - ); - } - }); - } + }); } + } - function updateSchemaDownloadSetting() { - schemaDownloadEnabled = workspace.getConfiguration().get(SettingIds.enableSchemaDownload) !== false; - if (schemaDownloadEnabled) { - schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema. Click to retry.'); - schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema'; - handleRetryResolveSchemaCommand(); - } else { - schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionDisabledMessage', 'Downloading schemas is disabled. Click to configure.'); - schemaResolutionErrorStatusBarItem.command = { command: 'workbench.action.openSettings', arguments: [SettingIds.enableSchemaDownload], title: '' }; - } + function updateSchemaDownloadSetting() { + schemaDownloadEnabled = workspace.getConfiguration().get(SettingIds.enableSchemaDownload) !== false; + if (schemaDownloadEnabled) { + schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema. Click to retry.'); + schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema'; + handleRetryResolveSchemaCommand(); + } else { + schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionDisabledMessage', 'Downloading schemas is disabled. Click to configure.'); + schemaResolutionErrorStatusBarItem.command = { command: 'workbench.action.openSettings', arguments: [SettingIds.enableSchemaDownload], title: '' }; } + } - }); + return client; } function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] { diff --git a/extensions/json-language-features/client/src/node/jsonClientMain.ts b/extensions/json-language-features/client/src/node/jsonClientMain.ts index 9809074a5a931..752abe59a9f62 100644 --- a/extensions/json-language-features/client/src/node/jsonClientMain.ts +++ b/extensions/json-language-features/client/src/node/jsonClientMain.ts @@ -5,7 +5,7 @@ import { ExtensionContext, OutputChannel, window, workspace } from 'vscode'; import { startClient, LanguageClientConstructor, SchemaRequestService, languageServerDescription } from '../jsonClient'; -import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node'; +import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient, BaseLanguageClient } from 'vscode-languageclient/node'; import { promises as fs } from 'fs'; import * as path from 'path'; @@ -15,6 +15,7 @@ import TelemetryReporter from '@vscode/extension-telemetry'; import { JSONSchemaCache } from './schemaCache'; let telemetry: TelemetryReporter | undefined; +let client: BaseLanguageClient | undefined; // this method is called when vs code is activated export async function activate(context: ExtensionContext) { @@ -45,11 +46,15 @@ export async function activate(context: ExtensionContext) { const schemaRequests = await getSchemaRequestService(context, log); - startClient(context, newLanguageClient, { schemaRequests, telemetry }); + client = await startClient(context, newLanguageClient, { schemaRequests, telemetry }); } -export function deactivate(): Promise { - return telemetry ? telemetry.dispose() : Promise.resolve(null); +export async function deactivate(): Promise { + if (client) { + await client.stop(); + client = undefined; + } + telemetry?.dispose(); } interface IPackageInfo { diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 4941fced2652a..1fc6753efbb7a 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -149,8 +149,8 @@ "dependencies": { "@vscode/extension-telemetry": "0.5.1", "request-light": "^0.5.8", - "vscode-languageclient": "^7.0.0", - "vscode-nls": "^5.0.0" + "vscode-languageclient": "^8.0.1", + "vscode-nls": "^5.0.1" }, "devDependencies": { "@types/node": "16.x" diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index cd04f24c3ab73..a60bc177dc128 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -14,8 +14,8 @@ "dependencies": { "jsonc-parser": "^3.0.0", "request-light": "^0.5.8", - "vscode-json-languageservice": "^4.2.1", - "vscode-languageserver": "^7.0.0", + "vscode-json-languageservice": "^5.0.0", + "vscode-languageserver": "^8.0.1", "vscode-uri": "^3.0.3" }, "devDependencies": { diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index e105859371faa..b818bcefbf280 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -6,11 +6,11 @@ import { Connection, TextDocuments, InitializeParams, InitializeResult, NotificationType, RequestType, - DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions + DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions, Diagnostic } from 'vscode-languageserver'; import { formatError, runSafe, runSafeAsync } from './utils/runner'; -import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Diagnostic, Range, Position } from 'vscode-json-languageservice'; +import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Range, Position } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; import { Utils, URI } from 'vscode-uri'; @@ -117,7 +117,9 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) // in the passed params the rootPath of the workspace plus the client capabilities. connection.onInitialize((params: InitializeParams): InitializeResult => { - const handledProtocols = params.initializationOptions?.handledSchemaProtocols; + const initializationOptions = params.initializationOptions as any || {}; + + const handledProtocols = initializationOptions?.handledSchemaProtocols; languageService = getLanguageService({ schemaRequestService: getSchemaRequestService(handledProtocols), @@ -138,11 +140,13 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) return c; } + + clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false); - dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions?.provideFormatter !== 'boolean'); + dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof initializationOptions.provideFormatter !== 'boolean'); foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false); - formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; + formatterMaxNumberOfEdits = initializationOptions.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; const capabilities: ServerCapabilities = { textDocumentSync: TextDocumentSyncKind.Incremental, completionProvider: clientSnippetSupport ? { @@ -151,8 +155,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } : undefined, hoverProvider: true, documentSymbolProvider: true, - documentRangeFormattingProvider: params.initializationOptions?.provideFormatter === true, - documentFormattingProvider: params.initializationOptions?.provideFormatter === true, + documentRangeFormattingProvider: initializationOptions.provideFormatter === true, + documentFormattingProvider: initializationOptions.provideFormatter === true, colorProvider: {}, foldingRangeProvider: true, selectionRangeProvider: true, @@ -403,7 +407,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) runtime.timer.setImmediate(() => { const currDocument = documents.get(textDocument.uri); if (currDocument && currDocument.version === version) { - respond(diagnostics); // Send the computed diagnostics to VSCode. + respond(diagnostics as Diagnostic[]); // Send the computed diagnostics to VSCode. } }); }, error => { diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 5cd1cf5f48057..2e74a77e9d3b2 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -22,51 +22,51 @@ request-light@^0.5.8: resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.8.tgz#8bf73a07242b9e7b601fac2fa5dc22a094abcc27" integrity sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg== -vscode-json-languageservice@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz#94b6f471ece193bf4a1ef37f6ab5cce86d50a8b4" - integrity sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA== +vscode-json-languageservice@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-5.0.0.tgz#465d76cfe5dfeed4c3d5a2123b50e3f115bb7f78" + integrity sha512-1/+1TJBRFrfCNizmrW0fbIvguKzzO+4ehlqWCCnF7ioSACUGHrYop4ANb+eRnFaCP6fi3+i+llJC5Y5yAvmL6w== dependencies: jsonc-parser "^3.0.0" - vscode-languageserver-textdocument "^1.0.3" - vscode-languageserver-types "^3.16.0" - vscode-nls "^5.0.0" + vscode-languageserver-textdocument "^1.0.4" + vscode-languageserver-types "^3.17.1" + vscode-nls "^5.0.1" vscode-uri "^3.0.3" -vscode-jsonrpc@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e" - integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg== +vscode-jsonrpc@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e" + integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ== -vscode-languageserver-protocol@3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821" - integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A== +vscode-languageserver-protocol@3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed" + integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg== dependencies: - vscode-jsonrpc "6.0.0" - vscode-languageserver-types "3.16.0" + vscode-jsonrpc "8.0.1" + vscode-languageserver-types "3.17.1" -vscode-languageserver-textdocument@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.3.tgz#879f2649bfa5a6e07bc8b392c23ede2dfbf43eff" - integrity sha512-ynEGytvgTb6HVSUwPJIAZgiHQmPCx8bZ8w5um5Lz+q5DjP0Zj8wTFhQpyg8xaMvefDytw2+HH5yzqS+FhsR28A== +vscode-languageserver-textdocument@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.4.tgz#3cd56dd14cec1d09e86c4bb04b09a246cb3df157" + integrity sha512-/xhqXP/2A2RSs+J8JNXpiiNVvvNM0oTosNVmQnunlKvq9o4mupHOBAnnzH0lwIPKazXKvAKsVp1kr+H/K4lgoQ== -vscode-languageserver-types@3.16.0, vscode-languageserver-types@^3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247" - integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== +vscode-languageserver-types@3.17.1, vscode-languageserver-types@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16" + integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ== -vscode-languageserver@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz#49b068c87cfcca93a356969d20f5d9bdd501c6b0" - integrity sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw== +vscode-languageserver@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.0.1.tgz#56bd7a01f5c88af075a77f1d220edcb30fc4bdc7" + integrity sha512-sn7SjBwWm3OlmLtgg7jbM0wBULppyL60rj8K5HF0ny/MzN+GzPBX1kCvYdybhl7UW63V5V5tRVnyB8iwC73lSQ== dependencies: - vscode-languageserver-protocol "3.16.0" + vscode-languageserver-protocol "3.17.1" -vscode-nls@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" - integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== +vscode-nls@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2" + integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== vscode-uri@^3.0.3: version "3.0.3" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 7e56398f06241..3b6ad62624a1a 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -49,44 +49,44 @@ request-light@^0.5.8: resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.8.tgz#8bf73a07242b9e7b601fac2fa5dc22a094abcc27" integrity sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg== -semver@^7.3.4: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== +semver@^7.3.5: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" -vscode-jsonrpc@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e" - integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg== +vscode-jsonrpc@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e" + integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ== -vscode-languageclient@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz#b505c22c21ffcf96e167799757fca07a6bad0fb2" - integrity sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg== +vscode-languageclient@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.0.1.tgz#bf5535c4463a78daeaca0bcb4f5868aec86bb301" + integrity sha512-9XoE+HJfaWvu7Y75H3VmLo5WLCtsbxEgEhrLPqwt7eyoR49lUIyyrjb98Yfa50JCMqF2cePJAEVI6oe2o1sIhw== dependencies: minimatch "^3.0.4" - semver "^7.3.4" - vscode-languageserver-protocol "3.16.0" + semver "^7.3.5" + vscode-languageserver-protocol "3.17.1" -vscode-languageserver-protocol@3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821" - integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A== +vscode-languageserver-protocol@3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed" + integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg== dependencies: - vscode-jsonrpc "6.0.0" - vscode-languageserver-types "3.16.0" - -vscode-languageserver-types@3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247" - integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== - -vscode-nls@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" - integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== + vscode-jsonrpc "8.0.1" + vscode-languageserver-types "3.17.1" + +vscode-languageserver-types@3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16" + integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ== + +vscode-nls@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.1.tgz#ba23fc4d4420d25e7f886c8e83cbdcec47aa48b2" + integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== yallist@^4.0.0: version "4.0.0" diff --git a/extensions/vscode-colorize-tests/test/colorize-fixtures/test.html b/extensions/vscode-colorize-tests/test/colorize-fixtures/test.html index 13fd84fbd0679..be1437797f8b8 100644 --- a/extensions/vscode-colorize-tests/test/colorize-fixtures/test.html +++ b/extensions/vscode-colorize-tests/test/colorize-fixtures/test.html @@ -39,4 +39,4 @@ You signed out in another tab or window. Reload to refresh your session. - \ No newline at end of file + From 32e6d2c113f750cf0ed591b8c38a02c5a82a80a7 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 19 May 2022 12:54:28 +0200 Subject: [PATCH 162/942] extract logic for title suffix and prefix computations and use it for title menu --- .../parts/titlebar/titleMenuControl.ts | 14 ++++++- .../browser/parts/titlebar/windowTitle.ts | 41 ++++++++++++------- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts b/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts index a68616a47d8ef..77e1ebb32eb04 100644 --- a/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts @@ -95,7 +95,19 @@ export class TitleMenuControl { } private _updateFromWindowTitle() { - this.workspaceTitle.innerText = windowTitle.workspaceName; + + // label: just workspace name and optional decorations + const { prefix, suffix } = windowTitle.getTitleDecorations(); + let label = windowTitle.workspaceName; + if (prefix) { + label = localize('label1', "{0} {1}", prefix, label); + } + if (suffix) { + label = localize('label2', "{0} {1}", label, suffix); + } + this.workspaceTitle.innerText = label; + + // tooltip: full windowTitle const kb = keybindingService.lookupKeybinding(action.id)?.getLabel(); const title = kb ? localize('title', "Search {0} ({1}) \u2014 {2}", windowTitle.workspaceName, kb, windowTitle.value) diff --git a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts index cdae94ad27746..1920790f52070 100644 --- a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts +++ b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts @@ -108,28 +108,41 @@ export class WindowTitle extends Disposable { } private getWindowTitle(): string { - let title = this.doGetWindowTitle(); + let title = this.doGetWindowTitle() || this.productService.nameLong; + let { prefix, suffix } = this.getTitleDecorations(); + if (prefix) { + title = `${prefix} ${title}`; + } + if (suffix) { + title = `${title} ${suffix}`; + } + // Replace non-space whitespace + title = title.replace(/[^\S ]/g, ' '); + return title; + } + + getTitleDecorations() { + let prefix: string | undefined; + let suffix: string | undefined; if (this.properties.prefix) { - title = `${this.properties.prefix} ${title || this.productService.nameLong}`; + prefix = this.properties.prefix; + } + if (this.environmentService.isExtensionDevelopment) { + prefix = !prefix + ? WindowTitle.NLS_EXTENSION_HOST + : `${WindowTitle.NLS_EXTENSION_HOST} - ${prefix}`; } if (this.properties.isAdmin) { - title = `${title || this.productService.nameLong} ${WindowTitle.NLS_USER_IS_ADMIN}`; + suffix = WindowTitle.NLS_USER_IS_ADMIN; } - if (!this.properties.isPure) { - title = `${title || this.productService.nameLong} ${WindowTitle.NLS_UNSUPPORTED}`; + suffix = !suffix + ? WindowTitle.NLS_UNSUPPORTED + : `${suffix} ${WindowTitle.NLS_UNSUPPORTED}`; } - - if (this.environmentService.isExtensionDevelopment) { - title = `${WindowTitle.NLS_EXTENSION_HOST} - ${title || this.productService.nameLong}`; - } - - // Replace non-space whitespace - title = title.replace(/[^\S ]/g, ' '); - - return title; + return { prefix, suffix }; } updateProperties(properties: ITitleProperties): void { From 04e34f25549ca272802d8532a5616b48ef262b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Thu, 19 May 2022 13:56:04 +0200 Subject: [PATCH 163/942] dispose native policy service on shutdown --- src/vs/code/electron-main/main.ts | 10 ++++++---- src/vs/code/node/cliProcessMain.ts | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index cb41f40555e0e..cfa944e76c8f3 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -65,6 +65,7 @@ import { IThemeMainService, ThemeMainService } from 'vs/platform/theme/electron- import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; import { NativePolicyService } from 'vs/platform/policy/node/nativePolicyService'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; +import { DisposableStore } from 'vs/base/common/lifecycle'; /** * The main VS Code entry point. @@ -142,6 +143,8 @@ class CodeMain { private createServices(): [IInstantiationService, IProcessEnvironment, IEnvironmentMainService, IPolicyService, ConfigurationService, StateMainService, BufferLogService, IProductService] { const services = new ServiceCollection(); + const disposables = new DisposableStore(); + process.once('exit', () => disposables.dispose()); // Product const productService = { _serviceBrand: undefined, ...product }; @@ -156,8 +159,7 @@ class CodeMain { // we are the only instance running, otherwise we'll have concurrent // log file access on Windows (https://github.com/microsoft/vscode/issues/41218) const bufferLogService = new BufferLogService(); - const logService = new MultiplexLogService([new ConsoleMainLogger(getLogLevel(environmentMainService)), bufferLogService]); - process.once('exit', () => logService.dispose()); + const logService = disposables.add(new MultiplexLogService([new ConsoleMainLogger(getLogLevel(environmentMainService)), bufferLogService])); services.set(ILogService, logService); // Files @@ -170,8 +172,8 @@ class CodeMain { services.set(ILoggerService, new LoggerService(logService, fileService)); // Policy - const policyService = isWindows && productService.win32RegValueName ? new NativePolicyService(productService.win32RegValueName) - : environmentMainService.policyFile ? new FilePolicyService(environmentMainService.policyFile, fileService, logService) + const policyService = isWindows && productService.win32RegValueName ? disposables.add(new NativePolicyService(productService.win32RegValueName)) + : environmentMainService.policyFile ? disposables.add(new FilePolicyService(environmentMainService.policyFile, fileService, logService)) : new NullPolicyService(); services.set(IPolicyService, policyService); diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index bc630b7cae821..6c197b1766148 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -133,8 +133,8 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, diskFileSystemProvider); // Policy - const policyService = isWindows && productService.win32RegValueName ? new NativePolicyService(productService.win32RegValueName) - : environmentService.policyFile ? new FilePolicyService(environmentService.policyFile, fileService, logService) + const policyService = isWindows && productService.win32RegValueName ? this._register(new NativePolicyService(productService.win32RegValueName)) + : environmentService.policyFile ? this._register(new FilePolicyService(environmentService.policyFile, fileService, logService)) : new NullPolicyService(); services.set(IPolicyService, policyService); From a63bebc2967e04ea6cb13fb4721977cff77bb685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Thu, 19 May 2022 16:02:56 +0200 Subject: [PATCH 164/942] build: :arrow_up: bump deemon (#149938) fixes deemon issue https://github.com/joaomoreno/deemon/issues/6 --- yarn.lock | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index 21813ce973708..9aab56681f15b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2563,16 +2563,7 @@ bl@^1.0.0: readable-stream "^2.3.5" safe-buffer "^5.1.1" -bl@^4.0.2: - version "4.0.3" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" - integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - -bl@^4.0.3: +bl@^4.0.2, bl@^4.0.3: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== @@ -3957,9 +3948,9 @@ decompress@^4.2.1: strip-dirs "^2.0.0" deemon@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/deemon/-/deemon-1.4.0.tgz#01c09cc23eec41e5d7ddac082eb52c3611d38dff" - integrity sha512-S0zK5tNTdVFsJZVUeKi/CYJn4zzhW0Y55lwXzv2hVxb7ajzAHf91BhE5y2xvx1X7czIZ6PHLPDj00TVAmylVXw== + version "1.7.1" + resolved "https://registry.yarnpkg.com/deemon/-/deemon-1.7.1.tgz#46cf8313fd320fac6ee944bbb1bea3e8c2a978ee" + integrity sha512-UcBiu6+4sZgkDrs7GT78FDkqmt9ZVN412XXT4Bm9sn5Hcrwv/M9HmCay3108Yvxl4Ti5n1UjkkuSVA1y3+FIvg== dependencies: bl "^4.0.2" tree-kill "^1.2.2" From 0db3cee2a42d8c180ff429edacf82de3b7dcedba Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 19 May 2022 11:20:05 +0200 Subject: [PATCH 165/942] let -> const --- .../folding/test/browser/foldingModel.test.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/contrib/folding/test/browser/foldingModel.test.ts b/src/vs/editor/contrib/folding/test/browser/foldingModel.test.ts index 6b54319a21a78..9db0f7f572d4e 100644 --- a/src/vs/editor/contrib/folding/test/browser/foldingModel.test.ts +++ b/src/vs/editor/contrib/folding/test/browser/foldingModel.test.ts @@ -347,20 +347,20 @@ suite('Folding Model', () => { let textModel = createTextModel(lines.join('\n')); try { - let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); + const foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); - let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ }); + const ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ }); foldingModel.update(ranges); - let r1 = r(1, 2, false); - let r2 = r(3, 11, false); - let r3 = r(4, 10, false); - let r4 = r(5, 6, false); - let r5 = r(8, 9, false); + const r1 = r(1, 2, false); + const r2 = r(3, 11, false); + const r3 = r(4, 10, false); + const r4 = r(5, 6, false); + const r5 = r(8, 9, false); - let region1 = foldingModel.getRegionAtLine(r1.startLineNumber); - let region2 = foldingModel.getRegionAtLine(r2.startLineNumber); - let region3 = foldingModel.getRegionAtLine(r3.startLineNumber); + const region1 = foldingModel.getRegionAtLine(r1.startLineNumber); + const region2 = foldingModel.getRegionAtLine(r2.startLineNumber); + const region3 = foldingModel.getRegionAtLine(r3.startLineNumber); assertRanges(foldingModel, [r1, r2, r3, r4, r5]); From a3243b282c0a882ad45064f4555ee5a7fb985d2d Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Thu, 19 May 2022 08:12:29 -0700 Subject: [PATCH 166/942] Add indicator, fixes #148672 (#149294) * Add indicator, fixes #148672 This PR adds a default override indicator to extension-contributed language-specific default setting overrides. * Remove indicator from language tag settings --- .../preferences/browser/settingsTreeModels.ts | 9 ++++++++- .../preferences/common/preferencesModels.ts | 14 ++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index 6c4169e6b7c87..e41f72985542c 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -18,8 +18,9 @@ import { FOLDER_SCOPES, WORKSPACE_SCOPES, REMOTE_MACHINE_SCOPES, LOCAL_MACHINE_S import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import { ConfigurationScope, EditPresentationTypes } from 'vs/platform/configuration/common/configurationRegistry'; +import { ConfigurationScope, EditPresentationTypes, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ILanguageService } from 'vs/editor/common/languages/language'; +import { Registry } from 'vs/platform/registry/common/platform'; export const ONLINE_SERVICES_SETTING_TAG = 'usesOnlineServices'; @@ -221,6 +222,12 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { this.value = displayValue; this.scopeValue = isConfigured && overrideValues[targetSelector]; this.defaultValue = overrideValues.defaultValue ?? inspected.defaultValue; + + const registryValues = Registry.as(Extensions.Configuration).getConfigurationDefaultsOverrides(); + const overrideValueSource = registryValues.get(`[${languageSelector}]`)?.valuesSources?.get(this.setting.key); + if (overrideValueSource) { + this.setting.defaultValueSource = overrideValueSource; + } } else { this.value = displayValue; this.scopeValue = isConfigured && inspected[targetSelector]; diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 000a74b77ca9a..d93923deb81f8 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -707,17 +707,19 @@ export class DefaultSettings extends Disposable { }); } - const registeredConfigurationProp = prop as IRegisteredConfigurationPropertySchema; - let defaultValueSource: string | IExtensionInfo | undefined; - if (registeredConfigurationProp && registeredConfigurationProp.defaultValueSource) { - defaultValueSource = registeredConfigurationProp.defaultValueSource; - } - let isLanguageTagSetting = false; if (OVERRIDE_PROPERTY_REGEX.test(key)) { isLanguageTagSetting = true; } + let defaultValueSource: string | IExtensionInfo | undefined; + if (!isLanguageTagSetting) { + const registeredConfigurationProp = prop as IRegisteredConfigurationPropertySchema; + if (registeredConfigurationProp && registeredConfigurationProp.defaultValueSource) { + defaultValueSource = registeredConfigurationProp.defaultValueSource; + } + } + result.push({ key, value, From 6e36be6f339e9b60a63ff34f7dd79075397f2c25 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Thu, 19 May 2022 10:49:29 -0700 Subject: [PATCH 167/942] Align menubar hover background with rest of titlebar items (#149956) --- src/vs/workbench/common/theme.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index bf7c818fb946a..7258b951affa7 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken, focusBorder, activeContrastBorder, editorWidgetForeground, editorErrorForeground, editorWarningForeground, editorInfoForeground, treeIndentGuidesStroke, errorForeground, listActiveSelectionBackground, listActiveSelectionForeground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor, editorBackground, contrastBorder, transparent, editorWidgetBackground, textLinkForeground, lighten, darken, focusBorder, activeContrastBorder, editorWidgetForeground, editorErrorForeground, editorWarningForeground, editorInfoForeground, treeIndentGuidesStroke, errorForeground, listActiveSelectionBackground, listActiveSelectionForeground, editorForeground, toolbarHoverBackground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; import { ColorScheme } from 'vs/platform/theme/common/theme'; @@ -763,8 +763,8 @@ export const MENUBAR_SELECTION_FOREGROUND = registerColor('menubar.selectionFore }, localize('menubarSelectionForeground', "Foreground color of the selected menu item in the menubar.")); export const MENUBAR_SELECTION_BACKGROUND = registerColor('menubar.selectionBackground', { - dark: transparent(Color.white, 0.1), - light: transparent(Color.black, 0.1), + dark: toolbarHoverBackground, + light: toolbarHoverBackground, hcDark: null, hcLight: null, }, localize('menubarSelectionBackground', "Background color of the selected menu item in the menubar.")); From 3f27a6103b6c61c7a552140e92e19de54cd03780 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 19 May 2022 11:01:45 -0700 Subject: [PATCH 168/942] Pick up latest TS nightly for building VS Code (#149959) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index c9215abb13af9..a8ffe4891ff4f 100644 --- a/package.json +++ b/package.json @@ -199,7 +199,7 @@ "style-loader": "^1.0.0", "ts-loader": "^9.2.7", "tsec": "0.1.4", - "typescript": "^4.8.0-dev.20220511", + "typescript": "^4.8.0-dev.20220518", "typescript-formatter": "7.1.0", "underscore": "^1.12.1", "util": "^0.12.4", diff --git a/yarn.lock b/yarn.lock index 9aab56681f15b..382b89816cd81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11385,10 +11385,10 @@ typescript@^2.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= -typescript@^4.8.0-dev.20220511: - version "4.8.0-dev.20220511" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.0-dev.20220511.tgz#42dd5ad99dcd345277c784949cc36a7b5d43fa18" - integrity sha512-MDo0tI/TRHCJ1sxochoU4LhT41C6jhEIkWneehqsxNYV84+d+C4HvD7Nq4DQrI03cIGvPJbdW2s3ZladROXE+A== +typescript@^4.8.0-dev.20220518: + version "4.8.0-dev.20220518" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.0-dev.20220518.tgz#3082c89c764daece904480552b9f2c3f5ce79c56" + integrity sha512-yczRLiowXD4THxpe2DrClYXsmIRt9VPDft1dat4Le50mQwuUcmvdqD43o4hmlbNP7HpyeEYX51KXozGq1s7zlw== typical@^4.0.0: version "4.0.0" From 85bf8af5b9661b0c7f9587d33b3cb61499f6e4b8 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Thu, 19 May 2022 11:20:48 -0700 Subject: [PATCH 169/942] Improve Counter-Zoom Handling for Context Views (#149958) * fixes #149957 * fixes #149741 * use const * use const --- src/vs/base/browser/dom.ts | 18 ++++++++++++++++++ .../base/browser/ui/contextview/contextview.ts | 13 +++++++++---- .../electron-sandbox/contextmenuService.ts | 10 +--------- .../services/hover/browser/hoverWidget.ts | 14 +++++++++++++- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index d30acf1fff9ee..d2fa1bd3ba83d 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -577,6 +577,24 @@ export function getDomNodePagePosition(domNode: HTMLElement): IDomNodePagePositi }; } +/** + * Returns the effective zoom on a given element before window zoom level is applied + */ +export function getDomNodeZoomLevel(domNode: HTMLElement): number { + let testElement: HTMLElement | null = domNode; + let zoom = 1.0; + do { + const elementZoomLevel = (getComputedStyle(testElement) as any).zoom; + if (elementZoomLevel !== null && elementZoomLevel !== undefined && elementZoomLevel !== '1') { + zoom *= elementZoomLevel; + } + + testElement = testElement.parentElement; + } while (testElement !== null && testElement !== document.documentElement); + + return zoom; +} + export interface IStandardWindow { readonly scrollX: number; readonly scrollY: number; diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index c34f711ae9f0a..1aa24bde2723d 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -262,11 +262,16 @@ export class ContextView extends Disposable { if (DOM.isHTMLElement(anchor)) { let elementPosition = DOM.getDomNodePagePosition(anchor); + // In areas where zoom is applied to the element or its ancestors, we need to adjust the size of the element + // e.g. The title bar has counter zoom behavior meaning it applies the inverse of zoom level. + // Window Zoom Level: 1.5, Title Bar Zoom: 1/1.5, Size Multiplier: 1.5 + const zoom = DOM.getDomNodeZoomLevel(anchor); + around = { - top: elementPosition.top, - left: elementPosition.left, - width: elementPosition.width, - height: elementPosition.height + top: elementPosition.top * zoom, + left: elementPosition.left * zoom, + width: elementPosition.width * zoom, + height: elementPosition.height * zoom }; } else { around = { diff --git a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts index b491ce7079e13..b4c0e11cbebb7 100644 --- a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts @@ -105,15 +105,7 @@ class NativeContextMenuService extends Disposable implements IContextMenuService // In areas where zoom is applied to the element or its ancestors, we need to adjust accordingly // e.g. The title bar has counter zoom behavior meaning it applies the inverse of zoom level. // Window Zoom Level: 1.5, Title Bar Zoom: 1/1.5, Coordinate Multiplier: 1.5 * 1.0 / 1.5 = 1.0 - let testElement: HTMLElement | null = anchor; - do { - const elementZoomLevel = (dom.getComputedStyle(testElement) as any).zoom; - if (elementZoomLevel !== null && elementZoomLevel !== undefined && elementZoomLevel !== '1') { - zoom *= elementZoomLevel; - } - - testElement = testElement.parentElement; - } while (testElement !== null && testElement !== document.documentElement); + zoom *= dom.getDomNodeZoomLevel(anchor); x = elementPosition.left; y = elementPosition.top + elementPosition.height; diff --git a/src/vs/workbench/services/hover/browser/hoverWidget.ts b/src/vs/workbench/services/hover/browser/hoverWidget.ts index 837a4bdfd52f0..24f4173e3f87b 100644 --- a/src/vs/workbench/services/hover/browser/hoverWidget.ts +++ b/src/vs/workbench/services/hover/browser/hoverWidget.ts @@ -230,7 +230,19 @@ export class HoverWidget extends Widget { this._hover.containerDomNode.classList.remove('right-aligned'); this._hover.contentsDomNode.style.maxHeight = ''; - const targetBounds = this._target.targetElements.map(e => e.getBoundingClientRect()); + const getZoomAccountedBoundingClientRect = (e: HTMLElement) => { + const zoom = dom.getDomNodeZoomLevel(e); + + const boundingRect = e.getBoundingClientRect(); + return { + top: boundingRect.top * zoom, + bottom: boundingRect.bottom * zoom, + right: boundingRect.right * zoom, + left: boundingRect.left * zoom, + }; + }; + + const targetBounds = this._target.targetElements.map(e => getZoomAccountedBoundingClientRect(e)); const top = Math.min(...targetBounds.map(e => e.top)); const right = Math.max(...targetBounds.map(e => e.right)); const bottom = Math.max(...targetBounds.map(e => e.bottom)); From a00e1380401cb64023272c189772563f2885e8ab Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 19 May 2022 11:25:45 -0700 Subject: [PATCH 170/942] Changing dependency syntax for markdown-it-katex (#149962) Fixes #149291 --- extensions/markdown-math/package.json | 2 +- extensions/markdown-math/yarn.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/markdown-math/package.json b/extensions/markdown-math/package.json index 8267989daee4b..d3bb21a553a97 100644 --- a/extensions/markdown-math/package.json +++ b/extensions/markdown-math/package.json @@ -90,7 +90,7 @@ "build-notebook": "node ./esbuild" }, "dependencies": { - "@iktakahiro/markdown-it-katex": "https://github.com/mjbvz/markdown-it-katex.git" + "@iktakahiro/markdown-it-katex": "mjbvz/markdown-it-katex" }, "devDependencies": { "@types/markdown-it": "^0.0.0", diff --git a/extensions/markdown-math/yarn.lock b/extensions/markdown-math/yarn.lock index 645b008070777..202cceb35b96e 100644 --- a/extensions/markdown-math/yarn.lock +++ b/extensions/markdown-math/yarn.lock @@ -2,9 +2,9 @@ # yarn lockfile v1 -"@iktakahiro/markdown-it-katex@https://github.com/mjbvz/markdown-it-katex.git": +"@iktakahiro/markdown-it-katex@mjbvz/markdown-it-katex": version "4.0.1" - resolved "https://github.com/mjbvz/markdown-it-katex.git#2e3736e4b916ee64ed92ebfabeaa94643612665a" + resolved "https://codeload.github.com/mjbvz/markdown-it-katex/tar.gz/2e3736e4b916ee64ed92ebfabeaa94643612665a" dependencies: katex "^0.13.0" From 2c9c8e6770df84d39c837f875818763c5f4cf49f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 19 May 2022 11:43:09 -0700 Subject: [PATCH 171/942] Fix lazy button layout and cursor (#149963) Fix #149875 --- .../contrib/debug/browser/media/debug.contribution.css | 3 +-- src/vs/workbench/contrib/debug/browser/media/repl.css | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css index c061b736c55b0..36390d28f6ab0 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css @@ -71,8 +71,7 @@ /* Expressions */ -.monaco-workbench .debug-pane .monaco-list-row .expression, -.monaco-workbench .debug-hover-widget .monaco-list-row .expression { +.monaco-workbench .monaco-list-row .expression { display: flex; } diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index d28e388e4743c..38c3431d3b305 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -23,6 +23,10 @@ line-height: var(--vscode-repl-line-height); } +.monaco-workbench .repl .repl-tree .monaco-tl-contents .expression .lazy-button { + cursor: pointer; +} + .monaco-workbench .repl .repl-tree .monaco-tl-twistie { background-position-y: calc(100% - (var(--vscode-repl-font-size-for-twistie))); } From 92b6c1be5462c276f82bb39244b9e155fc7a653a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 19 May 2022 11:50:28 -0700 Subject: [PATCH 172/942] Allow cancelling webview's readFileStream (#149964) This passes a cancellation token to `readFileStream`. This ensures we stop reading the file if the webview is disposed of --- src/vs/workbench/contrib/webview/browser/resourceLoading.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/resourceLoading.ts b/src/vs/workbench/contrib/webview/browser/resourceLoading.ts index 9d717dd0c1c83..cdff87cccd0eb 100644 --- a/src/vs/workbench/contrib/webview/browser/resourceLoading.ts +++ b/src/vs/workbench/contrib/webview/browser/resourceLoading.ts @@ -65,7 +65,7 @@ export async function loadLocalResource( const mime = getWebviewContentMimeType(requestUri); // Use the original path for the mime try { - const result = await fileService.readFileStream(resourceToLoad, { etag: options.ifNoneMatch }); + const result = await fileService.readFileStream(resourceToLoad, { etag: options.ifNoneMatch }, token); return new WebviewResourceResponse.StreamSuccess(result.value, result.etag, result.mtime, mime); } catch (err) { if (err instanceof FileOperationError) { From 55e6d2f138112bd1fb5321e9ca7e19839000af57 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 19 May 2022 22:54:28 +0200 Subject: [PATCH 173/942] move user data resources to user data profile --- .../sharedProcess/sharedProcessMain.ts | 7 +- src/vs/code/electron-main/main.ts | 7 +- src/vs/code/node/cliProcessMain.ts | 7 +- .../environment/common/environment.ts | 3 - .../environment/common/environmentService.ts | 9 -- .../test/browser/fileUserDataProvider.test.ts | 53 +++---- .../userDataProfile/common/userDataProfile.ts | 59 ++++++++ .../common/abstractSynchronizer.ts | 2 + .../userDataSync/common/extensionsSync.ts | 4 +- .../userDataSync/common/globalStateSync.ts | 4 +- .../userDataSync/common/keybindingsSync.ts | 11 +- .../userDataSync/common/settingsSync.ts | 11 +- .../userDataSync/common/snippetsSync.ts | 11 +- .../platform/userDataSync/common/tasksSync.ts | 11 +- .../test/common/keybindingsSync.test.ts | 24 +-- .../test/common/settingsSync.test.ts | 14 +- .../test/common/snippetsSync.test.ts | 15 +- .../test/common/tasksSync.test.ts | 66 +++------ .../common/userDataAutoSyncService.test.ts | 8 +- .../test/common/userDataSyncClient.ts | 22 +-- .../test/common/userDataSyncService.test.ts | 57 +++---- .../common/userDataSyncStoreService.test.ts | 4 +- .../node/remoteExtensionHostAgentCli.ts | 7 +- src/vs/workbench/browser/web.main.ts | 13 +- .../electron-sandbox/remoteExtensionsInit.ts | 4 +- .../browser/keybindingsEditorContribution.ts | 12 +- .../browser/preferences.contribution.ts | 6 +- .../common/preferencesContribution.ts | 6 +- .../snippets/browser/configureSnippets.ts | 12 +- .../snippets/browser/snippetsService.ts | 4 +- .../browser/telemetry.contribution.ts | 10 +- .../userDataSync/browser/userDataSync.ts | 4 +- .../browser/userDataSyncTrigger.ts | 8 +- .../electron-sandbox/desktop.main.ts | 11 +- .../browser/configurationService.ts | 4 +- .../common/configurationEditingService.ts | 8 +- .../configurationEditingService.test.ts | 23 +-- .../test/browser/configurationService.test.ts | 139 +++++++++--------- .../environment/browser/environmentService.ts | 9 -- .../keybinding/browser/keybindingService.ts | 6 +- .../keybinding/common/keybindingEditing.ts | 6 +- .../test/browser/keybindingEditing.test.ts | 16 +- .../preferences/browser/preferencesService.ts | 8 +- .../profiles/common/settingsProfile.ts | 12 +- .../services/userData/browser/userDataInit.ts | 15 +- 45 files changed, 427 insertions(+), 325 deletions(-) create mode 100644 src/vs/platform/userDataProfile/common/userDataProfile.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 4dd6d42da9b96..3bd1e64a7a0cc 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -100,6 +100,7 @@ import { InspectProfilingService as V8InspectProfilingService } from 'vs/platfor import { IV8InspectProfilingService } from 'vs/platform/profiling/common/profiling'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; class SharedProcessMain extends Disposable { @@ -221,8 +222,12 @@ class SharedProcessMain extends Disposable { )); fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider); + // User Data Profiles + const userDataProfilesService = this._register(new UserDataProfilesService(environmentService, logService)); + services.set(IUserDataProfilesService, userDataProfilesService); + // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService)); services.set(IConfigurationService, configurationService); // Storage (global access only) diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 762f288dabaa2..be023e5f03a25 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -62,6 +62,7 @@ import { IStateMainService } from 'vs/platform/state/electron-main/state'; import { StateMainService } from 'vs/platform/state/electron-main/stateMainService'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IThemeMainService, ThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; /** * The main VS Code entry point. @@ -166,8 +167,12 @@ class CodeMain { // Logger services.set(ILoggerService, new LoggerService(logService, fileService)); + // User Data Profiles + const userDataProfilesService = new UserDataProfilesService(environmentMainService, logService); + services.set(IUserDataProfilesService, userDataProfilesService); + // Configuration - const configurationService = new ConfigurationService(environmentMainService.settingsResource, fileService); + const configurationService = new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService); services.set(IConfigurationService, configurationService); // Lifecycle diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 9495713716e3a..f69778b5f8500 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -51,6 +51,7 @@ import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppen import { buildTelemetryMessage } from 'vs/platform/telemetry/node/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; class CliMain extends Disposable { @@ -128,8 +129,12 @@ class CliMain extends Disposable { const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService)); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + // User Data Profiles + const userDataProfilesService = new UserDataProfilesService(environmentService, logService); + services.set(IUserDataProfilesService, userDataProfilesService); + // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService)); services.set(IConfigurationService, configurationService); // Init config diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 56531a3a7845f..52a16c3fa3f9e 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -49,11 +49,8 @@ export interface IEnvironmentService { // --- user roaming data userRoamingDataHome: URI; - settingsResource: URI; - keybindingsResource: URI; keyboardLayoutResource: URI; argvResource: URI; - snippetsHome: URI; // --- data paths untitledWorkspacesHome: URI; diff --git a/src/vs/platform/environment/common/environmentService.ts b/src/vs/platform/environment/common/environmentService.ts index fcea3f58023aa..2edc3238cdda5 100644 --- a/src/vs/platform/environment/common/environmentService.ts +++ b/src/vs/platform/environment/common/environmentService.ts @@ -62,9 +62,6 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron @memoize get userRoamingDataHome(): URI { return this.appSettingsHome; } - @memoize - get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); } - @memoize get userDataSyncHome(): URI { return joinPath(this.userRoamingDataHome, 'sync'); } @@ -95,9 +92,6 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron @memoize get localHistoryHome(): URI { return joinPath(this.appSettingsHome, 'History'); } - @memoize - get keybindingsResource(): URI { return joinPath(this.userRoamingDataHome, 'keybindings.json'); } - @memoize get keyboardLayoutResource(): URI { return joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); } @@ -111,9 +105,6 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron return joinPath(this.userHome, this.productService.dataFolderName, 'argv.json'); } - @memoize - get snippetsHome(): URI { return joinPath(this.userRoamingDataHome, 'snippets'); } - @memoize get isExtensionDevelopment(): boolean { return !!this.args.extensionDevelopmentPath; } diff --git a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts index 8e2852d182d05..6317d43b979bd 100644 --- a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts +++ b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts @@ -18,6 +18,7 @@ import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFil import { AbstractNativeEnvironmentService } from 'vs/platform/environment/common/environmentService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import product from 'vs/platform/product/common/product'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' }); @@ -34,6 +35,7 @@ suite('FileUserDataProvider', () => { let userDataHomeOnDisk: URI; let backupWorkspaceHomeOnDisk: URI; let environmentService: IEnvironmentService; + let userDataProfilesService: IUserDataProfilesService; const disposables = new DisposableStore(); let fileUserDataProvider: FileUserDataProvider; @@ -50,6 +52,7 @@ suite('FileUserDataProvider', () => { await testObject.createFolder(backupWorkspaceHomeOnDisk); environmentService = new TestEnvironmentService(userDataHomeOnDisk); + userDataProfilesService = new UserDataProfilesService(environmentService, logService); fileUserDataProvider = new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService); disposables.add(fileUserDataProvider); @@ -59,25 +62,25 @@ suite('FileUserDataProvider', () => { teardown(() => disposables.clear()); test('exists return false when file does not exist', async () => { - const exists = await testObject.exists(environmentService.settingsResource); + const exists = await testObject.exists(userDataProfilesService.defaultProfile.settingsResource); assert.strictEqual(exists, false); }); test('read file throws error if not exist', async () => { try { - await testObject.readFile(environmentService.settingsResource); + await testObject.readFile(userDataProfilesService.defaultProfile.settingsResource); assert.fail('Should fail since file does not exist'); } catch (e) { } }); test('read existing file', async () => { await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('{}')); - const result = await testObject.readFile(environmentService.settingsResource); + const result = await testObject.readFile(userDataProfilesService.defaultProfile.settingsResource); assert.strictEqual(result.value.toString(), '{}'); }); test('create file', async () => { - const resource = environmentService.settingsResource; + const resource = userDataProfilesService.defaultProfile.settingsResource; const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'settings.json')); @@ -85,7 +88,7 @@ suite('FileUserDataProvider', () => { }); test('write file creates the file if not exist', async () => { - const resource = environmentService.settingsResource; + const resource = userDataProfilesService.defaultProfile.settingsResource; const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'settings.json')); @@ -93,7 +96,7 @@ suite('FileUserDataProvider', () => { }); test('write to existing file', async () => { - const resource = environmentService.settingsResource; + const resource = userDataProfilesService.defaultProfile.settingsResource; await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('{}')); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); @@ -103,33 +106,33 @@ suite('FileUserDataProvider', () => { test('delete file', async () => { await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('')); - await testObject.del(environmentService.settingsResource); + await testObject.del(userDataProfilesService.defaultProfile.settingsResource); const result = await testObject.exists(joinPath(userDataHomeOnDisk, 'settings.json')); assert.strictEqual(false, result); }); test('resolve file', async () => { await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('')); - const result = await testObject.resolve(environmentService.settingsResource); + const result = await testObject.resolve(userDataProfilesService.defaultProfile.settingsResource); assert.ok(!result.isDirectory); assert.ok(result.children === undefined); }); test('exists return false for folder that does not exist', async () => { - const exists = await testObject.exists(environmentService.snippetsHome); + const exists = await testObject.exists(userDataProfilesService.defaultProfile.snippetsHome); assert.strictEqual(exists, false); }); test('exists return true for folder that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); - const exists = await testObject.exists(environmentService.snippetsHome); + const exists = await testObject.exists(userDataProfilesService.defaultProfile.snippetsHome); assert.strictEqual(exists, true); }); test('read file throws error for folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); try { - await testObject.readFile(environmentService.snippetsHome); + await testObject.readFile(userDataProfilesService.defaultProfile.snippetsHome); assert.fail('Should fail since read file is not supported for folders'); } catch (e) { } }); @@ -137,7 +140,7 @@ suite('FileUserDataProvider', () => { test('read file under folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - const resource = joinPath(environmentService.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual = await testObject.readFile(resource); assert.strictEqual(actual.resource.toString(), resource.toString()); assert.strictEqual(actual.value.toString(), '{}'); @@ -146,7 +149,7 @@ suite('FileUserDataProvider', () => { test('read file under sub folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets', 'java')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'java', 'settings.json'), VSBuffer.fromString('{}')); - const resource = joinPath(environmentService.snippetsHome, 'java/settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'java/settings.json'); const actual = await testObject.readFile(resource); assert.strictEqual(actual.resource.toString(), resource.toString()); assert.strictEqual(actual.value.toString(), '{}'); @@ -154,7 +157,7 @@ suite('FileUserDataProvider', () => { test('create file under folder that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); - const resource = joinPath(environmentService.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -162,7 +165,7 @@ suite('FileUserDataProvider', () => { }); test('create file under folder that does not exist', async () => { - const resource = joinPath(environmentService.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -171,7 +174,7 @@ suite('FileUserDataProvider', () => { test('write to not existing file under container that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); - const resource = joinPath(environmentService.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -179,7 +182,7 @@ suite('FileUserDataProvider', () => { }); test('write to not existing file under container that does not exists', async () => { - const resource = joinPath(environmentService.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -189,7 +192,7 @@ suite('FileUserDataProvider', () => { test('write to existing file under container', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - const resource = joinPath(environmentService.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -197,7 +200,7 @@ suite('FileUserDataProvider', () => { }); test('write file under sub container', async () => { - const resource = joinPath(environmentService.snippetsHome, 'java/settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'java/settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'java', 'settings.json')); @@ -206,7 +209,7 @@ suite('FileUserDataProvider', () => { test('delete throws error for folder that does not exist', async () => { try { - await testObject.del(environmentService.snippetsHome); + await testObject.del(userDataProfilesService.defaultProfile.snippetsHome); assert.fail('Should fail the folder does not exist'); } catch (e) { } }); @@ -214,14 +217,14 @@ suite('FileUserDataProvider', () => { test('delete not existing file under container that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); try { - await testObject.del(joinPath(environmentService.snippetsHome, 'settings.json')); + await testObject.del(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json')); assert.fail('Should fail since file does not exist'); } catch (e) { } }); test('delete not existing file under container that does not exists', async () => { try { - await testObject.del(joinPath(environmentService.snippetsHome, 'settings.json')); + await testObject.del(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json')); assert.fail('Should fail since file does not exist'); } catch (e) { } }); @@ -229,7 +232,7 @@ suite('FileUserDataProvider', () => { test('delete existing file under folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - await testObject.del(joinPath(environmentService.snippetsHome, 'settings.json')); + await testObject.del(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json')); const exists = await testObject.exists(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); assert.strictEqual(exists, false); }); @@ -237,11 +240,11 @@ suite('FileUserDataProvider', () => { test('resolve folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - const result = await testObject.resolve(environmentService.snippetsHome); + const result = await testObject.resolve(userDataProfilesService.defaultProfile.snippetsHome); assert.ok(result.isDirectory); assert.ok(result.children !== undefined); assert.strictEqual(result.children!.length, 1); - assert.strictEqual(result.children![0].resource.toString(), joinPath(environmentService.snippetsHome, 'settings.json').toString()); + assert.strictEqual(result.children![0].resource.toString(), joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json').toString()); }); test('read backup file', async () => { diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts new file mode 100644 index 0000000000000..721fcdabcb12a --- /dev/null +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { joinPath } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; + +export interface IUserDataProfile { + readonly location: URI; + readonly settingsResource: URI; + readonly keybindingsResource: URI; + readonly tasksResource: URI; + readonly snippetsHome: URI; + readonly extensionsResource: URI; +} + +export const IUserDataProfilesService = createDecorator('IUserDataProfilesService'); +export interface IUserDataProfilesService { + readonly _serviceBrand: undefined; + + readonly defaultProfile: IUserDataProfile; + + readonly onDidChangeCurrentProfile: Event; + readonly currentProfile: IUserDataProfile; +} + +export class UserDataProfilesService extends Disposable implements IUserDataProfilesService { + readonly _serviceBrand: undefined; + + private _currentProfile: IUserDataProfile; + get currentProfile(): IUserDataProfile { return this._currentProfile; } + + readonly defaultProfile: IUserDataProfile; + + private readonly _onDidChangeCurrentProfile = this._register(new Emitter()); + readonly onDidChangeCurrentProfile = this._onDidChangeCurrentProfile.event; + + constructor( + @IEnvironmentService environmentService: IEnvironmentService, + @ILogService logService: ILogService + ) { + super(); + const defaultProfileLocation = environmentService.userRoamingDataHome; + this._currentProfile = this.defaultProfile = { + location: defaultProfileLocation, + settingsResource: joinPath(defaultProfileLocation, 'settings.json'), + keybindingsResource: joinPath(defaultProfileLocation, 'keybindings.json'), + tasksResource: joinPath(defaultProfileLocation, 'tasks.json'), + snippetsHome: joinPath(defaultProfileLocation, 'snippets'), + extensionsResource: joinPath(defaultProfileLocation, 'extensions.json') + }; + } +} diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index 932841567c669..65022680f928a 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -27,6 +27,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { Change, getLastSyncResourceUri, IRemoteUserData, IResourcePreview as IBaseResourcePreview, ISyncData, ISyncResourceHandle, ISyncResourcePreview as IBaseSyncResourcePreview, IUserData, IUserDataInitializer, IUserDataManifest, IUserDataSyncBackupStoreService, IUserDataSyncConfiguration, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, IUserDataSyncUtilService, MergeState, PREVIEW_DIR_NAME, SyncResource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_CONFIGURATION_SCOPE, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; type SyncSourceClassification = { source?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; @@ -820,6 +821,7 @@ export abstract class AbstractInitializer implements IUserDataInitializer { constructor( readonly resource: SyncResource, + @IUserDataProfilesService protected readonly userDataProfilesService: IUserDataProfilesService, @IEnvironmentService protected readonly environmentService: IEnvironmentService, @ILogService protected readonly logService: ILogService, @IFileService protected readonly fileService: IFileService, diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 2aae1a5028b9e..f9c91926700bd 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -22,6 +22,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { AbstractInitializer, AbstractSynchroniser, IAcceptResult, IMergeResult, IResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { IMergeResult as IExtensionMergeResult, merge } from 'vs/platform/userDataSync/common/extensionsMerge'; import { IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; @@ -507,11 +508,12 @@ export abstract class AbstractExtensionsInitializer extends AbstractInitializer @IExtensionManagementService protected readonly extensionManagementService: IExtensionManagementService, @IIgnoredExtensionsManagementService private readonly ignoredExtensionsManagementService: IIgnoredExtensionsManagementService, @IFileService fileService: IFileService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IEnvironmentService environmentService: IEnvironmentService, @ILogService logService: ILogService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(SyncResource.Extensions, environmentService, logService, fileService, uriIdentityService); + super(SyncResource.Extensions, userDataProfilesService, environmentService, logService, fileService, uriIdentityService); } protected async parseExtensions(remoteUserData: IRemoteUserData): Promise { diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index e44eda9ed5e03..fd67bd8447579 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -27,6 +27,7 @@ import { edit } from 'vs/platform/userDataSync/common/content'; import { merge } from 'vs/platform/userDataSync/common/globalStateMerge'; import { ALL_SYNC_RESOURCES, Change, createSyncHeaders, getEnablementKey, IGlobalState, IRemoteUserData, IStorageValue, ISyncData, ISyncResourceHandle, IUserData, IUserDataSyncBackupStoreService, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, SyncResource, SYNC_SERVICE_URL_TYPE, UserDataSyncError, UserDataSyncErrorCode, UserDataSyncStoreType, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; const argvStoragePrefx = 'globalState.argv.'; const argvProperties: string[] = ['locale']; @@ -387,11 +388,12 @@ export class GlobalStateInitializer extends AbstractInitializer { constructor( @IStorageService private readonly storageService: IStorageService, @IFileService fileService: IFileService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IEnvironmentService environmentService: IEnvironmentService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(SyncResource.GlobalState, environmentService, logService, fileService, uriIdentityService); + super(SyncResource.GlobalState, userDataProfilesService, environmentService, logService, fileService, uriIdentityService); } async doInitialize(remoteUserData: IRemoteUserData): Promise { diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 29839dbcfb258..ded980131b52c 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -19,6 +19,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { AbstractInitializer, AbstractJsonFileSynchroniser, IAcceptResult, IFileResourcePreview, IMergeResult } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge'; import { Change, IRemoteUserData, ISyncResourceHandle, IUserDataSyncBackupStoreService, IUserDataSyncConfiguration, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, IUserDataSyncUtilService, SyncResource, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync'; @@ -74,13 +75,14 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem @IConfigurationService configurationService: IConfigurationService, @IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService, @IFileService fileService: IFileService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IEnvironmentService environmentService: IEnvironmentService, @IStorageService storageService: IStorageService, @IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService, @ITelemetryService telemetryService: ITelemetryService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(environmentService.keybindingsResource, SyncResource.Keybindings, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService, uriIdentityService); + super(userDataProfilesService.defaultProfile.keybindingsResource, SyncResource.Keybindings, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService, uriIdentityService); this._register(Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('settingsSync.keybindingsPerPlatform'))(() => this.triggerLocalChange())); } @@ -348,11 +350,12 @@ export class KeybindingsInitializer extends AbstractInitializer { constructor( @IFileService fileService: IFileService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IEnvironmentService environmentService: IEnvironmentService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(SyncResource.Keybindings, environmentService, logService, fileService, uriIdentityService); + super(SyncResource.Keybindings, userDataProfilesService, environmentService, logService, fileService, uriIdentityService); } async doInitialize(remoteUserData: IRemoteUserData): Promise { @@ -368,14 +371,14 @@ export class KeybindingsInitializer extends AbstractInitializer { return; } - await this.fileService.writeFile(this.environmentService.keybindingsResource, VSBuffer.fromString(keybindingsContent)); + await this.fileService.writeFile(this.userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString(keybindingsContent)); await this.updateLastSyncUserData(remoteUserData); } private async isEmpty(): Promise { try { - const fileContent = await this.fileService.readFile(this.environmentService.settingsResource); + const fileContent = await this.fileService.readFile(this.userDataProfilesService.defaultProfile.settingsResource); const keybindings = parse(fileContent.value.toString()); return !isNonEmptyArray(keybindings); } catch (error) { diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index a7c77e99f5a9f..fff3782c3a422 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -16,6 +16,7 @@ import { FileOperationError, FileOperationResult, IFileService } from 'vs/platfo import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { AbstractInitializer, AbstractJsonFileSynchroniser, IAcceptResult, IFileResourcePreview, IMergeResult } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { edit } from 'vs/platform/userDataSync/common/content'; import { getIgnoredSettings, isEmpty, merge, updateIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; @@ -51,6 +52,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement constructor( @IFileService fileService: IFileService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IEnvironmentService environmentService: IEnvironmentService, @IStorageService storageService: IStorageService, @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @@ -63,7 +65,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(environmentService.settingsResource, SyncResource.Settings, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService, uriIdentityService); + super(userDataProfilesService.defaultProfile.settingsResource, SyncResource.Settings, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService, uriIdentityService); } async getRemoteUserDataSyncConfiguration(manifest: IUserDataManifest | null): Promise { @@ -351,11 +353,12 @@ export class SettingsInitializer extends AbstractInitializer { constructor( @IFileService fileService: IFileService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IEnvironmentService environmentService: IEnvironmentService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(SyncResource.Settings, environmentService, logService, fileService, uriIdentityService); + super(SyncResource.Settings, userDataProfilesService, environmentService, logService, fileService, uriIdentityService); } async doInitialize(remoteUserData: IRemoteUserData): Promise { @@ -371,14 +374,14 @@ export class SettingsInitializer extends AbstractInitializer { return; } - await this.fileService.writeFile(this.environmentService.settingsResource, VSBuffer.fromString(settingsSyncContent.settings)); + await this.fileService.writeFile(this.userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(settingsSyncContent.settings)); await this.updateLastSyncUserData(remoteUserData); } private async isEmpty(): Promise { try { - const fileContent = await this.fileService.readFile(this.environmentService.settingsResource); + const fileContent = await this.fileService.readFile(this.userDataProfilesService.defaultProfile.settingsResource); return isEmpty(fileContent.value.toString().trim()); } catch (error) { return (error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND; diff --git a/src/vs/platform/userDataSync/common/snippetsSync.ts b/src/vs/platform/userDataSync/common/snippetsSync.ts index b0b2bb48d8b24..608f7e0ce9c81 100644 --- a/src/vs/platform/userDataSync/common/snippetsSync.ts +++ b/src/vs/platform/userDataSync/common/snippetsSync.ts @@ -15,6 +15,7 @@ import { FileOperationError, FileOperationResult, IFileContent, IFileService, IF import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { AbstractInitializer, AbstractSynchroniser, IAcceptResult, IFileResourcePreview, IMergeResult } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { areSame, IMergeResult as ISnippetsMergeResult, merge } from 'vs/platform/userDataSync/common/snippetsMerge'; import { Change, IRemoteUserData, ISyncData, ISyncResourceHandle, IUserDataSyncBackupStoreService, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, SyncResource, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync'; @@ -35,6 +36,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD constructor( @IEnvironmentService environmentService: IEnvironmentService, @IFileService fileService: IFileService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IStorageService storageService: IStorageService, @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService, @@ -45,7 +47,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD @IUriIdentityService uriIdentityService: IUriIdentityService, ) { super(SyncResource.Snippets, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService, uriIdentityService); - this.snippetsFolder = environmentService.snippetsHome; + this.snippetsFolder = userDataProfilesService.defaultProfile.snippetsHome; this._register(this.fileService.watch(environmentService.userRoamingDataHome)); this._register(this.fileService.watch(this.snippetsFolder)); this._register(Event.filter(this.fileService.onDidFilesChange, e => e.affects(this.snippetsFolder))(() => this.triggerLocalChange())); @@ -510,11 +512,12 @@ export class SnippetsInitializer extends AbstractInitializer { constructor( @IFileService fileService: IFileService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IEnvironmentService environmentService: IEnvironmentService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(SyncResource.Snippets, environmentService, logService, fileService, uriIdentityService); + super(SyncResource.Snippets, userDataProfilesService, environmentService, logService, fileService, uriIdentityService); } async doInitialize(remoteUserData: IRemoteUserData): Promise { @@ -533,7 +536,7 @@ export class SnippetsInitializer extends AbstractInitializer { for (const key of Object.keys(remoteSnippets)) { const content = remoteSnippets[key]; if (content) { - const resource = this.extUri.joinPath(this.environmentService.snippetsHome, key); + const resource = this.extUri.joinPath(this.userDataProfilesService.defaultProfile.snippetsHome, key); await this.fileService.createFile(resource, VSBuffer.fromString(content)); this.logService.info('Created snippet', this.extUri.basename(resource)); } @@ -544,7 +547,7 @@ export class SnippetsInitializer extends AbstractInitializer { private async isEmpty(): Promise { try { - const stat = await this.fileService.resolve(this.environmentService.snippetsHome); + const stat = await this.fileService.resolve(this.userDataProfilesService.defaultProfile.snippetsHome); return !stat.children?.length; } catch (error) { return (error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND; diff --git a/src/vs/platform/userDataSync/common/tasksSync.ts b/src/vs/platform/userDataSync/common/tasksSync.ts index b5680e31cce11..bd8855d1fbf91 100644 --- a/src/vs/platform/userDataSync/common/tasksSync.ts +++ b/src/vs/platform/userDataSync/common/tasksSync.ts @@ -13,6 +13,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { AbstractFileSynchroniser, AbstractInitializer, IAcceptResult, IFileResourcePreview, IMergeResult } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { Change, IRemoteUserData, ISyncResourceHandle, IUserDataSyncBackupStoreService, IUserDataSyncConfiguration, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, SyncResource, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync'; @@ -53,8 +54,9 @@ export class TasksSynchroniser extends AbstractFileSynchroniser implements IUser @IStorageService storageService: IStorageService, @ITelemetryService telemetryService: ITelemetryService, @IUriIdentityService uriIdentityService: IUriIdentityService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, ) { - super(uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(environmentService.settingsResource), 'tasks.json'), SyncResource.Tasks, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService, uriIdentityService); + super(userDataProfilesService.defaultProfile.tasksResource, SyncResource.Tasks, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService, uriIdentityService); } protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, isRemoteDataFromCurrentMachine: boolean, userDataSyncConfiguration: IUserDataSyncConfiguration): Promise { @@ -249,15 +251,16 @@ export class TasksSynchroniser extends AbstractFileSynchroniser implements IUser export class TasksInitializer extends AbstractInitializer { - private tasksResource = this.uriIdentityService.extUri.joinPath(this.uriIdentityService.extUri.dirname(this.environmentService.settingsResource), 'tasks.json'); + private tasksResource = this.userDataProfilesService.defaultProfile.tasksResource; constructor( @IFileService fileService: IFileService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IEnvironmentService environmentService: IEnvironmentService, @IUserDataSyncLogService logService: IUserDataSyncLogService, - @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(SyncResource.Tasks, environmentService, logService, fileService, uriIdentityService); + super(SyncResource.Tasks, userDataProfilesService, environmentService, logService, fileService, uriIdentityService); } async doInitialize(remoteUserData: IRemoteUserData): Promise { diff --git a/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts index 710cd03784641..2392da8e1cabd 100644 --- a/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts @@ -6,9 +6,9 @@ import * as assert from 'assert'; import { VSBuffer } from 'vs/base/common/buffer'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { getKeybindingsContentFromSyncContent, KeybindingsSynchroniser } from 'vs/platform/userDataSync/common/keybindingsSync'; import { IUserDataSyncStoreService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; @@ -32,7 +32,7 @@ suite('KeybindingsSync', () => { test('when keybindings file does not exist', async () => { const fileService = client.instantiationService.get(IFileService); - const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + const keybindingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.keybindingsResource; assert.deepStrictEqual(await testObject.getLastSyncUserData(), null); let manifest = await client.manifest(); @@ -63,7 +63,7 @@ suite('KeybindingsSync', () => { test('when keybindings file is empty and remote has no changes', async () => { const fileService = client.instantiationService.get(IFileService); - const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + const keybindingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.keybindingsResource; await fileService.writeFile(keybindingsResource, VSBuffer.fromString('')); await testObject.sync(await client.manifest()); @@ -84,11 +84,11 @@ suite('KeybindingsSync', () => { 'command': 'workbench.action.closeAllEditors', } ]); - await client2.instantiationService.get(IFileService).writeFile(client2.instantiationService.get(IEnvironmentService).keybindingsResource, VSBuffer.fromString(content)); + await client2.instantiationService.get(IFileService).writeFile(client2.instantiationService.get(IUserDataProfilesService).defaultProfile.keybindingsResource, VSBuffer.fromString(content)); await client2.sync(); const fileService = client.instantiationService.get(IFileService); - const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + const keybindingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.keybindingsResource; await fileService.writeFile(keybindingsResource, VSBuffer.fromString('')); await testObject.sync(await client.manifest()); @@ -102,7 +102,7 @@ suite('KeybindingsSync', () => { test('when keybindings file is empty with comment and remote has no changes', async () => { const fileService = client.instantiationService.get(IFileService); - const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + const keybindingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.keybindingsResource; const expectedContent = '// Empty Keybindings'; await fileService.writeFile(keybindingsResource, VSBuffer.fromString(expectedContent)); @@ -124,11 +124,11 @@ suite('KeybindingsSync', () => { 'command': 'workbench.action.closeAllEditors', } ]); - await client2.instantiationService.get(IFileService).writeFile(client2.instantiationService.get(IEnvironmentService).keybindingsResource, VSBuffer.fromString(content)); + await client2.instantiationService.get(IFileService).writeFile(client2.instantiationService.get(IUserDataProfilesService).defaultProfile.keybindingsResource, VSBuffer.fromString(content)); await client2.sync(); const fileService = client.instantiationService.get(IFileService); - const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + const keybindingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.keybindingsResource; await fileService.writeFile(keybindingsResource, VSBuffer.fromString('// Empty Keybindings')); await testObject.sync(await client.manifest()); @@ -147,11 +147,11 @@ suite('KeybindingsSync', () => { `// Place your key bindings in this file to override the defaults [ ]`; - await client2.instantiationService.get(IFileService).writeFile(client2.instantiationService.get(IEnvironmentService).keybindingsResource, VSBuffer.fromString(content)); + await client2.instantiationService.get(IFileService).writeFile(client2.instantiationService.get(IUserDataProfilesService).defaultProfile.keybindingsResource, VSBuffer.fromString(content)); await client2.sync(); const fileService = client.instantiationService.get(IFileService); - const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + const keybindingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.keybindingsResource; const expectedLocalContent = '// Empty Keybindings'; await fileService.writeFile(keybindingsResource, VSBuffer.fromString(expectedLocalContent)); @@ -166,7 +166,7 @@ suite('KeybindingsSync', () => { test('when keybindings file is created after first sync', async () => { const fileService = client.instantiationService.get(IFileService); - const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + const keybindingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.keybindingsResource; await testObject.sync(await client.manifest()); await fileService.createFile(keybindingsResource, VSBuffer.fromString('[]')); @@ -188,7 +188,7 @@ suite('KeybindingsSync', () => { test('test apply remote when keybindings file does not exist', async () => { const fileService = client.instantiationService.get(IFileService); - const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + const keybindingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.keybindingsResource; if (await fileService.exists(keybindingsResource)) { await fileService.del(keybindingsResource); } diff --git a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts index 6c2bfbea3051b..0f0b5240fb96c 100644 --- a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts @@ -9,9 +9,9 @@ import { Event } from 'vs/base/common/event'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { Registry } from 'vs/platform/registry/common/platform'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISettingsSyncContent, parseSettingsSyncContent, SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; import { ISyncData, IUserDataSyncStoreService, SyncResource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; @@ -49,7 +49,7 @@ suite('SettingsSync - Auto', () => { test('when settings file does not exist', async () => { const fileService = client.instantiationService.get(IFileService); - const settingResource = client.instantiationService.get(IEnvironmentService).settingsResource; + const settingResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource; assert.deepStrictEqual(await testObject.getLastSyncUserData(), null); let manifest = await client.manifest(); @@ -80,7 +80,7 @@ suite('SettingsSync - Auto', () => { test('when settings file is empty and remote has no changes', async () => { const fileService = client.instantiationService.get(IFileService); - const settingsResource = client.instantiationService.get(IEnvironmentService).settingsResource; + const settingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource; await fileService.writeFile(settingsResource, VSBuffer.fromString('')); await testObject.sync(await client.manifest()); @@ -117,11 +117,11 @@ suite('SettingsSync - Auto', () => { // Experimental "workbench.view.experimental.allowMovingToNewContainer": true, }`; - await client2.instantiationService.get(IFileService).writeFile(client2.instantiationService.get(IEnvironmentService).settingsResource, VSBuffer.fromString(content)); + await client2.instantiationService.get(IFileService).writeFile(client2.instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource, VSBuffer.fromString(content)); await client2.sync(); const fileService = client.instantiationService.get(IFileService); - const settingsResource = client.instantiationService.get(IEnvironmentService).settingsResource; + const settingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource; await fileService.writeFile(settingsResource, VSBuffer.fromString('')); await testObject.sync(await client.manifest()); @@ -136,7 +136,7 @@ suite('SettingsSync - Auto', () => { test('when settings file is created after first sync', async () => { const fileService = client.instantiationService.get(IFileService); - const settingsResource = client.instantiationService.get(IEnvironmentService).settingsResource; + const settingsResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource; await testObject.sync(await client.manifest()); await fileService.createFile(settingsResource, VSBuffer.fromString('{}')); @@ -568,6 +568,6 @@ function parseSettings(content: string): string { } async function updateSettings(content: string, client: UserDataSyncClient): Promise { - await client.instantiationService.get(IFileService).writeFile(client.instantiationService.get(IEnvironmentService).settingsResource, VSBuffer.fromString(content)); + await client.instantiationService.get(IFileService).writeFile(client.instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource, VSBuffer.fromString(content)); await client.instantiationService.get(IConfigurationService).reloadConfiguration(); } diff --git a/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts b/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts index 7f9bcaecff76d..a34b37a7f587b 100644 --- a/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts @@ -11,6 +11,7 @@ import { dirname, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { SnippetsSynchroniser } from 'vs/platform/userDataSync/common/snippetsSync'; import { IResourcePreview, ISyncData, IUserDataSyncStoreService, PREVIEW_DIR_NAME, SyncResource, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; @@ -168,7 +169,7 @@ suite('SnippetsSync', () => { test('when snippets does not exist', async () => { const fileService = testClient.instantiationService.get(IFileService); - const snippetsResource = testClient.instantiationService.get(IEnvironmentService).snippetsHome; + const snippetsResource = testClient.instantiationService.get(IUserDataProfilesService).defaultProfile.snippetsHome; assert.deepStrictEqual(await testObject.getLastSyncUserData(), null); let manifest = await testClient.manifest(); @@ -967,22 +968,22 @@ suite('SnippetsSync', () => { async function updateSnippet(name: string, content: string, client: UserDataSyncClient): Promise { const fileService = client.instantiationService.get(IFileService); - const environmentService = client.instantiationService.get(IEnvironmentService); - const snippetsResource = joinPath(environmentService.snippetsHome, name); + const userDataProfilesService = client.instantiationService.get(IUserDataProfilesService); + const snippetsResource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, name); await fileService.writeFile(snippetsResource, VSBuffer.fromString(content)); } async function removeSnippet(name: string, client: UserDataSyncClient): Promise { const fileService = client.instantiationService.get(IFileService); - const environmentService = client.instantiationService.get(IEnvironmentService); - const snippetsResource = joinPath(environmentService.snippetsHome, name); + const userDataProfilesService = client.instantiationService.get(IUserDataProfilesService); + const snippetsResource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, name); await fileService.del(snippetsResource); } async function readSnippet(name: string, client: UserDataSyncClient): Promise { const fileService = client.instantiationService.get(IFileService); - const environmentService = client.instantiationService.get(IEnvironmentService); - const snippetsResource = joinPath(environmentService.snippetsHome, name); + const userDataProfilesService = client.instantiationService.get(IUserDataProfilesService); + const snippetsResource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, name); if (await fileService.exists(snippetsResource)) { const content = await fileService.readFile(snippetsResource); return content.value.toString(); diff --git a/src/vs/platform/userDataSync/test/common/tasksSync.test.ts b/src/vs/platform/userDataSync/test/common/tasksSync.test.ts index f02606d0a5d61..bbec81b672652 100644 --- a/src/vs/platform/userDataSync/test/common/tasksSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/tasksSync.test.ts @@ -6,10 +6,9 @@ import * as assert from 'assert'; import { VSBuffer } from 'vs/base/common/buffer'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; -import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { getTasksContentFromSyncContent, TasksSynchroniser } from 'vs/platform/userDataSync/common/tasksSync'; import { Change, IUserDataSyncStoreService, MergeState, SyncResource, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; @@ -33,8 +32,7 @@ suite('TasksSync', () => { test('when tasks file does not exist', async () => { const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; assert.deepStrictEqual(await testObject.getLastSyncUserData(), null); let manifest = await client.manifest(); @@ -74,14 +72,12 @@ suite('TasksSync', () => { 'label': 'Watch' }] }); - const uriIdentityService2 = client2.instantiationService.get(IUriIdentityService); - const tasksResource2 = uriIdentityService2.extUri.joinPath(uriIdentityService2.extUri.dirname(client2.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource2 = client2.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; await client2.instantiationService.get(IFileService).writeFile(tasksResource2, VSBuffer.fromString(content)); await client2.sync(); const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; await testObject.sync(await client.manifest()); @@ -95,8 +91,7 @@ suite('TasksSync', () => { test('when tasks file exists locally and remote has no tasks', async () => { const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; const content = JSON.stringify({ 'version': '2.0.0', 'tasks': [{ @@ -127,14 +122,12 @@ suite('TasksSync', () => { 'label': 'Watch' }] }); - const uriIdentityService2 = client2.instantiationService.get(IUriIdentityService); - const tasksResource2 = uriIdentityService2.extUri.joinPath(uriIdentityService2.extUri.dirname(client2.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource2 = client2.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; await client2.instantiationService.get(IFileService).writeFile(tasksResource2, VSBuffer.fromString(content)); await client2.sync(); const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; await fileService.writeFile(tasksResource, VSBuffer.fromString(content)); await testObject.sync(await client.manifest()); @@ -149,8 +142,7 @@ suite('TasksSync', () => { test('when tasks file locally has moved forward', async () => { const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; fileService.writeFile(tasksResource, VSBuffer.fromString(JSON.stringify({ 'version': '2.0.0', 'tasks': [] @@ -180,8 +172,7 @@ suite('TasksSync', () => { test('when tasks file remotely has moved forward', async () => { const client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); - const uriIdentityService2 = client2.instantiationService.get(IUriIdentityService); - const tasksResource2 = uriIdentityService2.extUri.joinPath(uriIdentityService2.extUri.dirname(client2.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource2 = client2.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; const fileService2 = client2.instantiationService.get(IFileService); await fileService2.writeFile(tasksResource2, VSBuffer.fromString(JSON.stringify({ 'version': '2.0.0', @@ -189,8 +180,7 @@ suite('TasksSync', () => { }))); const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; await client2.sync(); await testObject.sync(await client.manifest()); @@ -219,8 +209,7 @@ suite('TasksSync', () => { test('when tasks file has moved forward locally and remotely with same changes', async () => { const client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); - const uriIdentityService2 = client2.instantiationService.get(IUriIdentityService); - const tasksResource2 = uriIdentityService2.extUri.joinPath(uriIdentityService2.extUri.dirname(client2.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource2 = client2.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; const fileService2 = client2.instantiationService.get(IFileService); await fileService2.writeFile(tasksResource2, VSBuffer.fromString(JSON.stringify({ 'version': '2.0.0', @@ -228,8 +217,7 @@ suite('TasksSync', () => { }))); const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; await client2.sync(); await testObject.sync(await client.manifest()); @@ -259,8 +247,7 @@ suite('TasksSync', () => { test('when tasks file has moved forward locally and remotely - accept preview', async () => { const client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); - const uriIdentityService2 = client2.instantiationService.get(IUriIdentityService); - const tasksResource2 = uriIdentityService2.extUri.joinPath(uriIdentityService2.extUri.dirname(client2.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource2 = client2.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; const fileService2 = client2.instantiationService.get(IFileService); await fileService2.writeFile(tasksResource2, VSBuffer.fromString(JSON.stringify({ 'version': '2.0.0', @@ -268,8 +255,7 @@ suite('TasksSync', () => { }))); const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; await client2.sync(); await testObject.sync(await client.manifest()); @@ -314,8 +300,7 @@ suite('TasksSync', () => { test('when tasks file has moved forward locally and remotely - accept modified preview', async () => { const client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); - const uriIdentityService2 = client2.instantiationService.get(IUriIdentityService); - const tasksResource2 = uriIdentityService2.extUri.joinPath(uriIdentityService2.extUri.dirname(client2.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource2 = client2.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; const fileService2 = client2.instantiationService.get(IFileService); await fileService2.writeFile(tasksResource2, VSBuffer.fromString(JSON.stringify({ 'version': '2.0.0', @@ -323,8 +308,7 @@ suite('TasksSync', () => { }))); const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; await client2.sync(); await testObject.sync(await client.manifest()); @@ -369,8 +353,7 @@ suite('TasksSync', () => { test('when tasks file has moved forward locally and remotely - accept remote', async () => { const client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); - const uriIdentityService2 = client2.instantiationService.get(IUriIdentityService); - const tasksResource2 = uriIdentityService2.extUri.joinPath(uriIdentityService2.extUri.dirname(client2.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource2 = client2.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; const fileService2 = client2.instantiationService.get(IFileService); await fileService2.writeFile(tasksResource2, VSBuffer.fromString(JSON.stringify({ 'version': '2.0.0', @@ -378,8 +361,7 @@ suite('TasksSync', () => { }))); const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; await client2.sync(); await testObject.sync(await client.manifest()); @@ -418,8 +400,7 @@ suite('TasksSync', () => { test('when tasks file has moved forward locally and remotely - accept local', async () => { const client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); - const uriIdentityService2 = client2.instantiationService.get(IUriIdentityService); - const tasksResource2 = uriIdentityService2.extUri.joinPath(uriIdentityService2.extUri.dirname(client2.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource2 = client2.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; const fileService2 = client2.instantiationService.get(IFileService); await fileService2.writeFile(tasksResource2, VSBuffer.fromString(JSON.stringify({ 'version': '2.0.0', @@ -427,8 +408,7 @@ suite('TasksSync', () => { }))); const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; await client2.sync(); await testObject.sync(await client.manifest()); @@ -466,8 +446,7 @@ suite('TasksSync', () => { test('when tasks file is created after first sync', async () => { const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; await testObject.sync(await client.manifest()); const content = JSON.stringify({ @@ -498,8 +477,7 @@ suite('TasksSync', () => { test('apply remote when tasks file does not exist', async () => { const fileService = client.instantiationService.get(IFileService); - const uriIdentityService = client.instantiationService.get(IUriIdentityService); - const tasksResource = uriIdentityService.extUri.joinPath(uriIdentityService.extUri.dirname(client.instantiationService.get(IEnvironmentService).settingsResource), 'tasks.json'); + const tasksResource = client.instantiationService.get(IUserDataProfilesService).defaultProfile.tasksResource; if (await fileService.exists(tasksResource)) { await fileService.del(tasksResource); } diff --git a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts index 134fb2e963b9b..cc94faefa1afb 100644 --- a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts @@ -11,6 +11,7 @@ import { joinPath } from 'vs/base/common/resources'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { UserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; import { IUserDataSyncService, SyncResource, UserDataAutoSyncError, UserDataSyncErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; @@ -209,9 +210,10 @@ suite('UserDataAutoSyncService', () => { // Do changes in the client const fileService = client.instantiationService.get(IFileService); const environmentService = client.instantiationService.get(IEnvironmentService); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); - await fileService.writeFile(joinPath(environmentService.snippetsHome, 'html.json'), VSBuffer.fromString(`{}`)); + const userDataProfilesService = client.instantiationService.get(IUserDataProfilesService); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); + await fileService.writeFile(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'html.json'), VSBuffer.fromString(`{}`)); await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); await testObject.sync(); diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 293324b83c576..0d335cb51d8dd 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -10,7 +10,7 @@ import { Emitter } from 'vs/base/common/event'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import { joinPath, dirname } from 'vs/base/common/resources'; +import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { IHeaders, IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request'; @@ -41,6 +41,7 @@ import { IUserDataSyncMachinesService, UserDataSyncMachinesService } from 'vs/pl import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSyncEnablementService'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncStoreManagementService, UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export class UserDataSyncClient extends Disposable { @@ -53,20 +54,19 @@ export class UserDataSyncClient extends Disposable { async setUp(empty: boolean = false): Promise { registerConfiguration(); + + const logService = this.instantiationService.stub(ILogService, new NullLogService()); + const userRoamingDataHome = URI.file('userdata').with({ scheme: Schemas.inMemory }); const userDataSyncHome = joinPath(userRoamingDataHome, '.sync'); const environmentService = this.instantiationService.stub(IEnvironmentService, >{ userDataSyncHome, userRoamingDataHome, - settingsResource: joinPath(userRoamingDataHome, 'settings.json'), - keybindingsResource: joinPath(userRoamingDataHome, 'keybindings.json'), - snippetsHome: joinPath(userRoamingDataHome, 'snippets'), argvResource: joinPath(userRoamingDataHome, 'argv.json'), sync: 'on', }); - const logService = new NullLogService(); - this.instantiationService.stub(ILogService, logService); + const userDataProfilesService = this.instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, logService)); this.instantiationService.stub(IProductService, { _serviceBrand: undefined, ...product, ...{ @@ -86,7 +86,7 @@ export class UserDataSyncClient extends Disposable { this.instantiationService.stub(IStorageService, this._register(new InMemoryStorageService())); - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService)); await configurationService.initialize(); this.instantiationService.stub(IConfigurationService, configurationService); this.instantiationService.stub(IUriIdentityService, this.instantiationService.createInstance(UriIdentityService)); @@ -123,10 +123,10 @@ export class UserDataSyncClient extends Disposable { this.instantiationService.stub(IUserDataSyncService, this._register(this.instantiationService.createInstance(UserDataSyncService))); if (!empty) { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({}))); - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([]))); - await fileService.writeFile(joinPath(environmentService.snippetsHome, 'c.json'), VSBuffer.fromString(`{}`)); - await fileService.writeFile(joinPath(dirname(environmentService.settingsResource), 'tasks.json'), VSBuffer.fromString(`{}`)); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({}))); + await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify([]))); + await fileService.writeFile(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'c.json'), VSBuffer.fromString(`{}`)); + await fileService.writeFile(userDataProfilesService.defaultProfile.tasksResource, VSBuffer.fromString(`{}`)); await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'en' }))); } await configurationService.reloadConfiguration(); diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts index 80c99c789c2e3..b549b519d9740 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts @@ -9,6 +9,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { dirname, joinPath } from 'vs/base/common/resources'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IUserDataSyncEnablementService, IUserDataSyncService, SyncResource, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; @@ -155,11 +156,12 @@ suite('UserDataSyncService', () => { await testClient.setUp(); const fileService = testClient.instantiationService.get(IFileService); const environmentService = testClient.instantiationService.get(IEnvironmentService); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); + const userDataProfilesService = testClient.instantiationService.get(IUserDataProfilesService); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); - await fileService.writeFile(joinPath(environmentService.snippetsHome, 'html.json'), VSBuffer.fromString(`{}`)); - await fileService.writeFile(joinPath(dirname(environmentService.settingsResource), 'tasks.json'), VSBuffer.fromString(JSON.stringify({}))); + await fileService.writeFile(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'html.json'), VSBuffer.fromString(`{}`)); + await fileService.writeFile(joinPath(dirname(userDataProfilesService.defaultProfile.settingsResource), 'tasks.json'), VSBuffer.fromString(JSON.stringify({}))); const testObject = testClient.instantiationService.get(IUserDataSyncService); // Sync (merge) from the test client @@ -213,9 +215,10 @@ suite('UserDataSyncService', () => { // Do changes in the client const fileService = client.instantiationService.get(IFileService); const environmentService = client.instantiationService.get(IEnvironmentService); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); - await fileService.writeFile(joinPath(environmentService.snippetsHome, 'html.json'), VSBuffer.fromString(`{}`)); + const userDataProfilesService = client.instantiationService.get(IUserDataProfilesService); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); + await fileService.writeFile(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'html.json'), VSBuffer.fromString(`{}`)); await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); // Sync from the client @@ -248,9 +251,10 @@ suite('UserDataSyncService', () => { // Do changes in the client const fileService = client.instantiationService.get(IFileService); const environmentService = client.instantiationService.get(IEnvironmentService); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); - await fileService.writeFile(joinPath(environmentService.snippetsHome, 'html.json'), VSBuffer.fromString(`{}`)); + const userDataProfilesService = client.instantiationService.get(IUserDataProfilesService); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); + await fileService.writeFile(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'html.json'), VSBuffer.fromString(`{}`)); await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); client.instantiationService.get(IUserDataSyncEnablementService).setResourceEnablement(SyncResource.Snippets, false); @@ -286,9 +290,10 @@ suite('UserDataSyncService', () => { // Do changes in first client and sync const fileService = client.instantiationService.get(IFileService); const environmentService = client.instantiationService.get(IEnvironmentService); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); - await fileService.writeFile(joinPath(environmentService.snippetsHome, 'html.json'), VSBuffer.fromString(`{ "a": "changed" }`)); + const userDataProfilesService = client.instantiationService.get(IUserDataProfilesService); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); + await fileService.writeFile(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'html.json'), VSBuffer.fromString(`{ "a": "changed" }`)); await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'de' }))); await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); @@ -395,16 +400,16 @@ suite('UserDataSyncService', () => { const client = disposableStore.add(new UserDataSyncClient(target)); await client.setUp(); let fileService = client.instantiationService.get(IFileService); - let environmentService = client.instantiationService.get(IEnvironmentService); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + let userDataProfilesService = client.instantiationService.get(IUserDataProfilesService); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); // Setup the test client const testClient = disposableStore.add(new UserDataSyncClient(target)); await testClient.setUp(); fileService = testClient.instantiationService.get(IFileService); - environmentService = testClient.instantiationService.get(IEnvironmentService); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 16 }))); + userDataProfilesService = testClient.instantiationService.get(IUserDataProfilesService); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 16 }))); const testObject = testClient.instantiationService.get(IUserDataSyncService); // sync from the client @@ -421,21 +426,21 @@ suite('UserDataSyncService', () => { const client = disposableStore.add(new UserDataSyncClient(target)); await client.setUp(); let fileService = client.instantiationService.get(IFileService); - let environmentService = client.instantiationService.get(IEnvironmentService); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + let userDataProfilesService = client.instantiationService.get(IUserDataProfilesService); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); // Setup the test client and get conflicts in settings const testClient = disposableStore.add(new UserDataSyncClient(target)); await testClient.setUp(); let testFileService = testClient.instantiationService.get(IFileService); - let testEnvironmentService = testClient.instantiationService.get(IEnvironmentService); - await testFileService.writeFile(testEnvironmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 16 }))); + userDataProfilesService = testClient.instantiationService.get(IUserDataProfilesService); + await testFileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 16 }))); const testObject = testClient.instantiationService.get(IUserDataSyncService); await (await testObject.createSyncTask(null)).run(); // sync from the first client with changes in keybindings - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); + await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify([{ 'command': 'abcd', 'key': 'cmd+c' }]))); await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); // sync from the test client @@ -463,16 +468,16 @@ suite('UserDataSyncService', () => { const client = disposableStore.add(new UserDataSyncClient(target)); await client.setUp(); let fileService = client.instantiationService.get(IFileService); - let environmentService = client.instantiationService.get(IEnvironmentService); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); + let userDataProfilesService = client.instantiationService.get(IUserDataProfilesService); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 14 }))); await (await client.instantiationService.get(IUserDataSyncService).createSyncTask(null)).run(); // Setup the test client const testClient = disposableStore.add(new UserDataSyncClient(target)); await testClient.setUp(); fileService = testClient.instantiationService.get(IFileService); - environmentService = testClient.instantiationService.get(IEnvironmentService); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 16 }))); + userDataProfilesService = testClient.instantiationService.get(IUserDataProfilesService); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ 'editor.fontSize': 16 }))); const testObject = testClient.instantiationService.get(IUserDataSyncService); diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts index 4790464ce4a82..e8c43dd0879e4 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts @@ -14,12 +14,12 @@ import { ConfigurationSyncStore } from 'vs/base/common/product'; import { URI } from 'vs/base/common/uri'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { NullLogService } from 'vs/platform/log/common/log'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRequestService } from 'vs/platform/request/common/request'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IUserDataSyncStore, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, SyncResource, UserDataSyncErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; import { RequestsSession, UserDataSyncStoreManagementService, UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; @@ -46,7 +46,7 @@ suite('UserDataSyncStoreManagementService', () => { canSwitch: false, authenticationProviders: { 'configuredAuthProvider': { scopes: [] } } }; - await client.instantiationService.get(IFileService).writeFile(client.instantiationService.get(IEnvironmentService).settingsResource, VSBuffer.fromString(JSON.stringify({ + await client.instantiationService.get(IFileService).writeFile(client.instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ 'configurationSync.store': configuredStore }))); await client.instantiationService.get(IConfigurationService).reloadConfiguration(); diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index f1e8986f5c508..e496c3e765cfa 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -42,6 +42,7 @@ import { buildHelpMessage, buildVersionMessage, OptionDescriptions } from 'vs/pl import { isWindows } from 'vs/base/common/platform'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; class CliMain extends Disposable { @@ -90,8 +91,12 @@ class CliMain extends Disposable { services.set(IFileService, fileService); fileService.registerProvider(Schemas.file, this._register(new DiskFileSystemProvider(logService))); + // User Data Profiles + const userDataProfilesService = this._register(new UserDataProfilesService(environmentService, logService)); + services.set(IUserDataProfilesService, userDataProfilesService); + // Configuration - const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService)); await configurationService.initialize(); services.set(IConfigurationService, configurationService); diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index b8dd198abc8b2..917fe072d5f11 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -73,6 +73,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLogChannel'; import { dirname, joinPath } from 'vs/base/common/resources'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export class BrowserMain extends Disposable { @@ -202,6 +203,10 @@ export class BrowserMain extends Disposable { const logService = new BufferLogService(getLogLevel(environmentService)); serviceCollection.set(ILogService, logService); + // User Data Profiles + const userDataProfilesService = new UserDataProfilesService(environmentService, logService); + serviceCollection.set(IUserDataProfilesService, userDataProfilesService); + // Remote const connectionToken = environmentService.options.connectionToken || getCookieValue(connectionTokenCookieName); const remoteAuthorityResolverService = new RemoteAuthorityResolverService(productService, connectionToken, this.configuration.resourceUriProvider); @@ -237,7 +242,7 @@ export class BrowserMain extends Disposable { // Long running services (workspace, config, storage) const [configurationService, storageService] = await Promise.all([ - this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, uriIdentityService, logService).then(service => { + this.createWorkspaceService(payload, environmentService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -302,7 +307,7 @@ export class BrowserMain extends Disposable { serviceCollection.set(ICredentialsService, credentialsService); // Userdata Initialize Service - const userDataInitializationService = new UserDataInitializationService(environmentService, credentialsService, userDataSyncStoreManagementService, fileService, storageService, productService, requestService, logService, uriIdentityService); + const userDataInitializationService = new UserDataInitializationService(environmentService, credentialsService, userDataSyncStoreManagementService, fileService, userDataProfilesService, storageService, productService, requestService, logService, uriIdentityService); serviceCollection.set(IUserDataInitializationService, userDataInitializationService); if (await userDataInitializationService.requiresInitialization()) { @@ -438,9 +443,9 @@ export class BrowserMain extends Disposable { } } - private async createWorkspaceService(payload: IAnyWorkspaceIdentifier, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { + private async createWorkspaceService(payload: IAnyWorkspaceIdentifier, environmentService: IWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData, Schemas.tmp] /* Cache all non native resources */, environmentService, fileService); - const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, fileService, remoteAgentService, uriIdentityService, logService); + const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService); try { await workspaceService.initialize(payload); diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts index 13380b3aaf51f..aa4a1d1953693 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts @@ -14,6 +14,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IStorageService, IS_NEW_KEY, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { AbstractExtensionsInitializer } from 'vs/platform/userDataSync/common/extensionsSync'; import { IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; import { IRemoteUserData, IUserDataSyncStoreManagementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; @@ -99,13 +100,14 @@ class RemoteExtensionsInitializer extends AbstractExtensionsInitializer { @IExtensionManagementService extensionManagementService: IExtensionManagementService, @IIgnoredExtensionsManagementService ignoredExtensionsManagementService: IIgnoredExtensionsManagementService, @IFileService fileService: IFileService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IEnvironmentService environmentService: IEnvironmentService, @ILogService logService: ILogService, @IUriIdentityService uriIdentityService: IUriIdentityService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService, ) { - super(extensionManagementService, ignoredExtensionsManagementService, fileService, environmentService, logService, uriIdentityService); + super(extensionManagementService, ignoredExtensionsManagementService, fileService, userDataProfilesService, environmentService, logService, uriIdentityService); } protected override async doInitialize(remoteUserData: IRemoteUserData): Promise { diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts index e69f1504400ad..04afba87a05a6 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts @@ -31,8 +31,8 @@ import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { equals } from 'vs/base/common/arrays'; import { assertIsDefined } from 'vs/base/common/types'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { isEqual } from 'vs/base/common/resources'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; const NLS_LAUNCH_MESSAGE = nls.localize('defineKeybinding.start', "Define Keybinding"); const NLS_KB_LAYOUT_ERROR_MESSAGE = nls.localize('defineKeybinding.kbLayoutErrorMessage', "You won't be able to produce this key combination under your current keyboard layout."); @@ -51,7 +51,7 @@ export class DefineKeybindingController extends Disposable implements IEditorCon constructor( private _editor: ICodeEditor, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService + @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService ) { super(); @@ -70,7 +70,7 @@ export class DefineKeybindingController extends Disposable implements IEditorCon } private _update(): void { - if (!isInterestingEditorModel(this._editor, this._environmentService)) { + if (!isInterestingEditorModel(this._editor, this._userDataProfilesService)) { this._disposeKeybindingWidgetRenderer(); this._disposeKeybindingDecorationRenderer(); return; @@ -365,7 +365,7 @@ class DefineKeybindingCommand extends EditorCommand { } runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor): void { - if (!isInterestingEditorModel(editor, accessor.get(IEnvironmentService)) || editor.getOption(EditorOption.readOnly)) { + if (!isInterestingEditorModel(editor, accessor.get(IUserDataProfilesService)) || editor.getOption(EditorOption.readOnly)) { return; } const controller = DefineKeybindingController.get(editor); @@ -375,12 +375,12 @@ class DefineKeybindingCommand extends EditorCommand { } } -function isInterestingEditorModel(editor: ICodeEditor, environmentService: IEnvironmentService): boolean { +function isInterestingEditorModel(editor: ICodeEditor, userDataProfilesService: IUserDataProfilesService): boolean { const model = editor.getModel(); if (!model) { return false; } - return isEqual(model.uri, environmentService.keybindingsResource); + return isEqual(model.uri, userDataProfilesService.defaultProfile.keybindingsResource); } registerEditorContribution(DefineKeybindingController.ID, DefineKeybindingController); diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index b5230a5af223b..f7c5eb3353593 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -43,6 +43,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle import { KeybindingsEditorInput } from 'vs/workbench/services/preferences/browser/keybindingsEditorInput'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; @@ -139,6 +140,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon constructor( @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IUserDataProfilesService private readonly userDataProfileService: IUserDataProfilesService, @IPreferencesService private readonly preferencesService: IPreferencesService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @ILabelService private readonly labelService: ILabelService, @@ -250,7 +252,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon icon: preferencesOpenSettingsIcon, menu: [{ id: MenuId.EditorTitle, - when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(that.environmentService.settingsResource.toString()), ContextKeyExpr.not('isInDiffEditor')), + when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(that.userDataProfileService.defaultProfile.settingsResource.toString()), ContextKeyExpr.not('isInDiffEditor')), group: 'navigation', order: 1 }] @@ -742,7 +744,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon { id: MenuId.CommandPalette }, { id: MenuId.EditorTitle, - when: ResourceContextKey.Resource.isEqualTo(that.environmentService.keybindingsResource.toString()), + when: ResourceContextKey.Resource.isEqualTo(that.userDataProfileService.defaultProfile.keybindingsResource.toString()), group: 'navigation', order: 1, } diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index 0a210e04734d3..cba9f70de2849 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -13,7 +13,6 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -24,6 +23,7 @@ import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEdit import { RegisteredEditorPriority, IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEditorService'; import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, IPreferencesService, USE_SPLIT_JSON_SETTING } from 'vs/workbench/services/preferences/common/preferences'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); @@ -36,7 +36,7 @@ export class PreferencesContribution implements IWorkbenchContribution { @ITextModelService private readonly textModelResolverService: ITextModelService, @IPreferencesService private readonly preferencesService: IPreferencesService, @ILanguageService private readonly languageService: ILanguageService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorResolverService private readonly editorResolverService: IEditorResolverService, @@ -71,7 +71,7 @@ export class PreferencesContribution implements IWorkbenchContribution { }, ({ resource, options }): EditorInputWithOptions => { // Global User Settings File - if (isEqual(resource, this.environmentService.settingsResource)) { + if (isEqual(resource, this.userDataProfilesService.defaultProfile.settingsResource)) { return { editor: this.preferencesService.createSplitJsonEditorInput(ConfigurationTarget.USER_LOCAL, resource), options }; } diff --git a/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts b/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts index 9b5871c2e89d2..b533257b37553 100644 --- a/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts +++ b/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { extname } from 'vs/base/common/path'; import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; @@ -19,6 +18,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { isValidBasename } from 'vs/base/common/extpath'; import { joinPath, basename } from 'vs/base/common/resources'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; namespace ISnippetPick { export function is(thing: object | undefined): thing is ISnippetPick { @@ -31,7 +31,7 @@ interface ISnippetPick extends IQuickPickItem { hint?: true; } -async function computePicks(snippetService: ISnippetsService, envService: IEnvironmentService, languageService: ILanguageService) { +async function computePicks(snippetService: ISnippetsService, userDataProfilesService: IUserDataProfilesService, languageService: ILanguageService) { const existing: ISnippetPick[] = []; const future: ISnippetPick[] = []; @@ -85,7 +85,7 @@ async function computePicks(snippetService: ISnippetsService, envService: IEnvir } } - const dir = envService.snippetsHome; + const dir = userDataProfilesService.defaultProfile.snippetsHome; for (const languageId of languageService.getRegisteredLanguageIds()) { const label = languageService.getLanguageName(languageId); if (label && !seen.has(languageId)) { @@ -227,19 +227,19 @@ registerAction2(class ConfigureSnippets extends Action2 { const quickInputService = accessor.get(IQuickInputService); const opener = accessor.get(IOpenerService); const languageService = accessor.get(ILanguageService); - const envService = accessor.get(IEnvironmentService); + const userDataProfilesService = accessor.get(IUserDataProfilesService); const workspaceService = accessor.get(IWorkspaceContextService); const fileService = accessor.get(IFileService); const textFileService = accessor.get(ITextFileService); - const picks = await computePicks(snippetService, envService, languageService); + const picks = await computePicks(snippetService, userDataProfilesService, languageService); const existing: QuickPickInput[] = picks.existing; type SnippetPick = IQuickPickItem & { uri: URI } & { scope: string }; const globalSnippetPicks: SnippetPick[] = [{ scope: nls.localize('new.global_scope', 'global'), label: nls.localize('new.global', "New Global Snippets file..."), - uri: envService.snippetsHome + uri: userDataProfilesService.defaultProfile.snippetsHome }]; const workspaceSnippetPicks: SnippetPick[] = []; diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts index 26e453f1b1e0c..53451fb26d33e 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts @@ -30,6 +30,7 @@ import { isStringArray } from 'vs/base/common/types'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; namespace snippetExt { @@ -177,6 +178,7 @@ class SnippetsService implements ISnippetsService { constructor( @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @ILanguageService private readonly _languageService: ILanguageService, @ILogService private readonly _logService: ILogService, @@ -348,7 +350,7 @@ class SnippetsService implements ISnippetsService { } private async _initUserSnippets(): Promise { - const userSnippetsFolder = this._environmentService.snippetsHome; + const userSnippetsFolder = this._userDataProfilesService.defaultProfile.snippetsHome; await this._fileService.createFolder(userSnippetsFolder); return await this._initFolderSnippets(SnippetSource.User, userSnippetsFolder, this._disposables); } diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index adf6f60619f5a..f84c4d822afa8 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -25,6 +25,7 @@ import { getMimeTypes } from 'vs/editor/common/services/languagesAssociations'; import { hash } from 'vs/base/common/hash'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; type TelemetryData = { mimeType: string; @@ -54,7 +55,8 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr @IEditorService editorService: IEditorService, @IKeybindingService keybindingsService: IKeybindingService, @IWorkbenchThemeService themeService: IWorkbenchThemeService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IConfigurationService configurationService: IConfigurationService, @IPaneCompositePartService paneCompositeService: IPaneCompositePartService, @ITextFileService textFileService: ITextFileService @@ -164,17 +166,17 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr } // Check for global settings file - if (isEqual(resource, this.environmentService.settingsResource)) { + if (isEqual(resource, this.userDataProfilesService.defaultProfile.settingsResource)) { return 'global-settings'; } // Check for keybindings file - if (isEqual(resource, this.environmentService.keybindingsResource)) { + if (isEqual(resource, this.userDataProfilesService.defaultProfile.keybindingsResource)) { return 'keybindings'; } // Check for snippets - if (isEqualOrParent(resource, this.environmentService.snippetsHome)) { + if (isEqualOrParent(resource, this.userDataProfilesService.defaultProfile.snippetsHome)) { return 'snippets'; } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 4a7fe8b620255..b07f2bac52460 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -60,6 +60,7 @@ import { CATEGORIES } from 'vs/workbench/common/actions'; import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; const CONTEXT_CONFLICTS_SOURCES = new RawContextKey('conflictsSources', ''); @@ -113,6 +114,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @IActivityService private readonly activityService: IActivityService, @INotificationService private readonly notificationService: INotificationService, @IEditorService private readonly editorService: IEditorService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IDialogService private readonly dialogService: IDialogService, @IQuickInputService private readonly quickInputService: IQuickInputService, @@ -436,7 +438,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (!this.hostService.hasFocus) { return; } - const resource = source === SyncResource.Settings ? this.environmentService.settingsResource : this.environmentService.keybindingsResource; + const resource = source === SyncResource.Settings ? this.userDataProfilesService.defaultProfile.settingsResource : this.userDataProfilesService.defaultProfile.keybindingsResource; if (isEqual(resource, EditorResourceAccessor.getCanonicalUri(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }))) { // Do not show notification if the file in error is active return; diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts index 15a65e3ba184c..210601cf0dbf4 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger.ts @@ -7,13 +7,13 @@ import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; import { isEqual } from 'vs/base/common/resources'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IViewsService } from 'vs/workbench/common/views'; import { VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { KeybindingsEditorInput } from 'vs/workbench/services/preferences/browser/keybindingsEditorInput'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; @@ -22,7 +22,7 @@ export class UserDataSyncTrigger extends Disposable implements IWorkbenchContrib constructor( @IEditorService editorService: IEditorService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IViewsService viewsService: IViewsService, @IUserDataAutoSyncService userDataAutoSyncService: IUserDataAutoSyncService, @IHostService hostService: IHostService, @@ -56,10 +56,10 @@ export class UserDataSyncTrigger extends Disposable implements IWorkbenchContrib return 'keybindingsEditor'; } const resource = editorInput.resource; - if (isEqual(resource, this.environmentService.settingsResource)) { + if (isEqual(resource, this.userDataProfilesService.defaultProfile.settingsResource)) { return 'settingsEditor'; } - if (isEqual(resource, this.environmentService.keybindingsResource)) { + if (isEqual(resource, this.userDataProfilesService.defaultProfile.keybindingsResource)) { return 'keybindingsEditor'; } return undefined; diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 98b95174864da..3531cfd21fae5 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -50,6 +50,7 @@ import { isCI, isMacintosh } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/electron-sandbox/diskFileSystemProvider'; import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export class DesktopMain extends Disposable { @@ -178,6 +179,10 @@ export class DesktopMain extends Disposable { logService.trace('workbench#open(): with configuration', safeStringify(this.configuration)); } + // User Data Profiles + const userDataProfilesService = new UserDataProfilesService(environmentService, logService); + serviceCollection.set(IUserDataProfilesService, userDataProfilesService); + // Shared Process const sharedProcessService = new SharedProcessService(this.configuration.windowId, logService); serviceCollection.set(ISharedProcessService, sharedProcessService); @@ -247,7 +252,7 @@ export class DesktopMain extends Disposable { const payload = this.resolveWorkspaceInitializationPayload(environmentService); const [configurationService, storageService] = await Promise.all([ - this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, uriIdentityService, logService).then(service => { + this.createWorkspaceService(payload, environmentService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -325,9 +330,9 @@ export class DesktopMain extends Disposable { return workspaceInitializationPayload; } - private async createWorkspaceService(payload: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { + private async createWorkspaceService(payload: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData] /* Cache all non native resources */, environmentService, fileService); - const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache }, environmentService, fileService, remoteAgentService, uriIdentityService, logService); + const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache }, environmentService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService); try { await workspaceService.initialize(payload); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 45af17cc1752e..bee40220f69b2 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -39,6 +39,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { isUndefined } from 'vs/base/common/types'; import { localize } from 'vs/nls'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; class Workspace extends BaseWorkspace { initialized: boolean = false; @@ -98,6 +99,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat constructor( { remoteAuthority, configurationCache }: { remoteAuthority?: string; configurationCache: IConfigurationCache }, environmentService: IWorkbenchEnvironmentService, + userDataProfilesService: IUserDataProfilesService, fileService: IFileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, @@ -116,7 +118,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat this.logService = logService; this._configuration = new Configuration(this.defaultConfiguration.configurationModel, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); this.cachedFolderConfigs = new ResourceMap(); - this.localUserConfiguration = this._register(new UserConfiguration(environmentService.settingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService)); + this.localUserConfiguration = this._register(new UserConfiguration(userDataProfilesService.defaultProfile.settingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService)); this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); if (remoteAuthority) { const remoteUserConfiguration = this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, fileService, uriIdentityService, remoteAgentService)); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index 3e4e805c23132..52a210d5d371c 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -11,7 +11,6 @@ import { Queue } from 'vs/base/common/async'; import { Edit, FormattingOptions } from 'vs/base/common/jsonFormatter'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IConfigurationService, IConfigurationUpdateOverrides } from 'vs/platform/configuration/common/configuration'; import { FOLDER_SETTINGS_PATH, WORKSPACE_STANDALONE_CONFIGURATIONS, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY, USER_STANDALONE_CONFIGURATIONS, TASKS_DEFAULT, FOLDER_SCOPES } from 'vs/workbench/services/configuration/common/configuration'; @@ -29,6 +28,7 @@ import { IReference } from 'vs/base/common/lifecycle'; import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Selection } from 'vs/editor/common/core/selection'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export const enum ConfigurationEditingErrorCode { @@ -148,7 +148,7 @@ export class ConfigurationEditingService { constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IFileService private readonly fileService: IFileService, @ITextModelService private readonly textModelResolverService: ITextModelService, @ITextFileService private readonly textFileService: ITextFileService, @@ -604,9 +604,9 @@ export class ConfigurationEditingService { private getConfigurationFileResource(target: EditableConfigurationTarget, relativePath: string, resource: URI | null | undefined): URI | null { if (target === EditableConfigurationTarget.USER_LOCAL) { if (relativePath) { - return this.uriIdentityService.extUri.joinPath(this.uriIdentityService.extUri.dirname(this.environmentService.settingsResource), relativePath); + return this.uriIdentityService.extUri.joinPath(this.uriIdentityService.extUri.dirname(this.userDataProfilesService.defaultProfile.settingsResource), relativePath); } else { - return this.environmentService.settingsResource; + return this.userDataProfilesService.defaultProfile.settingsResource; } } if (target === EditableConfigurationTarget.USER_REMOTE) { diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts index 7d7e3cb1267e8..09c980dd644f4 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts @@ -39,6 +39,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteAgentService'; import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { getSingleFolderWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' }); @@ -53,6 +54,7 @@ suite('ConfigurationEditingService', () => { let instantiationService: TestInstantiationService; let environmentService: BrowserWorkbenchEnvironmentService; + let userDataProfilesService: IUserDataProfilesService; let fileService: IFileService; let workspaceService: WorkspaceService; let testObject: ConfigurationEditingService; @@ -93,11 +95,12 @@ suite('ConfigurationEditingService', () => { instantiationService = workbenchInstantiationService(undefined, disposables); environmentService = TestEnvironmentService; instantiationService.stub(IEnvironmentService, environmentService); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, logService)); const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null)); disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService)))); instantiationService.stub(IFileService, fileService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, workspaceService); await workspaceService.initialize(getSingleFolderWorkspaceIdentifier(workspaceFolder)); @@ -133,7 +136,7 @@ suite('ConfigurationEditingService', () => { }); test('errors cases - invalid configuration', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); try { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }, { donotNotifyError: true }); } catch (error) { @@ -182,36 +185,36 @@ suite('ConfigurationEditingService', () => { test('write one setting - empty file', async () => { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }); - const contents = await fileService.readFile(environmentService.settingsResource); + const contents = await fileService.readFile(userDataProfilesService.defaultProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.strictEqual(parsed['configurationEditing.service.testSetting'], 'value'); }); test('write one setting - existing file', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }); - const contents = await fileService.readFile(environmentService.settingsResource); + const contents = await fileService.readFile(userDataProfilesService.defaultProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.strictEqual(parsed['configurationEditing.service.testSetting'], 'value'); assert.strictEqual(parsed['my.super.setting'], 'my.super.value'); }); test('remove an existing setting - existing file', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value", "configurationEditing.service.testSetting": "value" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value", "configurationEditing.service.testSetting": "value" }')); await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: undefined }); - const contents = await fileService.readFile(environmentService.settingsResource); + const contents = await fileService.readFile(userDataProfilesService.defaultProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.deepStrictEqual(Object.keys(parsed), ['my.super.setting']); assert.strictEqual(parsed['my.super.setting'], 'my.super.value'); }); test('remove non existing setting - existing file', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: undefined }); - const contents = await fileService.readFile(environmentService.settingsResource); + const contents = await fileService.readFile(userDataProfilesService.defaultProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.deepStrictEqual(Object.keys(parsed), ['my.super.setting']); assert.strictEqual(parsed['my.super.setting'], 'my.super.value'); @@ -222,7 +225,7 @@ suite('ConfigurationEditingService', () => { const value = { 'configurationEditing.service.testSetting': 'overridden value' }; await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key, value }); - const contents = await fileService.readFile(environmentService.settingsResource); + const contents = await fileService.readFile(userDataProfilesService.defaultProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.deepStrictEqual(parsed[key], value); }); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 352fd8e9d7e58..7ffce88d01be5 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -44,6 +44,7 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteA import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService'; import { hash } from 'vs/base/common/hash'; import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; function convertToWorkspacePayload(folder: URI): ISingleFolderWorkspaceIdentifier { return { @@ -77,7 +78,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(environmentService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); await (testObject).initialize(convertToWorkspacePayload(folder)); }); @@ -117,7 +118,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(environmentService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); await (testObject).initialize(convertToWorkspacePayload(folder)); const actual = testObject.getWorkspaceFolder(joinPath(folder, 'a')); @@ -137,7 +138,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(environmentService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); await (testObject).initialize(convertToWorkspacePayload(folder)); @@ -184,7 +185,7 @@ suite('WorkspaceContextService - Workspace', () => { const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null)); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(environmentService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -242,7 +243,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(environmentService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); @@ -440,7 +441,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { suite('WorkspaceService - Initialization', () => { - let configResource: URI, testObject: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService; + let configResource: URI, testObject: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables = new DisposableStore(); @@ -484,8 +485,9 @@ suite('WorkspaceService - Initialization', () => { environmentService = TestEnvironmentService; const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, logService)); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -501,7 +503,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a folder workspace from an empty workspace with no configuration changes', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -526,7 +528,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a folder workspace from an empty workspace with configuration changes', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -553,7 +555,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a multi root workspace from an empty workspace with no configuration changes', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -576,7 +578,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a multi root workspace from an empty workspace with configuration changes', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -603,7 +605,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a folder workspace from a folder workspace with no configuration changes', async () => { await testObject.initialize(convertToWorkspacePayload(joinPath(ROOT, 'a'))); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); testObject.onDidChangeWorkbenchState(target); @@ -669,7 +671,7 @@ suite('WorkspaceService - Initialization', () => { suite('WorkspaceConfigurationService - Folder', () => { - let testObject: WorkspaceService, workspaceService: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService; + let testObject: WorkspaceService, workspaceService: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables: DisposableStore = new DisposableStore(); @@ -734,7 +736,8 @@ suite('WorkspaceConfigurationService - Folder', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, logService)); + workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -754,13 +757,13 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('globals override defaults', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'userValue'); }); test('globals', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('testworkbench.editor.tabs'), true); }); @@ -772,21 +775,21 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('workspace settings override user settings', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'workspaceValue'); }); test('machine overridable settings override user Settings', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.machineOverridableSetting'), 'workspaceValue'); }); test('workspace settings override user settings after defaults are registered ', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.newSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); configurationRegistry.registerConfiguration({ @@ -803,7 +806,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('machine overridable settings override user settings after defaults are registered ', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); configurationRegistry.registerConfiguration({ @@ -821,7 +824,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('application settings are not read from workspace', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -830,7 +833,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('application settings are not read from workspace when workspace folder uri is passed', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -839,7 +842,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('machine settings are not read from workspace', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -848,7 +851,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('machine settings are not read from workspace when workspace folder uri is passed', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -857,7 +860,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get application scope settings are not loaded after defaults are registered', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -882,7 +885,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get application scope settings are not loaded after defaults are registered when workspace folder uri is passed', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -907,7 +910,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get machine scope settings are not loaded after defaults are registered', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -932,7 +935,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get machine scope settings are not loaded after defaults are registered when workspace folder uri is passed', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -957,7 +960,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('reload configuration emits events after global configuraiton changes', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); const target = sinon.spy(); testObject.onDidChangeConfiguration(target); await testObject.reloadConfiguration(); @@ -973,7 +976,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('reload configuration should not emit event if no changes', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -997,7 +1000,7 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.strictEqual(actual.workspaceFolderValue, undefined); assert.strictEqual(actual.value, 'isSet'); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); actual = testObject.inspect('configurationService.folder.testSetting'); assert.strictEqual(actual.defaultValue, 'isSet'); @@ -1023,7 +1026,7 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.deepStrictEqual(actual.workspace, []); assert.deepStrictEqual(actual.workspaceFolder, []); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); actual = testObject.keys(); assert.ok(actual.default.indexOf('configurationService.folder.testSetting') !== -1); @@ -1178,7 +1181,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('creating workspace settings', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); await new Promise((c, e) => { const disposable = testObject.onDidChangeConfiguration(e => { @@ -1192,7 +1195,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('deleting workspace settings', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); const workspaceSettingsResource = joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'); await fileService.writeFile(workspaceSettingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1207,7 +1210,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is read from workspace when workspace is trusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1223,7 +1226,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is not read from workspace when workspace is changed to trusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1241,7 +1244,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('change event is triggered when workspace is changed to untrusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1256,7 +1259,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is not read from workspace when workspace is not trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1272,7 +1275,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is read when workspace is changed to trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1290,7 +1293,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('change event is triggered when workspace is changed to trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1303,7 +1306,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('adding an restricted setting triggers change event', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); testObject.updateWorkspaceTrust(false); const promise = Event.toPromise(testObject.onDidChangeRestrictedSettings); @@ -1314,7 +1317,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('remove an unregistered setting', async () => { const key = 'configurationService.folder.unknownSetting'; - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1329,7 +1332,7 @@ suite('WorkspaceConfigurationService - Folder', () => { suite('WorkspaceConfigurationService-Multiroot', () => { - let workspaceContextService: IWorkspaceContextService, jsonEditingServce: IJSONEditingService, testObject: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService; + let workspaceContextService: IWorkspaceContextService, jsonEditingServce: IJSONEditingService, testObject: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables = new DisposableStore(); @@ -1405,7 +1408,8 @@ suite('WorkspaceConfigurationService-Multiroot', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, logService)); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, workspaceService); @@ -1427,7 +1431,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { teardown(() => disposables.clear()); test('application settings are not read from workspace', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1436,7 +1440,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace when folder is passed', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1445,7 +1449,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1454,7 +1458,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace when folder is passed', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1463,7 +1467,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('get application scope settings are not loaded after defaults are registered', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1488,7 +1492,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('get application scope settings are not loaded after defaults are registered when workspace folder is passed', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting-2": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting-2": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newSetting-2': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1513,7 +1517,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('workspace settings override user settings after defaults are registered for machine overridable settings ', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newMachineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newMachineOverridableSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newMachineOverridableSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1539,7 +1543,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1548,7 +1552,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder when workspace folder is passed', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1557,7 +1561,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace folder', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1566,7 +1570,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace folder when workspace folder is passed', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1575,7 +1579,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder after defaults are registered', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1600,7 +1604,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder after defaults are registered', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1693,7 +1697,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.strictEqual(actual.workspaceFolderValue, undefined); assert.strictEqual(actual.value, 'isSet'); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testResourceSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testResourceSetting": "userValue" }')); await testObject.reloadConfiguration(); actual = testObject.inspect('configurationService.workspace.testResourceSetting'); assert.strictEqual(actual.defaultValue, 'isSet'); @@ -1948,7 +1952,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('restricted setting is read from workspace folders when workspace is trusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testRestrictedSetting1': 'workspaceValue' } }], true); await fileService.writeFile(joinPath(testObject.getWorkspace().folders[1].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting2": "workspaceFolder2Value" }')); await testObject.reloadConfiguration(); @@ -1968,7 +1972,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('restricted setting is not read from workspace when workspace is not trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testRestrictedSetting1': 'workspaceValue' } }], true); await fileService.writeFile(joinPath(testObject.getWorkspace().folders[1].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting2": "workspaceFolder2Value" }')); await testObject.reloadConfiguration(); @@ -1987,7 +1991,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('remove an unregistered setting', async () => { const key = 'configurationService.workspace.unknownSetting'; - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.unknownSetting': 'workspaceValue' } }], true); await fileService.writeFile(joinPath(workspaceContextService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "workspaceFolderValue1" }')); await fileService.writeFile(joinPath(workspaceContextService.getWorkspace().folders[1].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "workspaceFolderValue2" }')); @@ -2013,7 +2017,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { let testObject: WorkspaceService, folder: URI, machineSettingsResource: URI, remoteSettingsResource: URI, fileSystemProvider: InMemoryFileSystemProvider, resolveRemoteEnvironment: () => void, - instantiationService: TestInstantiationService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService; + instantiationService: TestInstantiationService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService; const remoteAuthority = 'configuraiton-tests'; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables = new DisposableStore(); @@ -2066,7 +2070,8 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve(), needsCaching: () => false }; - testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, logService)); + testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); instantiationService.stub(IEnvironmentService, environmentService); @@ -2152,7 +2157,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine settings in local user settings does not override defaults', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "globalValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "globalValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); @@ -2160,7 +2165,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine overridable settings in local user settings does not override defaults', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineOverridableSetting": "globalValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineOverridableSetting": "globalValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); @@ -2195,7 +2200,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine settings in local user settings does not override defaults after defalts are registered ', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineSetting": "userValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); @@ -2214,7 +2219,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine overridable settings in local user settings does not override defaults after defaults are registered ', async () => { - await fileService.writeFile(environmentService.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineOverridableSetting": "userValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index d41839b87ba62..85cda949313cc 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -55,15 +55,9 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi @memoize get userRoamingDataHome(): URI { return URI.file('/User').with({ scheme: Schemas.vscodeUserData }); } - @memoize - get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); } - @memoize get argvResource(): URI { return joinPath(this.userRoamingDataHome, 'argv.json'); } - @memoize - get snippetsHome(): URI { return joinPath(this.userRoamingDataHome, 'snippets'); } - @memoize get cacheHome(): URI { return joinPath(this.userRoamingDataHome, 'caches'); } @@ -92,9 +86,6 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi @memoize get sync(): 'on' | 'off' | undefined { return undefined; } - @memoize - get keybindingsResource(): URI { return joinPath(this.userRoamingDataHome, 'keybindings.json'); } - @memoize get keyboardLayoutResource(): URI { return joinPath(this.userRoamingDataHome, 'keyboardLayout.json'); } diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index b55025bade59a..5de2f1a6084e3 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -17,7 +17,6 @@ import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/c import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationScope, Extensions as ConfigExtensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ContextKeyExpr, IContextKeyService, ContextKeyExpression, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; import { IKeyboardEvent, IUserFriendlyKeybinding, KeybindingSource, IKeybindingService, IKeybindingEvent, KeybindingsSchemaContribution } from 'vs/platform/keybinding/common/keybinding'; @@ -52,6 +51,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { dirname } from 'vs/base/common/resources'; import { getAllUnboundCommands } from 'vs/workbench/services/keybinding/browser/unboundCommands'; import { UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; interface ContributedKeyBinding { command: string; @@ -191,7 +191,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { @ICommandService commandService: ICommandService, @ITelemetryService telemetryService: ITelemetryService, @INotificationService notificationService: INotificationService, - @IEnvironmentService environmentService: IEnvironmentService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IConfigurationService configurationService: IConfigurationService, @IHostService private readonly hostService: IHostService, @IExtensionService extensionService: IExtensionService, @@ -224,7 +224,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { this._cachedResolver = null; - this.userKeybindings = this._register(new UserKeybindings(environmentService.keybindingsResource, fileService, logService)); + this.userKeybindings = this._register(new UserKeybindings(userDataProfilesService.defaultProfile.keybindingsResource, fileService, logService)); this.userKeybindings.initialize().then(() => { if (this.userKeybindings.keybindings.length) { this.updateResolver({ source: KeybindingSource.User }); diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index 047618c1d731f..e939fb074457d 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -19,13 +19,13 @@ import { ITextModel } from 'vs/editor/common/model'; import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export const IKeybindingEditingService = createDecorator('keybindingEditingService'); @@ -47,14 +47,14 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding public _serviceBrand: undefined; private queue: Queue; - private resource: URI = this.environmentService.keybindingsResource; + private resource: URI = this.userDataProfilesService.defaultProfile.keybindingsResource; constructor( @ITextModelService private readonly textModelResolverService: ITextModelService, @ITextFileService private readonly textFileService: ITextFileService, @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService ) { super(); this.queue = new Queue(); diff --git a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts index 9de89527a827c..f1a828d3057a3 100644 --- a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts @@ -28,6 +28,7 @@ import { joinPath } from 'vs/base/common/resources'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; interface Modifiers { metaKey?: boolean; @@ -44,6 +45,7 @@ suite('KeybindingsEditing', () => { let instantiationService: TestInstantiationService; let fileService: IFileService; let environmentService: IEnvironmentService; + let userDataProfilesService: IUserDataProfilesService; let testObject: KeybindingsEditingService; setup(async () => { @@ -62,6 +64,8 @@ suite('KeybindingsEditing', () => { const configService = new TestConfigurationService(); configService.setUserConfiguration('files', { 'eol': '\n' }); + userDataProfilesService = new UserDataProfilesService(environmentService, logService); + instantiationService = workbenchInstantiationService({ fileService: () => fileService, configurationService: () => configService, @@ -74,7 +78,7 @@ suite('KeybindingsEditing', () => { teardown(() => disposables.clear()); test('errors cases - parse errors', async () => { - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); + await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); try { await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape } }), 'alt+c', undefined); assert.fail('Should fail with parse errors'); @@ -84,7 +88,7 @@ suite('KeybindingsEditing', () => { }); test('errors cases - parse errors 2', async () => { - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString('[{"key": }]')); + await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString('[{"key": }]')); try { await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape } }), 'alt+c', undefined); assert.fail('Should fail with parse errors'); @@ -101,7 +105,7 @@ suite('KeybindingsEditing', () => { }); test('errors cases - did not find an array', async () => { - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString('{"key": "alt+c", "command": "hello"}')); + await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString('{"key": "alt+c", "command": "hello"}')); try { await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape } }), 'alt+c', undefined); assert.fail('Should fail'); @@ -111,7 +115,7 @@ suite('KeybindingsEditing', () => { }); test('edit a default keybinding to an empty file', async () => { - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString('')); + await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString('')); const expected: IUserFriendlyKeybinding[] = [{ key: 'alt+c', command: 'a' }, { key: 'escape', command: '-a' }]; await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape }, command: 'a' }), 'alt+c', undefined); assert.deepStrictEqual(await getUserKeybindings(), expected); @@ -241,11 +245,11 @@ suite('KeybindingsEditing', () => { }); async function writeToKeybindingsFile(...keybindings: IUserFriendlyKeybinding[]): Promise { - await fileService.writeFile(environmentService.keybindingsResource, VSBuffer.fromString(JSON.stringify(keybindings || []))); + await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify(keybindings || []))); } async function getUserKeybindings(): Promise { - return json.parse((await fileService.readFile(environmentService.keybindingsResource)).value.toString()); + return json.parse((await fileService.readFile(userDataProfilesService.defaultProfile.keybindingsResource)).value.toString()); } function aResolvedKeybindingItem({ command, when, isDefault, firstPart, chordPart }: { command?: string; when?: string; isDefault?: boolean; firstPart?: { keyCode: KeyCode; modifiers?: Modifiers }; chordPart?: { keyCode: KeyCode; modifiers?: Modifiers } }): ResolvedKeybindingItem { diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 555a3a7ee20a8..a99c536c48023 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -20,7 +20,6 @@ import * as nls from 'vs/nls'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Extensions, getDefaultValue, IConfigurationRegistry, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; import { EditorResolution } from 'vs/platform/editor/common/editor'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -46,6 +45,7 @@ import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEd import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { isArray, isObject } from 'vs/base/common/types'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; const emptyEditableSettingsContent = '{\n}'; @@ -67,7 +67,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic @INotificationService private readonly notificationService: INotificationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @ITelemetryService private readonly telemetryService: ITelemetryService, @ITextModelService private readonly textModelResolverService: ITextModelService, @IKeybindingService keybindingService: IKeybindingService, @@ -95,7 +95,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic private readonly defaultSettingsRawResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/defaultSettings.json' }); get userSettingsResource(): URI { - return this.environmentService.settingsResource; + return this.userDataProfilesService.defaultProfile.settingsResource; } get workspaceSettingsResource(): URI | null { @@ -311,7 +311,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic options = { pinned: true, revealIfOpened: true, ...options }; if (textual) { const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + '\n[\n]'; - const editableKeybindings = this.environmentService.keybindingsResource; + const editableKeybindings = this.userDataProfilesService.defaultProfile.keybindingsResource; const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings'); // Create as needed and open in editor diff --git a/src/vs/workbench/services/profiles/common/settingsProfile.ts b/src/vs/workbench/services/profiles/common/settingsProfile.ts index 906e0891449d9..d6e564c8216df 100644 --- a/src/vs/workbench/services/profiles/common/settingsProfile.ts +++ b/src/vs/workbench/services/profiles/common/settingsProfile.ts @@ -5,10 +5,10 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { Registry } from 'vs/platform/registry/common/platform'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { removeComments, updateIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; import { IResourceProfile, ProfileCreationOptions } from 'vs/workbench/services/profiles/common/profile'; @@ -21,7 +21,7 @@ export class SettingsProfile implements IResourceProfile { constructor( @IFileService private readonly fileService: IFileService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IUserDataSyncUtilService private readonly userDataSyncUtilService: IUserDataSyncUtilService, @ILogService private readonly logService: ILogService, ) { @@ -29,7 +29,7 @@ export class SettingsProfile implements IResourceProfile { async getProfileContent(options?: ProfileCreationOptions): Promise { const ignoredSettings = this.getIgnoredSettings(); - const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.environmentService.settingsResource); + const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfilesService.defaultProfile.settingsResource); const localContent = await this.getLocalFileContent(); let settingsProfileContent = updateIgnoredSettings(localContent || '{}', '{}', ignoredSettings, formattingOptions); if (options?.skipComments) { @@ -45,9 +45,9 @@ export class SettingsProfile implements IResourceProfile { const settingsContent: ISettingsContent = JSON.parse(content); this.logService.trace(`Profile: Applying settings...`); const localSettingsContent = await this.getLocalFileContent(); - const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.environmentService.settingsResource); + const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfilesService.defaultProfile.settingsResource); const contentToUpdate = updateIgnoredSettings(settingsContent.settings, localSettingsContent || '{}', this.getIgnoredSettings(), formattingOptions); - await this.fileService.writeFile(this.environmentService.settingsResource, VSBuffer.fromString(contentToUpdate)); + await this.fileService.writeFile(this.userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(contentToUpdate)); this.logService.info(`Profile: Applied settings`); } @@ -59,7 +59,7 @@ export class SettingsProfile implements IResourceProfile { private async getLocalFileContent(): Promise { try { - const content = await this.fileService.readFile(this.environmentService.settingsResource); + const content = await this.fileService.readFile(this.userDataProfilesService.defaultProfile.settingsResource); return content.value.toString(); } catch (error) { return null; diff --git a/src/vs/workbench/services/userData/browser/userDataInit.ts b/src/vs/workbench/services/userData/browser/userDataInit.ts index b2a85b9e4589d..404c963b5ffcf 100644 --- a/src/vs/workbench/services/userData/browser/userDataInit.ts +++ b/src/vs/workbench/services/userData/browser/userDataInit.ts @@ -37,6 +37,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { TasksInitializer } from 'vs/platform/userDataSync/common/tasksSync'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export const IUserDataInitializationService = createDecorator('IUserDataInitializationService'); export interface IUserDataInitializationService { @@ -62,6 +63,7 @@ export class UserDataInitializationService implements IUserDataInitializationSer @ICredentialsService private readonly credentialsService: ICredentialsService, @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, @IFileService private readonly fileService: IFileService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IStorageService private readonly storageService: IStorageService, @IProductService private readonly productService: IProductService, @IRequestService private readonly requestService: IRequestService, @@ -270,11 +272,11 @@ export class UserDataInitializationService implements IUserDataInitializationSer private createSyncResourceInitializer(syncResource: SyncResource): IUserDataInitializer { switch (syncResource) { - case SyncResource.Settings: return new SettingsInitializer(this.fileService, this.environmentService, this.logService, this.uriIdentityService); - case SyncResource.Keybindings: return new KeybindingsInitializer(this.fileService, this.environmentService, this.logService, this.uriIdentityService); - case SyncResource.Tasks: return new TasksInitializer(this.fileService, this.environmentService, this.logService, this.uriIdentityService); - case SyncResource.Snippets: return new SnippetsInitializer(this.fileService, this.environmentService, this.logService, this.uriIdentityService); - case SyncResource.GlobalState: return new GlobalStateInitializer(this.storageService, this.fileService, this.environmentService, this.logService, this.uriIdentityService); + case SyncResource.Settings: return new SettingsInitializer(this.fileService, this.userDataProfilesService, this.environmentService, this.logService, this.uriIdentityService); + case SyncResource.Keybindings: return new KeybindingsInitializer(this.fileService, this.userDataProfilesService, this.environmentService, this.logService, this.uriIdentityService); + case SyncResource.Tasks: return new TasksInitializer(this.fileService, this.userDataProfilesService, this.environmentService, this.logService, this.uriIdentityService); + case SyncResource.Snippets: return new SnippetsInitializer(this.fileService, this.userDataProfilesService, this.environmentService, this.logService, this.uriIdentityService); + case SyncResource.GlobalState: return new GlobalStateInitializer(this.storageService, this.fileService, this.userDataProfilesService, this.environmentService, this.logService, this.uriIdentityService); } throw new Error(`Cannot create initializer for ${syncResource}`); } @@ -291,11 +293,12 @@ class ExtensionsPreviewInitializer extends AbstractExtensionsInitializer { @IExtensionManagementService extensionManagementService: IExtensionManagementService, @IIgnoredExtensionsManagementService ignoredExtensionsManagementService: IIgnoredExtensionsManagementService, @IFileService fileService: IFileService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IEnvironmentService environmentService: IEnvironmentService, @IUserDataSyncLogService logService: IUserDataSyncLogService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(extensionManagementService, ignoredExtensionsManagementService, fileService, environmentService, logService, uriIdentityService); + super(extensionManagementService, ignoredExtensionsManagementService, fileService, userDataProfilesService, environmentService, logService, uriIdentityService); } getPreview(): Promise { From 3b3a3e3e362dcd1a2390955cd2cca1bfe3318f87 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 19 May 2022 14:08:15 -0700 Subject: [PATCH 174/942] remote: fix wrong event name used for latency (#149973) --- .../workbench/contrib/remote/common/remote.contribution.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index a2d8b9671aeee..7da92791ee6a6 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -243,20 +243,20 @@ class InitialRemoteConnectionHealthContribution implements IWorkbenchContributio await timeout(EXT_HOST_LATENCY_DELAY); } - type RemoteConnectionFailureClassification = { + type RemoteConnectionLatencyClassification = { owner: 'connor4312'; comment: 'The latency to the remote extension host'; web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether this is running on web' }; remoteName: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Anonymized remote name' }; latencyMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Latency to the remote, in milliseconds'; isMeasurement: true }; }; - type RemoteConnectionFailureEvent = { + type RemoteConnectionLatencyEvent = { web: boolean; remoteName: string | undefined; latencyMs: number; }; - this._telemetryService.publicLog2('remoteConnectionFailure', { + this._telemetryService.publicLog2('remoteConnectionLatency', { web: isWeb, remoteName: getRemoteName(this._environmentService.remoteAuthority), latencyMs: bestLatency From 0ee05bb617c5585d9a329e7b34cc6ec9edca10dc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 19 May 2022 14:54:08 -0700 Subject: [PATCH 175/942] Pick up latest markdown-it-katex (#149978) Fixes #149682 --- extensions/markdown-math/yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-math/yarn.lock b/extensions/markdown-math/yarn.lock index 202cceb35b96e..2b979935781f7 100644 --- a/extensions/markdown-math/yarn.lock +++ b/extensions/markdown-math/yarn.lock @@ -4,7 +4,7 @@ "@iktakahiro/markdown-it-katex@mjbvz/markdown-it-katex": version "4.0.1" - resolved "https://codeload.github.com/mjbvz/markdown-it-katex/tar.gz/2e3736e4b916ee64ed92ebfabeaa94643612665a" + resolved "https://codeload.github.com/mjbvz/markdown-it-katex/tar.gz/1e0d09f9174b3ee1537de2586ce8d8a460284ce4" dependencies: katex "^0.13.0" From 55abdaee3e4f3a5ea5f7f95007708e4d50543339 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 19 May 2022 15:12:43 -0700 Subject: [PATCH 176/942] Render outputs a viewport above as well as below (#149879) * Also render outputs/markdowns a viewport above the current viewport, as well as below Towards #148164 * Update docs and add background color to svg --- build/hygiene.js | 2 +- .../viewportCustomMarkdown.ts | 2 +- .../cell-resize-above-viewport.drawio.svg | 406 +++++++++--------- .../notebook/browser/docs/notebook.layout.md | 5 +- .../notebook/browser/notebookBrowser.ts | 2 +- .../notebook/browser/notebookEditorWidget.ts | 4 +- .../notebook/browser/view/notebookCellList.ts | 11 +- .../browser/view/notebookRenderingCommon.ts | 2 +- 8 files changed, 217 insertions(+), 217 deletions(-) diff --git a/build/hygiene.js b/build/hygiene.js index a5d2b08017271..1668e508ec9ec 100644 --- a/build/hygiene.js +++ b/build/hygiene.js @@ -54,7 +54,7 @@ function hygiene(some, linting = true) { const m = /([^\t\n\r\x20-\x7E⊃⊇✔︎✓🎯⚠️🛑🔴🚗🚙🚕🎉✨❗⇧⌥⌘×÷¦⋯…↑↓→→←↔⟷·•●◆▼⟪⟫┌└├⏎↩√φ]+)/g.exec(line); if (m) { console.error( - file.relative + `(${i + 1},${m.index + 1}): Unexpected unicode character: "${m[0]}". To suppress, use // allow-any-unicode-next-line` + file.relative + `(${i + 1},${m.index + 1}): Unexpected unicode character: "${m[0]}" (charCode: ${m[0].charCodeAt(0)}). To suppress, use // allow-any-unicode-next-line` ); errorCount++; } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/viewportCustomMarkdown/viewportCustomMarkdown.ts b/src/vs/workbench/contrib/notebook/browser/contrib/viewportCustomMarkdown/viewportCustomMarkdown.ts index 81fd6185e5948..212b41dbdb52f 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/viewportCustomMarkdown/viewportCustomMarkdown.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/viewportCustomMarkdown/viewportCustomMarkdown.ts @@ -70,7 +70,7 @@ class NotebookViewportContribution extends Disposable implements INotebookEditor return; } - const visibleRanges = this._notebookEditor.getVisibleRangesPlusViewportBelow(); + const visibleRanges = this._notebookEditor.getVisibleRangesPlusViewportAboveAndBelow(); cellRangesToIndexes(visibleRanges).forEach(index => { const cell = this._notebookEditor.cellAt(index); diff --git a/src/vs/workbench/contrib/notebook/browser/docs/cell-resize-above-viewport.drawio.svg b/src/vs/workbench/contrib/notebook/browser/docs/cell-resize-above-viewport.drawio.svg index 909b8665213bc..6a2f80fc98da0 100644 --- a/src/vs/workbench/contrib/notebook/browser/docs/cell-resize-above-viewport.drawio.svg +++ b/src/vs/workbench/contrib/notebook/browser/docs/cell-resize-above-viewport.drawio.svg @@ -1,42 +1,42 @@ - + - - - - - - - - + + + + + + + + - - + + - - + + - - + + - +
    -
    -
    +
    +
    Notebook List View
    - + Notebook List View @@ -44,86 +44,86 @@ - +
    -
    -
    +
    +
    Webview top -1000
    - + Webview top -1000 - - + + - +
    -
    -
    +
    +
    Viewport
    - + Viewport - - - - + + + + - +
    -
    -
    +
    +
    Notebook List View
    - + Notebook List View - - + + - - + + - + - + - +
    -
    -
    +
    +
    Grow Height by 50
    - + Grow Height by 50 @@ -131,16 +131,16 @@ - +
    -
    -
    +
    +
    scrollTop 1000
    - + scrollTop 1000 @@ -148,172 +148,172 @@ - +
    -
    -
    +
    +
    scrollTop 1000
    - + scrollTop 1000 - - + + - - - - + + + + - +
    -
    -
    +
    +
    Notebook List View
    - + Notebook List View - - + + - - + + - + - + - +
    -
    -
    +
    +
    scrollTop 1050
    - + scrollTop 1050 - - + + - - + + - - + + - - + + - - - - + + + + - +
    -
    -
    +
    +
    Notebook List View
    - + Notebook List View - - + + - - + + - + - + - +
    -
    -
    +
    +
    scrollTop 1050
    - + scrollTop 1050 - - + + - +
    -
    -
    +
    +
    Adjust top
    - + Adjust top - - + + - +
    -
    -
    +
    +
    UpdateScrollTop
    - + UpdateScrollTop @@ -323,16 +323,16 @@ - +
    -
    -
    +
    +
    Webview top -1000
    - + Webview top -1000 @@ -340,16 +340,16 @@ - +
    -
    -
    +
    +
    Webview top -1000
    - + Webview top -1000 @@ -357,16 +357,16 @@ - +
    -
    -
    +
    +
    Webview top -1000
    - + Webview top -1000 @@ -374,55 +374,55 @@ - - + + - - - - + + + + - +
    -
    -
    +
    +
    Notebook List View
    - + Notebook List View - - + + - - + + - + - + - +
    -
    -
    +
    +
    scrollTop 1050
    - + scrollTop 1050 @@ -430,16 +430,16 @@ - +
    -
    -
    +
    +
    Webview top -950
    - + Webview top -950 @@ -447,55 +447,55 @@ - - + + - - - - + + + + - +
    -
    -
    +
    +
    Notebook List View
    - + Notebook List View - - + + - - + + - + - + - +
    -
    -
    +
    +
    scrollTop 1050
    - + scrollTop 1050 @@ -503,35 +503,35 @@ - +
    -
    -
    +
    +
    Webview top -950
    - + Webview top -950 - - + + - +
    -
    -
    +
    +
    Adjust top
    - + Adjust top @@ -543,16 +543,16 @@ - +
    -
    -
    +
    +
    1
    - + 1 @@ -560,16 +560,16 @@ - +
    -
    -
    +
    +
    2
    - + 2 @@ -577,16 +577,16 @@ - +
    -
    -
    +
    +
    3
    - + 3 @@ -594,16 +594,16 @@ - +
    -
    -
    +
    +
    4
    - + 4 @@ -611,16 +611,16 @@ - +
    -
    -
    +
    +
    4'
    - + 4' @@ -628,16 +628,16 @@ - +
    -
    -
    +
    +
    5
    - + 5 diff --git a/src/vs/workbench/contrib/notebook/browser/docs/notebook.layout.md b/src/vs/workbench/contrib/notebook/browser/docs/notebook.layout.md index b289f68fd3483..eb757a5a366b5 100644 --- a/src/vs/workbench/contrib/notebook/browser/docs/notebook.layout.md +++ b/src/vs/workbench/contrib/notebook/browser/docs/notebook.layout.md @@ -8,7 +8,6 @@ The notebook editor is a virtualized list view rendered in two contexts (mainfra - [Optimizations](#optimizations) - [Avoid flickering on resize of cells above current viewport](#avoid-flickering-on-resize-of-cells-above-current-viewport) - [Executing code cell followed by markdown cells](#executing-code-cell-followed-by-markdown-cells) - - [What's the catch](#whats-the-catch) - [Re-executing code cell followed by markdown cells](#re-executing-code-cell-followed-by-markdown-cells) - [Scrolling](#scrolling) @@ -172,6 +171,6 @@ If the new output is rendered within 200ms, users won't see the UI movement. ## Scrolling -Code cell outputs and markdown cells are rendered in the webview, which are async in nature. In order to have the cell outputs and markdown previews rendered when users scroll to them, we send rendering requests of cells in the next viewport when it's idle. Thus scrolling downwards is smoother. +Code cell outputs and markdown cells are rendered in the webview, which are async in nature. In order to have the cell outputs and markdown previews rendered when users scroll to them, we send rendering requests of cells in the next and previous viewport when it's idle. Thus scrolling is smoother. -However, we **don't** warmup the previous viewport as the cell height change of previous viewport might trigger the flickering of markdown cells in current viewport. Before we optimize this, do not do any warmup of cells before current viewport. +We also warm up all rendered markdown in the document, from top to bottom, when the browser is idle. We don't warm up outputs like this, since they can be more expensive to render than markdown. But outputs do get rendered when the user starts a search in outputs. diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 56012a1a0419a..75452af0bdeb4 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -442,7 +442,7 @@ export interface INotebookEditor { */ getLayoutInfo(): NotebookLayoutInfo; - getVisibleRangesPlusViewportBelow(): ICellRange[]; + getVisibleRangesPlusViewportAboveAndBelow(): ICellRange[]; /** * Focus the container of a cell (the monaco editor inside is not focused). diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 535e00b2aa655..8567bdded40a3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2088,8 +2088,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return this._listViewInfoAccessor.setHiddenAreas(_ranges); } - getVisibleRangesPlusViewportBelow(): ICellRange[] { - return this._listViewInfoAccessor.getVisibleRangesPlusViewportBelow(); + getVisibleRangesPlusViewportAboveAndBelow(): ICellRange[] { + return this._listViewInfoAccessor.getVisibleRangesPlusViewportAboveAndBelow(); } setScrollTop(scrollTop: number) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 859e007d6c9b2..0318aa6a2888b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -600,15 +600,16 @@ export class NotebookCellList extends WorkbenchList implements ID return reduceCellRanges(ranges); } - getVisibleRangesPlusViewportBelow() { + getVisibleRangesPlusViewportAboveAndBelow() { if (this.view.length <= 0) { return []; } - const bottom = clamp(this.getViewScrollBottom() + this.renderHeight, 0, this.scrollHeight); - const topViewIndex = this.firstVisibleIndex; + const top = Math.max(this.getViewScrollTop() - this.renderHeight, 0); + const topViewIndex = this.view.indexAt(top); const topElement = this.view.element(topViewIndex); const topModelIndex = this._viewModel!.getCellIndex(topElement); + const bottom = clamp(this.getViewScrollBottom() + this.renderHeight, 0, this.scrollHeight); const bottomViewIndex = clamp(this.view.indexAt(bottom), 0, this.view.length - 1); const bottomElement = this.view.element(bottomViewIndex); const bottomModelIndex = this._viewModel!.getCellIndex(bottomElement); @@ -1584,8 +1585,8 @@ export class ListViewInfoAccessor extends Disposable { return this.list.setHiddenAreas(_ranges, true); } - getVisibleRangesPlusViewportBelow(): ICellRange[] { - return this.list?.getVisibleRangesPlusViewportBelow() ?? []; + getVisibleRangesPlusViewportAboveAndBelow(): ICellRange[] { + return this.list?.getVisibleRangesPlusViewportAboveAndBelow() ?? []; } triggerScroll(event: IMouseWheelEvent) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 0eb419e6d32dd..0bdb4ff330f4e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -53,7 +53,7 @@ export interface INotebookCellList { getViewIndex2(modelIndex: number): number | undefined; getModelIndex(cell: CellViewModel): number | undefined; getModelIndex2(viewIndex: number): number | undefined; - getVisibleRangesPlusViewportBelow(): ICellRange[]; + getVisibleRangesPlusViewportAboveAndBelow(): ICellRange[]; focusElement(element: ICellViewModel): void; selectElements(elements: ICellViewModel[]): void; getFocusedElements(): ICellViewModel[]; From 08f247b5526de7a73aa9a743166bbb95fbb9146e Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 19 May 2022 15:50:55 -0700 Subject: [PATCH 177/942] debug: bump js profile visualizer (#149981) --- product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/product.json b/product.json index e80c93598d545..ae841799a0944 100644 --- a/product.json +++ b/product.json @@ -61,7 +61,7 @@ }, { "name": "ms-vscode.vscode-js-profile-table", - "version": "1.0.1", + "version": "1.0.2", "repo": "https://github.com/microsoft/vscode-js-profile-visualizer", "metadata": { "id": "7e52b41b-71ad-457b-ab7e-0620f1fc4feb", From 17c75e11805103026b943c8dc4d50a35f245f76f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 19 May 2022 16:07:14 -0700 Subject: [PATCH 178/942] Allow reusing webview origins across reloads (#149950) * Allow reusing webview origins across reloads Currently webviews are always loaded into a unique origin. This keeps them isolated but also means that we can't benefit from caching across window reloads This change adds a new `origin` option that you can pass to webviews which controls the origin they use. If this origin is not provided, we use a random one instead We then save off this origin for webview panels and restore it on window reloads. Webviews restore a little faster on window reload * Update webview fallback version --- product.json | 2 +- .../api/browser/mainThreadCustomEditors.ts | 1 + .../browser/customEditorInputFactory.ts | 5 ++++- .../contrib/webview/browser/overlayWebview.ts | 12 +++++++++++- .../workbench/contrib/webview/browser/pre/main.js | 4 ++-- src/vs/workbench/contrib/webview/browser/webview.ts | 8 ++++++++ .../contrib/webview/browser/webviewElement.ts | 13 +++++++++++-- .../browser/webviewEditorInputSerializer.ts | 4 ++++ .../environment/browser/environmentService.ts | 2 +- test/integration/browser/src/index.ts | 2 +- 10 files changed, 44 insertions(+), 9 deletions(-) diff --git a/product.json b/product.json index ae841799a0944..ca0f29df1daa5 100644 --- a/product.json +++ b/product.json @@ -27,7 +27,7 @@ "licenseFileName": "LICENSE.txt", "reportIssueUrl": "https://github.com/microsoft/vscode/issues/new", "urlProtocol": "code-oss", - "webviewContentExternalBaseUrlTemplate": "https://{{uuid}}.vscode-cdn.net/insider/181b43c0e2949e36ecb623d8cc6de29d4fa2bae8/out/vs/workbench/contrib/webview/browser/pre/", + "webviewContentExternalBaseUrlTemplate": "https://{{uuid}}.vscode-cdn.net/insider/3c8520fab514b9f56070214496b26ff68d1b1cb5/out/vs/workbench/contrib/webview/browser/pre/", "builtInExtensions": [ { "name": "ms-vscode.js-debug-companion", diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 6f247115e343d..3be47943f1a40 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -662,6 +662,7 @@ class MainThreadCustomEditorModel extends ResourceWorkingCopy implements ICustom } : undefined, webview: { id: primaryEditor.id, + origin: primaryEditor.webview.origin, options: primaryEditor.webview.options, state: primaryEditor.webview.state, } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts index a4a7f7eea7387..a1670e79b80ca 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts @@ -32,6 +32,7 @@ export interface CustomDocumentBackupData extends IWorkingCopyBackupMeta { readonly webview: { readonly id: string; + readonly origin: string | undefined; readonly options: SerializedWebviewOptions; readonly state: any; }; @@ -107,9 +108,10 @@ export class CustomEditorInputSerializer extends WebviewEditorInputSerializer { } } -function reviveWebview(webviewService: IWebviewService, data: { id: string; state: any; webviewOptions: WebviewOptions; contentOptions: WebviewContentOptions; extension?: WebviewExtensionDescription }) { +function reviveWebview(webviewService: IWebviewService, data: { id: string; origin: string | undefined; state: any; webviewOptions: WebviewOptions; contentOptions: WebviewContentOptions; extension?: WebviewExtensionDescription }) { const webview = webviewService.createWebviewOverlay({ id: data.id, + origin: data.origin, options: { purpose: WebviewContentPurpose.CustomEditor, enableFindWidget: data.webviewOptions.enableFindWidget, @@ -185,6 +187,7 @@ export class ComplexCustomWorkingCopyEditorHandler extends Disposable implements const extension = reviveWebviewExtensionDescription(backupData.extension?.id, backupData.extension?.location); const webview = reviveWebview(this._webviewService, { id, + origin: backupData.webview.origin, webviewOptions: restoreWebviewOptions(backupData.webview.options), contentOptions: restoreWebviewContentOptions(backupData.webview.options), state: backupData.webview.state, diff --git a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts index b1a228fc65c33..8c6cc98b2f49d 100644 --- a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts +++ b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts @@ -8,6 +8,7 @@ import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; @@ -43,6 +44,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { private _findWidgetEnabled: IContextKey | undefined; public readonly id: string; + public readonly origin: string; public constructor( initInfo: WebviewInitInfo, @@ -53,6 +55,8 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { super(); this.id = initInfo.id; + this.origin = initInfo.origin ?? generateUuid(); + this._extension = initInfo.extension; this._options = initInfo.options; this._contentOptions = initInfo.contentOptions; @@ -186,7 +190,13 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { } if (!this._webview.value) { - const webview = this._webviewService.createWebviewElement({ id: this.id, options: this._options, contentOptions: this._contentOptions, extension: this.extension }); + const webview = this._webviewService.createWebviewElement({ + id: this.id, + origin: this.origin, + options: this._options, + contentOptions: this._contentOptions, + extension: this.extension, + }); this._webview.value = webview; webview.state = this._state; diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index d2c57f4aa1063..74384c1550f4a 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -20,6 +20,7 @@ const isFirefox = ( const searchParams = new URL(location.toString()).searchParams; const ID = searchParams.get('id'); +const webviewOrigin = searchParams.get('origin'); const onElectron = searchParams.get('platform') === 'electron'; const expectedWorkerVersion = parseInt(searchParams.get('swVersion')); @@ -314,7 +315,6 @@ const hostMessaging = new class HostMessaging { }; const parentOrigin = searchParams.get('parentOrigin'); - const id = searchParams.get('id'); const hostname = location.hostname; @@ -327,7 +327,7 @@ const hostMessaging = new class HostMessaging { // compute a sha-256 composed of `parentOrigin` and `salt` converted to base 32 let parentOriginHash; try { - const strData = JSON.stringify({ parentOrigin, salt: id }); + const strData = JSON.stringify({ parentOrigin, salt: webviewOrigin }); const encoder = new TextEncoder(); const arrData = encoder.encode(strData); const hash = await crypto.subtle.digest('sha-256', arrData); diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 3d50fee82a08e..e9e90223d4ee3 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -151,8 +151,16 @@ export interface WebviewMessageReceivedEvent { export interface IWebview extends IDisposable { + /** + * External identifier of this webview. + */ readonly id: string; + /** + * The origin this webview itself is loaded from. May not be unique + */ + readonly origin: string; + html: string; contentOptions: WebviewContentOptions; localResourcesRoot: readonly URI[]; diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index fe286d9a83200..77aaf6683f95c 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -102,6 +102,7 @@ namespace WebviewState { export interface WebviewInitInfo { readonly id: string; + readonly origin?: string; readonly options: WebviewOptions; readonly contentOptions: WebviewContentOptions; @@ -118,7 +119,12 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD public readonly id: string; /** - * Unique identifier of this webview iframe element. + * The origin this webview itself is loaded from. May not be unique + */ + public readonly origin: string; + + /** + * Unique internal identifier of this webview's iframe element. */ private readonly iframeId: string; @@ -194,7 +200,9 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD this.id = initInfo.id; this.iframeId = generateUuid(); - this.encodedWebviewOriginPromise = parentOriginHash(window.origin, this.iframeId).then(id => this.encodedWebviewOrigin = id); + this.origin = initInfo.origin ?? this.iframeId; + + this.encodedWebviewOriginPromise = parentOriginHash(window.origin, this.origin).then(id => this.encodedWebviewOrigin = id); this.options = initInfo.options; this.extension = initInfo.extension; @@ -484,6 +492,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD // The extensionId and purpose in the URL are used for filtering in js-debug: const params: { [key: string]: string } = { id: this.iframeId, + origin: this.origin, swVersion: String(this._expectedServiceWorkerVersion), extensionId: extension?.id.value ?? '', platform: this.platform, diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.ts index abed8dcdb1642..ec4f4c9ef915f 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInputSerializer.ts @@ -21,6 +21,7 @@ interface SerializedIconPath { export interface SerializedWebview { readonly id: string; + readonly origin: string | undefined; readonly viewType: string; readonly title: string; readonly options: SerializedWebviewOptions; @@ -33,6 +34,7 @@ export interface SerializedWebview { export interface DeserializedWebview { readonly id: string; + readonly origin: string | undefined; readonly viewType: string; readonly title: string; readonly webviewOptions: WebviewOptions; @@ -76,6 +78,7 @@ export class WebviewEditorInputSerializer implements IEditorSerializer { return this._webviewWorkbenchService.reviveWebview({ webviewInitInfo: { id: data.id, + origin: data.origin, options: data.webviewOptions, contentOptions: data.contentOptions, extension: data.extension, @@ -102,6 +105,7 @@ export class WebviewEditorInputSerializer implements IEditorSerializer { protected toJson(input: WebviewInput): SerializedWebview { return { id: input.id, + origin: input.webview.origin, viewType: input.viewType, title: input.getName(), options: { ...input.webview.options, ...input.webview.contentOptions }, diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index d41839b87ba62..f205de47189b4 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -189,7 +189,7 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi const webviewExternalEndpointCommit = this.payload?.get('webviewExternalEndpointCommit'); return endpoint - .replace('{{commit}}', webviewExternalEndpointCommit ?? this.productService.commit ?? '181b43c0e2949e36ecb623d8cc6de29d4fa2bae8') + .replace('{{commit}}', webviewExternalEndpointCommit ?? this.productService.commit ?? '3c8520fab514b9f56070214496b26ff68d1b1cb5') .replace('{{quality}}', (webviewExternalEndpointCommit ? 'insider' : this.productService.quality) ?? 'insider'); } diff --git a/test/integration/browser/src/index.ts b/test/integration/browser/src/index.ts index 18350685d5ea2..39a996b6ff48d 100644 --- a/test/integration/browser/src/index.ts +++ b/test/integration/browser/src/index.ts @@ -65,7 +65,7 @@ async function runTestsInBrowser(browserType: BrowserType, endpoint: url.UrlWith const testExtensionUri = url.format({ pathname: URI.file(path.resolve(optimist.argv.extensionDevelopmentPath)).path, protocol, host, slashes: true }); const testFilesUri = url.format({ pathname: URI.file(path.resolve(optimist.argv.extensionTestsPath)).path, protocol, host, slashes: true }); - const payloadParam = `[["extensionDevelopmentPath","${testExtensionUri}"],["extensionTestsPath","${testFilesUri}"],["enableProposedApi",""],["webviewExternalEndpointCommit","181b43c0e2949e36ecb623d8cc6de29d4fa2bae8"],["skipWelcome","true"]]`; + const payloadParam = `[["extensionDevelopmentPath","${testExtensionUri}"],["extensionTestsPath","${testFilesUri}"],["enableProposedApi",""],["webviewExternalEndpointCommit","3c8520fab514b9f56070214496b26ff68d1b1cb5"],["skipWelcome","true"]]`; if (path.extname(testWorkspacePath) === '.code-workspace') { await page.goto(`${endpoint.href}&workspace=${testWorkspacePath}&payload=${payloadParam}`); From e9393b096d90a5ac49b1528d1ec60483fc6b993e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 20 May 2022 00:07:20 -0700 Subject: [PATCH 179/942] Inline webview main.js (#149983) For #145567 This inlines the `main.js` script that webview use. This saves us an extra request at the cost of currently having to maintain two copies of the source from `main.js` --- .../electron-main/webviewProtocolProvider.ts | 1 - .../webview/browser/pre/index-no-csp.html | 1131 +++++++++++++++- .../contrib/webview/browser/pre/index.html | 1134 ++++++++++++++++- .../contrib/webview/browser/pre/main.js | 1133 ---------------- 4 files changed, 2262 insertions(+), 1137 deletions(-) delete mode 100644 src/vs/workbench/contrib/webview/browser/pre/main.js diff --git a/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts b/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts index a0c161312fbf0..3da845d011a61 100644 --- a/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts +++ b/src/vs/platform/webview/electron-main/webviewProtocolProvider.ts @@ -14,7 +14,6 @@ export class WebviewProtocolProvider extends Disposable { private static validWebviewFilePaths = new Map([ ['/index.html', 'index.html'], ['/fake.html', 'fake.html'], - ['/main.js', 'main.js'], ['/service-worker.js', 'service-worker.js'], ]); diff --git a/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html b/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html index 6885c2488cdc8..625c46f31153c 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html +++ b/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html @@ -12,7 +12,1136 @@ - + diff --git a/src/vs/workbench/contrib/webview/browser/pre/index.html b/src/vs/workbench/contrib/webview/browser/pre/index.html index 3f2ade8478f25..457447af38e54 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/index.html +++ b/src/vs/workbench/contrib/webview/browser/pre/index.html @@ -4,7 +4,8 @@ - + - + diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js deleted file mode 100644 index 74384c1550f4a..0000000000000 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ /dev/null @@ -1,1133 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -// @ts-check - -/// - -const isSafari = ( - navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && - navigator.userAgent && - navigator.userAgent.indexOf('CriOS') === -1 && - navigator.userAgent.indexOf('FxiOS') === -1 -); - -const isFirefox = ( - navigator.userAgent && - navigator.userAgent.indexOf('Firefox') >= 0 -); - -const searchParams = new URL(location.toString()).searchParams; -const ID = searchParams.get('id'); -const webviewOrigin = searchParams.get('origin'); -const onElectron = searchParams.get('platform') === 'electron'; -const expectedWorkerVersion = parseInt(searchParams.get('swVersion')); - -/** - * Use polling to track focus of main webview and iframes within the webview - * - * @param {Object} handlers - * @param {() => void} handlers.onFocus - * @param {() => void} handlers.onBlur - */ -const trackFocus = ({ onFocus, onBlur }) => { - const interval = 250; - let isFocused = document.hasFocus(); - setInterval(() => { - const isCurrentlyFocused = document.hasFocus(); - if (isCurrentlyFocused === isFocused) { - return; - } - isFocused = isCurrentlyFocused; - if (isCurrentlyFocused) { - onFocus(); - } else { - onBlur(); - } - }, interval); -}; - -const getActiveFrame = () => { - return /** @type {HTMLIFrameElement | undefined} */ (document.getElementById('active-frame')); -}; - -const getPendingFrame = () => { - return /** @type {HTMLIFrameElement | undefined} */ (document.getElementById('pending-frame')); -}; - -/** - * @template T - * @param {T | undefined | null} obj - * @return {T} - */ -function assertIsDefined(obj) { - if (typeof obj === 'undefined' || obj === null) { - throw new Error('Found unexpected null'); - } - return obj; -} - -const vscodePostMessageFuncName = '__vscode_post_message__'; - -const defaultStyles = document.createElement('style'); -defaultStyles.id = '_defaultStyles'; -defaultStyles.textContent = ` - html { - scrollbar-color: var(--vscode-scrollbarSlider-background) var(--vscode-editor-background); - } - - body { - background-color: transparent; - color: var(--vscode-editor-foreground); - font-family: var(--vscode-font-family); - font-weight: var(--vscode-font-weight); - font-size: var(--vscode-font-size); - margin: 0; - padding: 0 20px; - } - - img { - max-width: 100%; - max-height: 100%; - } - - a, a code { - color: var(--vscode-textLink-foreground); - } - - a:hover { - color: var(--vscode-textLink-activeForeground); - } - - a:focus, - input:focus, - select:focus, - textarea:focus { - outline: 1px solid -webkit-focus-ring-color; - outline-offset: -1px; - } - - code { - color: var(--vscode-textPreformat-foreground); - } - - blockquote { - background: var(--vscode-textBlockQuote-background); - border-color: var(--vscode-textBlockQuote-border); - } - - kbd { - color: var(--vscode-editor-foreground); - border-radius: 3px; - vertical-align: middle; - padding: 1px 3px; - - background-color: hsla(0,0%,50%,.17); - border: 1px solid rgba(71,71,71,.4); - border-bottom-color: rgba(88,88,88,.4); - box-shadow: inset 0 -1px 0 rgba(88,88,88,.4); - } - .vscode-light kbd { - background-color: hsla(0,0%,87%,.5); - border: 1px solid hsla(0,0%,80%,.7); - border-bottom-color: hsla(0,0%,73%,.7); - box-shadow: inset 0 -1px 0 hsla(0,0%,73%,.7); - } - - ::-webkit-scrollbar { - width: 10px; - height: 10px; - } - - ::-webkit-scrollbar-corner { - background-color: var(--vscode-editor-background); - } - - ::-webkit-scrollbar-thumb { - background-color: var(--vscode-scrollbarSlider-background); - } - ::-webkit-scrollbar-thumb:hover { - background-color: var(--vscode-scrollbarSlider-hoverBackground); - } - ::-webkit-scrollbar-thumb:active { - background-color: var(--vscode-scrollbarSlider-activeBackground); - } - ::highlight(find-highlight) { - background-color: var(--vscode-editor-findMatchHighlightBackground); - } - ::highlight(current-find-highlight) { - background-color: var(--vscode-editor-findMatchBackground); - }`; - -/** - * @param {boolean} allowMultipleAPIAcquire - * @param {*} [state] - * @return {string} - */ -function getVsCodeApiScript(allowMultipleAPIAcquire, state) { - const encodedState = state ? encodeURIComponent(state) : undefined; - return /* js */` - globalThis.acquireVsCodeApi = (function() { - const originalPostMessage = window.parent['${vscodePostMessageFuncName}'].bind(window.parent); - const doPostMessage = (channel, data, transfer) => { - originalPostMessage(channel, data, transfer); - }; - - let acquired = false; - - let state = ${state ? `JSON.parse(decodeURIComponent("${encodedState}"))` : undefined}; - - return () => { - if (acquired && !${allowMultipleAPIAcquire}) { - throw new Error('An instance of the VS Code API has already been acquired'); - } - acquired = true; - return Object.freeze({ - postMessage: function(message, transfer) { - doPostMessage('onmessage', { message, transfer }, transfer); - }, - setState: function(newState) { - state = newState; - doPostMessage('do-update-state', JSON.stringify(newState)); - return newState; - }, - getState: function() { - return state; - } - }); - }; - })(); - delete window.parent; - delete window.top; - delete window.frameElement; - `; -} - -/** @type {Promise} */ -const workerReady = new Promise((resolve, reject) => { - if (!areServiceWorkersEnabled()) { - return reject(new Error('Service Workers are not enabled. Webviews will not work. Try disabling private/incognito mode.')); - } - - const swPath = `service-worker.js?v=${expectedWorkerVersion}&vscode-resource-base-authority=${searchParams.get('vscode-resource-base-authority')}&remoteAuthority=${searchParams.get('remoteAuthority') ?? ''}`; - navigator.serviceWorker.register(swPath) - .then(() => navigator.serviceWorker.ready) - .then(async registration => { - /** - * @param {MessageEvent} event - */ - const versionHandler = async (event) => { - if (event.data.channel !== 'version') { - return; - } - - navigator.serviceWorker.removeEventListener('message', versionHandler); - if (event.data.version === expectedWorkerVersion) { - return resolve(); - } else { - console.log(`Found unexpected service worker version. Found: ${event.data.version}. Expected: ${expectedWorkerVersion}`); - console.log(`Attempting to reload service worker`); - - // If we have the wrong version, try once (and only once) to unregister and re-register - // Note that `.update` doesn't seem to work desktop electron at the moment so we use - // `unregister` and `register` here. - return registration.unregister() - .then(() => navigator.serviceWorker.register(swPath)) - .then(() => navigator.serviceWorker.ready) - .finally(() => { resolve(); }); - } - }; - navigator.serviceWorker.addEventListener('message', versionHandler); - - const postVersionMessage = (/** @type {ServiceWorker} */ controller) => { - controller.postMessage({ channel: 'version' }); - }; - - // At this point, either the service worker is ready and - // became our controller, or we need to wait for it. - // Note that navigator.serviceWorker.controller could be a - // controller from a previously loaded service worker. - const currentController = navigator.serviceWorker.controller; - if (currentController?.scriptURL.endsWith(swPath)) { - // service worker already loaded & ready to receive messages - postVersionMessage(currentController); - } else { - // either there's no controlling service worker, or it's an old one: - // wait for it to change before posting the message - const onControllerChange = () => { - navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange); - postVersionMessage(navigator.serviceWorker.controller); - }; - navigator.serviceWorker.addEventListener('controllerchange', onControllerChange); - } - }).catch(error => { - reject(new Error(`Could not register service workers: ${error}.`)); - }); -}); - -const hostMessaging = new class HostMessaging { - - constructor() { - this.channel = new MessageChannel(); - - /** @type {Map void>>} */ - this.handlers = new Map(); - - this.channel.port1.onmessage = (e) => { - const channel = e.data.channel; - const handlers = this.handlers.get(channel); - if (handlers) { - for (const handler of handlers) { - handler(e, e.data.args); - } - } else { - console.log('no handler for ', e); - } - }; - } - - /** - * @param {string} channel - * @param {any} data - * @param {any} [transfer] - */ - postMessage(channel, data, transfer) { - this.channel.port1.postMessage({ channel, data }, transfer); - } - - /** - * @param {string} channel - * @param {(event: MessageEvent, data: any) => void} handler - */ - onMessage(channel, handler) { - let handlers = this.handlers.get(channel); - if (!handlers) { - handlers = []; - this.handlers.set(channel, handlers); - } - handlers.push(handler); - } - - async signalReady() { - const start = (/** @type {string} */ parentOrigin) => { - window.parent.postMessage({ target: ID, channel: 'webview-ready', data: {} }, parentOrigin, [this.channel.port2]); - }; - - const parentOrigin = searchParams.get('parentOrigin'); - - const hostname = location.hostname; - - if (!crypto.subtle) { - // cannot validate, not running in a secure context - throw new Error(`Cannot validate in current context!`); - } - - // Here the `parentOriginHash()` function from `src/vs/workbench/common/webview.ts` is inlined - // compute a sha-256 composed of `parentOrigin` and `salt` converted to base 32 - let parentOriginHash; - try { - const strData = JSON.stringify({ parentOrigin, salt: webviewOrigin }); - const encoder = new TextEncoder(); - const arrData = encoder.encode(strData); - const hash = await crypto.subtle.digest('sha-256', arrData); - const hashArray = Array.from(new Uint8Array(hash)); - const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); - // sha256 has 256 bits, so we need at most ceil(lg(2^256-1)/lg(32)) = 52 chars to represent it in base 32 - parentOriginHash = BigInt(`0x${hashHex}`).toString(32).padStart(52, '0'); - } catch (err) { - throw err instanceof Error ? err : new Error(String(err)); - } - - if (hostname === parentOriginHash || hostname.startsWith(parentOriginHash + '.')) { - // validation succeeded! - return start(parentOrigin); - } - - throw new Error(`Expected '${parentOriginHash}' as hostname or subdomain!`); - } -}(); - -const unloadMonitor = new class { - - constructor() { - this.confirmBeforeClose = 'keyboardOnly'; - this.isModifierKeyDown = false; - - hostMessaging.onMessage('set-confirm-before-close', (_e, /** @type {string} */ data) => { - this.confirmBeforeClose = data; - }); - - hostMessaging.onMessage('content', (_e, /** @type {any} */ data) => { - this.confirmBeforeClose = data.confirmBeforeClose; - }); - - window.addEventListener('beforeunload', (event) => { - if (onElectron) { - return; - } - - switch (this.confirmBeforeClose) { - case 'always': { - event.preventDefault(); - event.returnValue = ''; - return ''; - } - case 'never': { - break; - } - case 'keyboardOnly': - default: { - if (this.isModifierKeyDown) { - event.preventDefault(); - event.returnValue = ''; - return ''; - } - break; - } - } - }); - } - - onIframeLoaded(/** @type {HTMLIFrameElement} */ frame) { - frame.contentWindow.addEventListener('keydown', e => { - this.isModifierKeyDown = e.metaKey || e.ctrlKey || e.altKey; - }); - - frame.contentWindow.addEventListener('keyup', () => { - this.isModifierKeyDown = false; - }); - } -}; - -// state -let firstLoad = true; -/** @type {any} */ -let loadTimeout; -let styleVersion = 0; - -/** @type {Array<{ readonly message: any, transfer?: ArrayBuffer[] }>} */ -let pendingMessages = []; - -const initData = { - /** @type {number | undefined} */ - initialScrollProgress: undefined, - - /** @type {{ [key: string]: string } | undefined} */ - styles: undefined, - - /** @type {string | undefined} */ - activeTheme: undefined, - - /** @type {string | undefined} */ - themeName: undefined, - - /** @type {boolean} */ - screenReader: false, - - /** @type {boolean} */ - reduceMotion: false, -}; - -hostMessaging.onMessage('did-load-resource', (_event, data) => { - navigator.serviceWorker.ready.then(registration => { - assertIsDefined(registration.active).postMessage({ channel: 'did-load-resource', data }, data.data?.buffer ? [data.data.buffer] : []); - }); -}); - -hostMessaging.onMessage('did-load-localhost', (_event, data) => { - navigator.serviceWorker.ready.then(registration => { - assertIsDefined(registration.active).postMessage({ channel: 'did-load-localhost', data }); - }); -}); - -navigator.serviceWorker.addEventListener('message', event => { - switch (event.data.channel) { - case 'load-resource': - case 'load-localhost': - hostMessaging.postMessage(event.data.channel, event.data); - return; - } -}); -/** - * @param {HTMLDocument?} document - * @param {HTMLElement?} body - */ -const applyStyles = (document, body) => { - if (!document) { - return; - } - - if (body) { - body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast', 'vscode-reduce-motion', 'vscode-using-screen-reader'); - if (initData.activeTheme) { - body.classList.add(initData.activeTheme); - } - - if (initData.reduceMotion) { - body.classList.add('vscode-reduce-motion'); - } - - if (initData.screenReader) { - body.classList.add('vscode-using-screen-reader'); - } - - body.dataset.vscodeThemeKind = initData.activeTheme; - body.dataset.vscodeThemeName = initData.themeName || ''; - } - - if (initData.styles) { - const documentStyle = document.documentElement.style; - - // Remove stale properties - for (let i = documentStyle.length - 1; i >= 0; i--) { - const property = documentStyle[i]; - - // Don't remove properties that the webview might have added separately - if (property && property.startsWith('--vscode-')) { - documentStyle.removeProperty(property); - } - } - - // Re-add new properties - for (const variable of Object.keys(initData.styles)) { - documentStyle.setProperty(`--${variable}`, initData.styles[variable]); - } - } -}; - -/** - * @param {MouseEvent} event - */ -const handleInnerClick = (event) => { - if (!event?.view?.document) { - return; - } - - const baseElement = event.view.document.querySelector('base'); - - for (const pathElement of event.composedPath()) { - /** @type {any} */ - const node = pathElement; - if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href) { - if (node.getAttribute('href') === '#') { - event.view.scrollTo(0, 0); - } else if (node.hash && (node.getAttribute('href') === node.hash || (baseElement && node.href === baseElement.href + node.hash))) { - const fragment = node.hash.slice(1); - const scrollTarget = event.view.document.getElementById(fragment) ?? event.view.document.getElementById(decodeURIComponent(fragment)); - scrollTarget?.scrollIntoView(); - } else { - hostMessaging.postMessage('did-click-link', node.href.baseVal || node.href); - } - event.preventDefault(); - return; - } - } -}; - -/** - * @param {MouseEvent} event - */ -const handleAuxClick = (event) => { - // Prevent middle clicks opening a broken link in the browser - if (!event?.view?.document) { - return; - } - - if (event.button === 1) { - for (const pathElement of event.composedPath()) { - /** @type {any} */ - const node = pathElement; - if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href) { - event.preventDefault(); - return; - } - } - } -}; - -/** - * @param {KeyboardEvent} e - */ -const handleInnerKeydown = (e) => { - // If the keypress would trigger a browser event, such as copy or paste, - // make sure we block the browser from dispatching it. Instead VS Code - // handles these events and will dispatch a copy/paste back to the webview - // if needed - if (isUndoRedo(e) || isPrint(e) || isFindEvent(e)) { - e.preventDefault(); - } else if (isCopyPasteOrCut(e)) { - if (onElectron) { - e.preventDefault(); - } else { - return; // let the browser handle this - } - } - - hostMessaging.postMessage('did-keydown', { - key: e.key, - keyCode: e.keyCode, - code: e.code, - shiftKey: e.shiftKey, - altKey: e.altKey, - ctrlKey: e.ctrlKey, - metaKey: e.metaKey, - repeat: e.repeat - }); -}; -/** - * @param {KeyboardEvent} e - */ -const handleInnerUp = (e) => { - hostMessaging.postMessage('did-keyup', { - key: e.key, - keyCode: e.keyCode, - code: e.code, - shiftKey: e.shiftKey, - altKey: e.altKey, - ctrlKey: e.ctrlKey, - metaKey: e.metaKey, - repeat: e.repeat - }); -}; - -/** - * @param {KeyboardEvent} e - * @return {boolean} - */ -function isCopyPasteOrCut(e) { - const hasMeta = e.ctrlKey || e.metaKey; - const shiftInsert = e.shiftKey && e.key.toLowerCase() === 'insert'; - return (hasMeta && ['c', 'v', 'x'].includes(e.key.toLowerCase())) || shiftInsert; -} - -/** - * @param {KeyboardEvent} e - * @return {boolean} - */ -function isUndoRedo(e) { - const hasMeta = e.ctrlKey || e.metaKey; - return hasMeta && ['z', 'y'].includes(e.key.toLowerCase()); -} - -/** - * @param {KeyboardEvent} e - * @return {boolean} - */ -function isPrint(e) { - const hasMeta = e.ctrlKey || e.metaKey; - return hasMeta && e.key.toLowerCase() === 'p'; -} - -/** - * @param {KeyboardEvent} e - * @return {boolean} - */ -function isFindEvent(e) { - const hasMeta = e.ctrlKey || e.metaKey; - return hasMeta && e.key.toLowerCase() === 'f'; -} - -let isHandlingScroll = false; - -/** - * @param {WheelEvent} event - */ -const handleWheel = (event) => { - if (isHandlingScroll) { - return; - } - - hostMessaging.postMessage('did-scroll-wheel', { - deltaMode: event.deltaMode, - deltaX: event.deltaX, - deltaY: event.deltaY, - deltaZ: event.deltaZ, - detail: event.detail, - type: event.type - }); -}; - -/** - * @param {Event} event - */ -const handleInnerScroll = (event) => { - if (isHandlingScroll) { - return; - } - - const target = /** @type {HTMLDocument | null} */ (event.target); - const currentTarget = /** @type {Window | null} */ (event.currentTarget); - if (!currentTarget || !target?.body) { - return; - } - - const progress = currentTarget.scrollY / target.body.clientHeight; - if (isNaN(progress)) { - return; - } - - isHandlingScroll = true; - window.requestAnimationFrame(() => { - try { - hostMessaging.postMessage('did-scroll', progress); - } catch (e) { - // noop - } - isHandlingScroll = false; - }); -}; - -function handleInnerDragStartEvent(/** @type {DragEvent} */ e) { - if (e.defaultPrevented) { - // Extension code has already handled this event - return; - } - - if (!e.dataTransfer || e.shiftKey) { - return; - } - - // Only handle drags from outside editor for now - if (e.dataTransfer.items.length && Array.prototype.every.call(e.dataTransfer.items, item => item.kind === 'file')) { - hostMessaging.postMessage('drag-start'); - } -} - -/** - * @param {() => void} callback - */ -function onDomReady(callback) { - if (document.readyState === 'interactive' || document.readyState === 'complete') { - callback(); - } else { - document.addEventListener('DOMContentLoaded', callback); - } -} - -function areServiceWorkersEnabled() { - try { - return !!navigator.serviceWorker; - } catch (e) { - return false; - } -} - -/** - * @typedef {{ - * contents: string; - * options: { - * readonly allowScripts: boolean; - * readonly allowForms: boolean; - * readonly allowMultipleAPIAcquire: boolean; - * } - * state: any; - * cspSource: string; - * }} ContentUpdateData - */ - -/** - * @param {ContentUpdateData} data - * @return {string} - */ -function toContentHtml(data) { - const options = data.options; - const text = data.contents; - const newDocument = new DOMParser().parseFromString(text, 'text/html'); - - newDocument.querySelectorAll('a').forEach(a => { - if (!a.title) { - const href = a.getAttribute('href'); - if (typeof href === 'string') { - a.title = href; - } - } - }); - - // Set default aria role - if (!newDocument.body.hasAttribute('role')) { - newDocument.body.setAttribute('role', 'document'); - } - - // Inject default script - if (options.allowScripts) { - const defaultScript = newDocument.createElement('script'); - defaultScript.id = '_vscodeApiScript'; - defaultScript.textContent = getVsCodeApiScript(options.allowMultipleAPIAcquire, data.state); - newDocument.head.prepend(defaultScript); - } - - // Inject default styles - newDocument.head.prepend(defaultStyles.cloneNode(true)); - - applyStyles(newDocument, newDocument.body); - - // Strip out unsupported http-equiv tags - for (const metaElement of Array.from(newDocument.querySelectorAll('meta'))) { - const httpEquiv = metaElement.getAttribute('http-equiv'); - if (httpEquiv && !/^(content-security-policy|default-style|content-type)$/i.test(httpEquiv)) { - console.warn(`Removing unsupported meta http-equiv: ${httpEquiv}`); - metaElement.remove(); - } - } - - // Check for CSP - const csp = newDocument.querySelector('meta[http-equiv="Content-Security-Policy"]'); - if (!csp) { - hostMessaging.postMessage('no-csp-found'); - } else { - try { - // Attempt to rewrite CSPs that hardcode old-style resource endpoint - const cspContent = csp.getAttribute('content'); - if (cspContent) { - const newCsp = cspContent.replace(/(vscode-webview-resource|vscode-resource):(?=(\s|;|$))/g, data.cspSource); - csp.setAttribute('content', newCsp); - } - } catch (e) { - console.error(`Could not rewrite csp: ${e}`); - } - } - - // set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off - // and DOCTYPE is needed in the iframe to ensure that the user agent stylesheet is correctly overridden - return '\n' + newDocument.documentElement.outerHTML; -} - -onDomReady(() => { - if (!document.body) { - return; - } - - hostMessaging.onMessage('styles', (_event, data) => { - ++styleVersion; - - initData.styles = data.styles; - initData.activeTheme = data.activeTheme; - initData.themeName = data.themeName; - initData.reduceMotion = data.reduceMotion; - initData.screenReader = data.screenReader; - - const target = getActiveFrame(); - if (!target) { - return; - } - - if (target.contentDocument) { - applyStyles(target.contentDocument, target.contentDocument.body); - } - }); - - // propagate focus - hostMessaging.onMessage('focus', () => { - const activeFrame = getActiveFrame(); - if (!activeFrame || !activeFrame.contentWindow) { - // Focus the top level webview instead - window.focus(); - return; - } - - if (document.activeElement === activeFrame) { - // We are already focused on the iframe (or one of its children) so no need - // to refocus. - return; - } - - activeFrame.contentWindow.focus(); - }); - - // update iframe-contents - let updateId = 0; - hostMessaging.onMessage('content', async (_event, /** @type {ContentUpdateData} */ data) => { - const currentUpdateId = ++updateId; - try { - await workerReady; - } catch (e) { - console.error(`Webview fatal error: ${e}`); - hostMessaging.postMessage('fatal-error', { message: e + '' }); - return; - } - - if (currentUpdateId !== updateId) { - return; - } - - const options = data.options; - const newDocument = toContentHtml(data); - - const initialStyleVersion = styleVersion; - - const frame = getActiveFrame(); - const wasFirstLoad = firstLoad; - // keep current scrollY around and use later - /** @type {(body: HTMLElement, window: Window) => void} */ - let setInitialScrollPosition; - if (firstLoad) { - firstLoad = false; - setInitialScrollPosition = (body, window) => { - if (typeof initData.initialScrollProgress === 'number' && !isNaN(initData.initialScrollProgress)) { - if (window.scrollY === 0) { - window.scroll(0, body.clientHeight * initData.initialScrollProgress); - } - } - }; - } else { - const scrollY = frame && frame.contentDocument && frame.contentDocument.body ? assertIsDefined(frame.contentWindow).scrollY : 0; - setInitialScrollPosition = (body, window) => { - if (window.scrollY === 0) { - window.scroll(0, scrollY); - } - }; - } - - // Clean up old pending frames and set current one as new one - const previousPendingFrame = getPendingFrame(); - if (previousPendingFrame) { - previousPendingFrame.setAttribute('id', ''); - document.body.removeChild(previousPendingFrame); - } - if (!wasFirstLoad) { - pendingMessages = []; - } - - const newFrame = document.createElement('iframe'); - newFrame.setAttribute('id', 'pending-frame'); - newFrame.setAttribute('frameborder', '0'); - - const sandboxRules = new Set(['allow-same-origin', 'allow-pointer-lock']); - if (options.allowScripts) { - sandboxRules.add('allow-scripts'); - sandboxRules.add('allow-downloads'); - } - if (options.allowForms) { - sandboxRules.add('allow-forms'); - } - newFrame.setAttribute('sandbox', Array.from(sandboxRules).join(' ')); - if (!isFirefox) { - newFrame.setAttribute('allow', options.allowScripts ? 'clipboard-read; clipboard-write;' : ''); - } - // We should just be able to use srcdoc, but I wasn't - // seeing the service worker applying properly. - // Fake load an empty on the correct origin and then write real html - // into it to get around this. - newFrame.src = `./fake.html?id=${ID}`; - - newFrame.style.cssText = 'display: block; margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%; visibility: hidden'; - document.body.appendChild(newFrame); - - /** - * @param {Document} contentDocument - */ - function onFrameLoaded(contentDocument) { - // Workaround for https://bugs.chromium.org/p/chromium/issues/detail?id=978325 - setTimeout(() => { - contentDocument.open(); - contentDocument.write(newDocument); - contentDocument.close(); - hookupOnLoadHandlers(newFrame); - - if (initialStyleVersion !== styleVersion) { - applyStyles(contentDocument, contentDocument.body); - } - }, 0); - } - - if (!options.allowScripts && isSafari) { - // On Safari for iframes with scripts disabled, the `DOMContentLoaded` never seems to be fired: https://bugs.webkit.org/show_bug.cgi?id=33604 - // Use polling instead. - const interval = setInterval(() => { - // If the frame is no longer mounted, loading has stopped - if (!newFrame.parentElement) { - clearInterval(interval); - return; - } - - const contentDocument = assertIsDefined(newFrame.contentDocument); - if (contentDocument.location.pathname.endsWith('/fake.html') && contentDocument.readyState !== 'loading') { - clearInterval(interval); - onFrameLoaded(contentDocument); - } - }, 10); - } else { - assertIsDefined(newFrame.contentWindow).addEventListener('DOMContentLoaded', e => { - const contentDocument = e.target ? (/** @type {HTMLDocument} */ (e.target)) : undefined; - onFrameLoaded(assertIsDefined(contentDocument)); - }); - } - - /** - * @param {Document} contentDocument - * @param {Window} contentWindow - */ - const onLoad = (contentDocument, contentWindow) => { - if (contentDocument && contentDocument.body) { - // Workaround for https://github.com/microsoft/vscode/issues/12865 - // check new scrollY and reset if necessary - setInitialScrollPosition(contentDocument.body, contentWindow); - } - - const newFrame = getPendingFrame(); - if (newFrame && newFrame.contentDocument && newFrame.contentDocument === contentDocument) { - const wasFocused = document.hasFocus(); - const oldActiveFrame = getActiveFrame(); - if (oldActiveFrame) { - document.body.removeChild(oldActiveFrame); - } - // Styles may have changed since we created the element. Make sure we re-style - if (initialStyleVersion !== styleVersion) { - applyStyles(newFrame.contentDocument, newFrame.contentDocument.body); - } - newFrame.setAttribute('id', 'active-frame'); - newFrame.style.visibility = 'visible'; - - contentWindow.addEventListener('scroll', handleInnerScroll); - contentWindow.addEventListener('wheel', handleWheel); - - if (wasFocused) { - contentWindow.focus(); - } - - pendingMessages.forEach((message) => { - contentWindow.postMessage(message.message, window.origin, message.transfer); - }); - pendingMessages = []; - } - }; - - /** - * @param {HTMLIFrameElement} newFrame - */ - function hookupOnLoadHandlers(newFrame) { - clearTimeout(loadTimeout); - loadTimeout = undefined; - loadTimeout = setTimeout(() => { - clearTimeout(loadTimeout); - loadTimeout = undefined; - onLoad(assertIsDefined(newFrame.contentDocument), assertIsDefined(newFrame.contentWindow)); - }, 200); - - const contentWindow = assertIsDefined(newFrame.contentWindow); - - contentWindow.addEventListener('load', function (e) { - const contentDocument = /** @type {Document} */ (e.target); - - if (loadTimeout) { - clearTimeout(loadTimeout); - loadTimeout = undefined; - onLoad(contentDocument, this); - } - }); - - // Bubble out various events - contentWindow.addEventListener('click', handleInnerClick); - contentWindow.addEventListener('auxclick', handleAuxClick); - contentWindow.addEventListener('keydown', handleInnerKeydown); - contentWindow.addEventListener('keyup', handleInnerUp); - contentWindow.addEventListener('contextmenu', e => { - if (e.defaultPrevented) { - // Extension code has already handled this event - return; - } - - e.preventDefault(); - hostMessaging.postMessage('did-context-menu', { - clientX: e.clientX, - clientY: e.clientY, - }); - }); - - contentWindow.addEventListener('dragenter', handleInnerDragStartEvent); - contentWindow.addEventListener('dragover', handleInnerDragStartEvent); - - unloadMonitor.onIframeLoaded(newFrame); - } - }); - - // Forward message to the embedded iframe - hostMessaging.onMessage('message', (_event, /** @type {{message: any, transfer?: ArrayBuffer[] }} */ data) => { - const pending = getPendingFrame(); - if (!pending) { - const target = getActiveFrame(); - if (target) { - assertIsDefined(target.contentWindow).postMessage(data.message, window.origin, data.transfer); - return; - } - } - pendingMessages.push(data); - }); - - hostMessaging.onMessage('initial-scroll-position', (_event, progress) => { - initData.initialScrollProgress = progress; - }); - - hostMessaging.onMessage('execCommand', (_event, data) => { - const target = getActiveFrame(); - if (!target) { - return; - } - assertIsDefined(target.contentDocument).execCommand(data); - }); - - /** @type {string | undefined} */ - let lastFindValue = undefined; - - hostMessaging.onMessage('find', (_event, data) => { - const target = getActiveFrame(); - if (!target) { - return; - } - - if (!data.previous && lastFindValue !== data.value) { - // Reset selection so we start search at the head of the last search - const selection = target.contentWindow.getSelection(); - selection.collapse(selection.anchorNode); - } - lastFindValue = data.value; - - const didFind = (/** @type {any} */ (target.contentWindow)).find( - data.value, - /* caseSensitive*/ false, - /* backwards*/ data.previous, - /* wrapAround*/ true, - /* wholeWord */ false, - /* searchInFrames*/ false, - false); - hostMessaging.postMessage('did-find', didFind); - }); - - hostMessaging.onMessage('find-stop', (_event, data) => { - const target = getActiveFrame(); - if (!target) { - return; - } - - lastFindValue = undefined; - - if (!data.clearSelection) { - const selection = target.contentWindow.getSelection(); - for (let i = 0; i < selection.rangeCount; i++) { - selection.removeRange(selection.getRangeAt(i)); - } - } - }); - - trackFocus({ - onFocus: () => hostMessaging.postMessage('did-focus'), - onBlur: () => hostMessaging.postMessage('did-blur') - }); - - (/** @type {any} */ (window))[vscodePostMessageFuncName] = (/** @type {string} */ command, /** @type {any} */ data) => { - switch (command) { - case 'onmessage': - case 'do-update-state': - hostMessaging.postMessage(command, data); - break; - } - }; - - // Also forward events before the contents of the webview have loaded - window.addEventListener('keydown', handleInnerKeydown); - window.addEventListener('dragenter', handleInnerDragStartEvent); - window.addEventListener('dragover', handleInnerDragStartEvent); - - hostMessaging.signalReady(); -}); From a952653a56b90ddada33960cac8e895c22eb7672 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 20 May 2022 09:54:10 +0200 Subject: [PATCH 180/942] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a8ffe4891ff4f..ca6cec1a6bd91 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.68.0", - "distro": "dec8abea6c1191c66ab4a551172264be30a76e26", + "distro": "19bbe01e3aa85625f35f1fb2df6818a8e491670f", "author": { "name": "Microsoft Corporation" }, From fc9732a6833ad9b1ed9e4886dae1fc1e5f8db45e Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 20 May 2022 10:10:39 +0200 Subject: [PATCH 181/942] Spawn the extension host directly from the main process because fork() is now fast again (#150002) --- build/gulpfile.vscode.js | 1 - src/buildfile.js | 4 - src/vs/code/electron-main/app.ts | 4 +- .../extensionHostStarter.ts} | 244 +++++++++--------- .../workerMainProcessExtensionHostStarter.ts | 173 ------------- .../node/extensionHostStarterWorkerMain.ts | 66 ----- 6 files changed, 129 insertions(+), 363 deletions(-) rename src/vs/platform/extensions/{node/extensionHostStarterWorker.ts => electron-main/extensionHostStarter.ts} (78%) delete mode 100644 src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts delete mode 100644 src/vs/platform/extensions/node/extensionHostStarterWorkerMain.ts diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 7b3a5043154da..94b22026a0771 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -65,7 +65,6 @@ const vscodeResources = [ 'out-build/vs/base/browser/ui/codicons/codicon/**', 'out-build/vs/base/parts/sandbox/electron-browser/preload.js', 'out-build/vs/platform/environment/node/userDataPath.js', - 'out-build/vs/platform/extensions/node/extensionHostStarterWorkerMain.js', 'out-build/vs/workbench/browser/media/*-theme.css', 'out-build/vs/workbench/contrib/debug/**/*.json', 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', diff --git a/src/buildfile.js b/src/buildfile.js index 8c30339da6ee1..6b49aa30083ae 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -38,10 +38,6 @@ exports.base = [ }, { name: 'vs/base/common/worker/simpleWorker', - }, - { - name: 'vs/platform/extensions/node/extensionHostStarterWorker', - exclude: ['vs/base/common/worker/simpleWorker'] } ]; diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 4bc4a432ceb9e..651c9ea09e2ec 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -46,7 +46,7 @@ import { getResolvedShellEnv } from 'vs/platform/shell/node/shellEnv'; import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; import { ExtensionUrlTrustService } from 'vs/platform/extensionManagement/node/extensionUrlTrustService'; import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from 'vs/platform/extensions/common/extensionHostStarter'; -import { WorkerMainProcessExtensionHostStarter } from 'vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter'; +import { ExtensionHostStarter } from 'vs/platform/extensions/electron-main/extensionHostStarter'; import { IExternalTerminalMainService } from 'vs/platform/externalTerminal/common/externalTerminal'; import { LinuxExternalTerminalService, MacExternalTerminalService, WindowsExternalTerminalService } from 'vs/platform/externalTerminal/node/externalTerminalService'; import { LOCAL_FILE_SYSTEM_CHANNEL_NAME } from 'vs/platform/files/common/diskFileSystemProviderClient'; @@ -641,7 +641,7 @@ export class CodeApplication extends Disposable { services.set(IExtensionUrlTrustService, new SyncDescriptor(ExtensionUrlTrustService)); // Extension Host Starter - services.set(IExtensionHostStarter, new SyncDescriptor(WorkerMainProcessExtensionHostStarter)); + services.set(IExtensionHostStarter, new SyncDescriptor(ExtensionHostStarter)); // Storage services.set(IStorageMainService, new SyncDescriptor(StorageMainService)); diff --git a/src/vs/platform/extensions/node/extensionHostStarterWorker.ts b/src/vs/platform/extensions/electron-main/extensionHostStarter.ts similarity index 78% rename from src/vs/platform/extensions/node/extensionHostStarterWorker.ts rename to src/vs/platform/extensions/electron-main/extensionHostStarter.ts index e0f8d8b03e451..493168507e84f 100644 --- a/src/vs/platform/extensions/node/extensionHostStarterWorker.ts +++ b/src/vs/platform/extensions/electron-main/extensionHostStarter.ts @@ -3,21 +3,134 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { canceled, SerializedError, transformErrorForSerialization } from 'vs/base/common/errors'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter'; +import { Emitter, Event } from 'vs/base/common/event'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { StopWatch } from 'vs/base/common/stopwatch'; import { ChildProcess, fork } from 'child_process'; import { StringDecoder } from 'string_decoder'; import { Promises, timeout } from 'vs/base/common/async'; -import { SerializedError, transformErrorForSerialization } from 'vs/base/common/errors'; -import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { FileAccess } from 'vs/base/common/network'; import { mixin } from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { cwd } from 'vs/base/common/process'; -import { StopWatch } from 'vs/base/common/stopwatch'; -import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter'; -export interface IExtensionHostStarterWorkerHost { - logInfo(message: string): Promise; +export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter { + _serviceBrand: undefined; + + private static _lastId: number = 0; + + protected readonly _extHosts: Map; + private _shutdown = false; + + constructor( + @ILogService private readonly _logService: ILogService, + @ILifecycleMainService lifecycleMainService: ILifecycleMainService + ) { + this._extHosts = new Map(); + + // On shutdown: gracefully await extension host shutdowns + lifecycleMainService.onWillShutdown((e) => { + this._shutdown = true; + e.join(this._waitForAllExit(6000)); + }); + } + + dispose(): void { + // Intentionally not killing the extension host processes + } + + private _getExtHost(id: string): ExtensionHostProcess { + const extHostProcess = this._extHosts.get(id); + if (!extHostProcess) { + throw new Error(`Unknown extension host!`); + } + return extHostProcess; + } + + onDynamicStdout(id: string): Event { + return this._getExtHost(id).onStdout; + } + + onDynamicStderr(id: string): Event { + return this._getExtHost(id).onStderr; + } + + onDynamicMessage(id: string): Event { + return this._getExtHost(id).onMessage; + } + + onDynamicError(id: string): Event<{ error: SerializedError }> { + return this._getExtHost(id).onError; + } + + onDynamicExit(id: string): Event<{ code: number; signal: string }> { + return this._getExtHost(id).onExit; + } + + async createExtensionHost(): Promise<{ id: string }> { + if (this._shutdown) { + throw canceled(); + } + const id = String(++ExtensionHostStarter._lastId); + const extHost = new ExtensionHostProcess(id, this._logService); + this._extHosts.set(id, extHost); + extHost.onExit(({ pid, code, signal }) => { + this._logService.info(`Extension host with pid ${pid} exited with code: ${code}, signal: ${signal}.`); + setTimeout(() => { + extHost.dispose(); + this._extHosts.delete(id); + }); + }); + return { id }; + } + + async start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number }> { + if (this._shutdown) { + throw canceled(); + } + return this._getExtHost(id).start(opts); + } + + async enableInspectPort(id: string): Promise { + if (this._shutdown) { + throw canceled(); + } + const extHostProcess = this._extHosts.get(id); + if (!extHostProcess) { + return false; + } + return extHostProcess.enableInspectPort(); + } + + async kill(id: string): Promise { + if (this._shutdown) { + throw canceled(); + } + const extHostProcess = this._extHosts.get(id); + if (!extHostProcess) { + // already gone! + return; + } + extHostProcess.kill(); + } + + async _killAllNow(): Promise { + for (const [, extHost] of this._extHosts) { + extHost.kill(); + } + } + + async _waitForAllExit(maxWaitTimeMs: number): Promise { + const exitPromises: Promise[] = []; + for (const [, extHost] of this._extHosts) { + exitPromises.push(extHost.waitForExit(maxWaitTimeMs)); + } + return Promises.settled(exitPromises).then(() => { }); + } } class ExtensionHostProcess extends Disposable { @@ -42,14 +155,14 @@ class ExtensionHostProcess extends Disposable { constructor( public readonly id: string, - private readonly _host: IExtensionHostStarterWorkerHost + @ILogService private readonly _logService: ILogService, ) { super(); } start(opts: IExtensionHostProcessOptions): { pid: number } { if (platform.isCI) { - this._host.logInfo(`Calling fork to start extension host...`); + this._logService.info(`Calling fork to start extension host...`); } const sw = StopWatch.create(false); this._process = fork( @@ -60,7 +173,7 @@ class ExtensionHostProcess extends Disposable { const forkTime = sw.elapsed(); const pid = this._process.pid!; - this._host.logInfo(`Starting extension host with pid ${pid} (fork() took ${forkTime} ms).`); + this._logService.info(`Starting extension host with pid ${pid} (fork() took ${forkTime} ms).`); const stdoutDecoder = new StringDecoder('utf-8'); this._process.stdout?.on('data', (chunk) => { @@ -95,7 +208,7 @@ class ExtensionHostProcess extends Disposable { return false; } - this._host.logInfo(`Enabling inspect port on extension host with pid ${this._process.pid}.`); + this._logService.info(`Enabling inspect port on extension host with pid ${this._process.pid}.`); interface ProcessExt { _debugProcess?(n: number): any; @@ -119,7 +232,7 @@ class ExtensionHostProcess extends Disposable { if (!this._process) { return; } - this._host.logInfo(`Killing extension host with pid ${this._process.pid}.`); + this._logService.info(`Killing extension host with pid ${this._process.pid}.`); this._process.kill(); } @@ -128,116 +241,13 @@ class ExtensionHostProcess extends Disposable { return; } const pid = this._process.pid; - this._host.logInfo(`Waiting for extension host with pid ${pid} to exit.`); + this._logService.info(`Waiting for extension host with pid ${pid} to exit.`); await Promise.race([Event.toPromise(this.onExit), timeout(maxWaitTimeMs)]); if (!this._hasExited) { // looks like we timed out - this._host.logInfo(`Extension host with pid ${pid} did not exit within ${maxWaitTimeMs}ms.`); + this._logService.info(`Extension host with pid ${pid} did not exit within ${maxWaitTimeMs}ms.`); this._process.kill(); } } } - -export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter { - _serviceBrand: undefined; - - private static _lastId: number = 0; - - protected readonly _extHosts: Map; - - constructor( - private readonly _host: IExtensionHostStarterWorkerHost - ) { - this._extHosts = new Map(); - } - - dispose(): void { - // Intentionally not killing the extension host processes - } - - private _getExtHost(id: string): ExtensionHostProcess { - const extHostProcess = this._extHosts.get(id); - if (!extHostProcess) { - throw new Error(`Unknown extension host!`); - } - return extHostProcess; - } - - onDynamicStdout(id: string): Event { - return this._getExtHost(id).onStdout; - } - - onDynamicStderr(id: string): Event { - return this._getExtHost(id).onStderr; - } - - onDynamicMessage(id: string): Event { - return this._getExtHost(id).onMessage; - } - - onDynamicError(id: string): Event<{ error: SerializedError }> { - return this._getExtHost(id).onError; - } - - onDynamicExit(id: string): Event<{ code: number; signal: string }> { - return this._getExtHost(id).onExit; - } - - async createExtensionHost(): Promise<{ id: string }> { - const id = String(++ExtensionHostStarter._lastId); - const extHost = new ExtensionHostProcess(id, this._host); - this._extHosts.set(id, extHost); - extHost.onExit(({ pid, code, signal }) => { - this._host.logInfo(`Extension host with pid ${pid} exited with code: ${code}, signal: ${signal}.`); - setTimeout(() => { - extHost.dispose(); - this._extHosts.delete(id); - }); - }); - return { id }; - } - - async start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number }> { - return this._getExtHost(id).start(opts); - } - - async enableInspectPort(id: string): Promise { - const extHostProcess = this._extHosts.get(id); - if (!extHostProcess) { - return false; - } - return extHostProcess.enableInspectPort(); - } - - async kill(id: string): Promise { - const extHostProcess = this._extHosts.get(id); - if (!extHostProcess) { - // already gone! - return; - } - extHostProcess.kill(); - } - - async killAllNow(): Promise { - for (const [, extHost] of this._extHosts) { - extHost.kill(); - } - } - - async waitForAllExit(maxWaitTimeMs: number): Promise { - const exitPromises: Promise[] = []; - for (const [, extHost] of this._extHosts) { - exitPromises.push(extHost.waitForExit(maxWaitTimeMs)); - } - return Promises.settled(exitPromises).then(() => { }); - } -} - -/** - * The `create` function needs to be there by convention because - * we are loaded via the `vs/base/common/worker/simpleWorker` utility. - */ -export function create(host: IExtensionHostStarterWorkerHost) { - return new ExtensionHostStarter(host); -} diff --git a/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts b/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts deleted file mode 100644 index f78d3e2db1f50..0000000000000 --- a/src/vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter.ts +++ /dev/null @@ -1,173 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { canceled, SerializedError } from 'vs/base/common/errors'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter'; -import { Event } from 'vs/base/common/event'; -import { FileAccess } from 'vs/base/common/network'; -import { ILogService } from 'vs/platform/log/common/log'; -import { Worker } from 'worker_threads'; -import { IWorker, IWorkerCallback, IWorkerFactory, SimpleWorkerClient } from 'vs/base/common/worker/simpleWorker'; -import type { ExtensionHostStarter, IExtensionHostStarterWorkerHost } from 'vs/platform/extensions/node/extensionHostStarterWorker'; -import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { StopWatch } from 'vs/base/common/stopwatch'; - -class NodeWorker implements IWorker { - - private readonly _worker: Worker; - - public readonly onError: Event; - public readonly onExit: Event; - public readonly onMessageError: Event; - - constructor(callback: IWorkerCallback, onErrorCallback: (err: any) => void) { - this._worker = new Worker( - FileAccess.asFileUri('vs/platform/extensions/node/extensionHostStarterWorkerMain.js', require).fsPath, - ); - this._worker.on('message', callback); - this._worker.on('error', onErrorCallback); - this.onError = Event.fromNodeEventEmitter(this._worker, 'error'); - this.onExit = Event.fromNodeEventEmitter(this._worker, 'exit'); - this.onMessageError = Event.fromNodeEventEmitter(this._worker, 'messageerror'); - } - - getId(): number { - return 1; - } - - postMessage(message: any, transfer: ArrayBuffer[]): void { - this._worker.postMessage(message, transfer); - } - - dispose(): void { - this._worker.terminate(); - } -} - -class ExtensionHostStarterWorkerHost implements IExtensionHostStarterWorkerHost { - constructor( - @ILogService private readonly _logService: ILogService - ) { } - - public async logInfo(message: string): Promise { - this._logService.info(message); - } -} - -export class WorkerMainProcessExtensionHostStarter implements IDisposable, IExtensionHostStarter { - _serviceBrand: undefined; - - private _proxy: ExtensionHostStarter | null; - private readonly _worker: SimpleWorkerClient; - private _shutdown = false; - - constructor( - @ILogService private readonly _logService: ILogService, - @ILifecycleMainService lifecycleMainService: ILifecycleMainService - ) { - this._proxy = null; - - const workerFactory: IWorkerFactory = { - create: (moduleId: string, callback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker => { - const worker = new NodeWorker(callback, onErrorCallback); - worker.onError((err) => { - this._logService.error(`ExtensionHostStarterWorker has encountered an error:`); - this._logService.error(err); - }); - worker.onMessageError((err) => { - this._logService.error(`ExtensionHostStarterWorker has encountered a message error:`); - this._logService.error(err); - }); - worker.onExit((exitCode) => this._logService.info(`ExtensionHostStarterWorker exited with code ${exitCode}.`)); - worker.postMessage(moduleId, []); - return worker; - } - }; - this._worker = new SimpleWorkerClient( - workerFactory, - 'vs/platform/extensions/node/extensionHostStarterWorker', - new ExtensionHostStarterWorkerHost(this._logService) - ); - this._initialize(); - - // On shutdown: gracefully await extension host shutdowns - lifecycleMainService.onWillShutdown((e) => { - this._shutdown = true; - if (this._proxy) { - e.join(this._proxy.waitForAllExit(6000)); - } - }); - } - - dispose(): void { - // Intentionally not killing the extension host processes - } - - async _initialize(): Promise { - this._proxy = await this._worker.getProxyObject(); - this._logService.info(`ExtensionHostStarterWorker created`); - } - - onDynamicStdout(id: string): Event { - return this._proxy!.onDynamicStdout(id); - } - - onDynamicStderr(id: string): Event { - return this._proxy!.onDynamicStderr(id); - } - - onDynamicMessage(id: string): Event { - return this._proxy!.onDynamicMessage(id); - } - - onDynamicError(id: string): Event<{ error: SerializedError }> { - return this._proxy!.onDynamicError(id); - } - - onDynamicExit(id: string): Event<{ code: number; signal: string }> { - return this._proxy!.onDynamicExit(id); - } - - async createExtensionHost(): Promise<{ id: string }> { - const proxy = await this._worker.getProxyObject(); - if (this._shutdown) { - throw canceled(); - } - return proxy.createExtensionHost(); - } - - async start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number }> { - const sw = StopWatch.create(false); - const proxy = await this._worker.getProxyObject(); - if (this._shutdown) { - throw canceled(); - } - const timeout = setTimeout(() => { - this._logService.info(`ExtensionHostStarterWorker.start() did not return within 30s. This might be a problem.`); - }, 30000); - const result = await proxy.start(id, opts); - const duration = sw.elapsed(); - this._logService.info(`ExtensionHostStarterWorker.start() took ${duration} ms.`); - clearTimeout(timeout); - return result; - } - - async enableInspectPort(id: string): Promise { - const proxy = await this._worker.getProxyObject(); - if (this._shutdown) { - throw canceled(); - } - return proxy.enableInspectPort(id); - } - - async kill(id: string): Promise { - const proxy = await this._worker.getProxyObject(); - if (this._shutdown) { - throw canceled(); - } - return proxy.kill(id); - } -} diff --git a/src/vs/platform/extensions/node/extensionHostStarterWorkerMain.ts b/src/vs/platform/extensions/node/extensionHostStarterWorkerMain.ts deleted file mode 100644 index b4efa0c798fa8..0000000000000 --- a/src/vs/platform/extensions/node/extensionHostStarterWorkerMain.ts +++ /dev/null @@ -1,66 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -(function () { - 'use strict'; - - const loader = require('../../../loader'); - const bootstrap = require('../../../../bootstrap'); - const path = require('path'); - const parentPort = require('worker_threads').parentPort; - - // Bootstrap: NLS - const nlsConfig = bootstrap.setupNLS(); - - // Bootstrap: Loader - loader.config({ - baseUrl: bootstrap.fileUriFromPath(path.join(__dirname, '../../../../'), { isWindows: process.platform === 'win32' }), - catchError: true, - nodeRequire: require, - nodeMain: __filename, - 'vs/nls': nlsConfig, - amdModulesPattern: /^vs\//, - recordStats: true - }); - - let isFirstMessage = true; - let beforeReadyMessages: any[] = []; - - const initialMessageHandler = (data: any) => { - if (!isFirstMessage) { - beforeReadyMessages.push(data); - return; - } - - isFirstMessage = false; - loadCode(data); - }; - - parentPort.on('message', initialMessageHandler); - - const loadCode = function (moduleId: string) { - loader([moduleId], function (ws: any) { - setTimeout(() => { - - const messageHandler = ws.create((msg: any, transfer?: ArrayBuffer[]) => { - parentPort.postMessage(msg, transfer); - }, null); - parentPort.off('message', initialMessageHandler); - parentPort.on('message', (data: any) => { - messageHandler.onmessage(data); - }); - while (beforeReadyMessages.length > 0) { - const msg = beforeReadyMessages.shift()!; - messageHandler.onmessage(msg); - } - - }); - }, (err: any) => console.error(err)); - }; - - parentPort.on('messageerror', (err: Error) => { - console.error(err); - }); -})(); From c4870970ab275e89338d44a6b9750baf25ae6597 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 20 May 2022 10:52:56 +0200 Subject: [PATCH 182/942] Run a job on `main` which only warms up the node modules cache (#150007) --- .github/workflows/basic.yml | 52 +++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index 270e952393882..0d94b83350a77 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -1,17 +1,16 @@ name: Basic checks on: - # push: - # branches: - # - main - # - release/* + push: + branches: + - main pull_request: branches: - main - - release/* jobs: main: + if: github.ref != 'refs/heads/main' name: Compilation, Unit and Integration Tests runs-on: ubuntu-latest timeout-minutes: 40 @@ -77,6 +76,7 @@ jobs: run: DISPLAY=:10 ./scripts/test-integration.sh hygiene: + if: github.ref != 'refs/heads/main' name: Hygiene and Layering runs-on: ubuntu-latest timeout-minutes: 40 @@ -140,3 +140,45 @@ jobs: - name: Run Trusted Types Checks run: yarn tsec-compile-check + + warm-cache: + name: Warm up node modules cache + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + timeout-minutes: 40 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Compute node modules cache key + id: nodeModulesCacheKey + run: echo "::set-output name=value::$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" + - name: Cache node modules + id: cacheNodeModules + uses: actions/cache@v3 + with: + path: "**/node_modules" + key: ${{ runner.os }}-cacheNodeModules21-${{ steps.nodeModulesCacheKey.outputs.value }} + restore-keys: ${{ runner.os }}-cacheNodeModules21- + - name: Get yarn cache directory path + id: yarnCacheDirPath + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: Cache yarn directory + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + uses: actions/cache@v3 + with: + path: ${{ steps.yarnCacheDirPath.outputs.dir }} + key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} + restore-keys: ${{ runner.os }}-yarnCacheDir- + - name: Execute yarn + if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + run: yarn --frozen-lockfile --network-timeout 180000 From 29f30af16430560b20899649ce888fb22464e0e4 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Fri, 20 May 2022 05:23:39 -0700 Subject: [PATCH 183/942] Add an aria label to the checkall checkbox (#149994) --- src/vs/base/parts/quickinput/browser/quickInput.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 0dca8a2bd384c..49fb9f194559b 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -1233,6 +1233,7 @@ export class QuickInputController extends Disposable { const checkAll = dom.append(headerContainer, $('input.quick-input-check-all')); checkAll.type = 'checkbox'; + checkAll.setAttribute('aria-label', localize('quickInput.checkAll', "Toggle all checkboxes")); this._register(dom.addStandardDisposableListener(checkAll, dom.EventType.CHANGE, e => { const checked = checkAll.checked; list.setAllVisibleChecked(checked); From d93881d70673837285d6e42137f6d45c1d9769f5 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 20 May 2022 14:32:37 +0200 Subject: [PATCH 184/942] Fix #149518 (#150022) --- .../common/extensionGalleryService.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index a65562115811d..3f563b19e404b 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -679,8 +679,13 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi } if (compatible) { - const engine = await this.getEngine(rawGalleryExtensionVersion); - if (!isEngineValid(engine, this.productService.version, this.productService.date)) { + try { + const engine = await this.getEngine(rawGalleryExtensionVersion); + if (!isEngineValid(engine, this.productService.version, this.productService.date)) { + return false; + } + } catch (error) { + this.logService.error(`Error while getting the engine for the version ${rawGalleryExtensionVersion.version}.`, getErrorMessage(error)); return false; } } From 993c36c9205f156c1246f65161fa4796d460502f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 20 May 2022 09:17:57 -0700 Subject: [PATCH 185/942] Fix markdown ext errors accessing disposed webview (#149960) When reloading windows that need to restore a markdown preview, sometimes I'll see an error when the markdown preview tries accessing a disposed of webview. This seems to be cause caused by `provideTextDocumentContent`, where we end up disposing of the webview before an `await` resumes execution --- .../markdown-language-features/src/preview/preview.ts | 10 ++++++++-- .../src/preview/previewContentProvider.ts | 7 ++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/extensions/markdown-language-features/src/preview/preview.ts b/extensions/markdown-language-features/src/preview/preview.ts index f598dd62fa354..dd3c310a194e1 100644 --- a/extensions/markdown-language-features/src/preview/preview.ts +++ b/extensions/markdown-language-features/src/preview/preview.ts @@ -109,6 +109,8 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { private readonly _onScrollEmitter = this._register(new vscode.EventEmitter()); public readonly onScroll = this._onScrollEmitter.event; + private readonly _disposeCts = this._register(new vscode.CancellationTokenSource()); + constructor( webview: vscode.WebviewPanel, resource: vscode.Uri, @@ -202,6 +204,8 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { } override dispose() { + this._disposeCts.cancel(); + super.dispose(); this._disposed = true; @@ -286,7 +290,9 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { try { document = await vscode.workspace.openTextDocument(this._resource); } catch { - await this.showFileNotFoundError(); + if (!this._disposed) { + await this.showFileNotFoundError(); + } return; } @@ -306,7 +312,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { this.currentVersion = pendingVersion; const content = await (shouldReloadPage - ? this._contentProvider.provideTextDocumentContent(document, this, this._previewConfigurations, this.line, this.state) + ? this._contentProvider.provideTextDocumentContent(document, this, this._previewConfigurations, this.line, this.state, this._disposeCts.token) : this._contentProvider.markdownBody(document, this)); // Another call to `doUpdate` may have happened. diff --git a/extensions/markdown-language-features/src/preview/previewContentProvider.ts b/extensions/markdown-language-features/src/preview/previewContentProvider.ts index b7ff739ce9138..8437017dc4f18 100644 --- a/extensions/markdown-language-features/src/preview/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/preview/previewContentProvider.ts @@ -66,7 +66,8 @@ export class MarkdownContentProvider { resourceProvider: WebviewResourceProvider, previewConfigurations: MarkdownPreviewConfigurationManager, initialLine: number | undefined = undefined, - state?: any + state: any | undefined, + token: vscode.CancellationToken ): Promise { const sourceUri = markdownDocument.uri; const config = previewConfigurations.loadAndCacheConfiguration(sourceUri); @@ -89,6 +90,10 @@ export class MarkdownContentProvider { const csp = this.getCsp(resourceProvider, sourceUri, nonce); const body = await this.markdownBody(markdownDocument, resourceProvider); + if (token.isCancellationRequested) { + return { html: '', containingImages: [] }; + } + const html = ` From 603c8c036fec9ca9c883169b0018c7ddd526dabe Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 20 May 2022 18:21:35 +0200 Subject: [PATCH 186/942] read tasks resource from profile --- .../services/configuration/browser/configuration.ts | 11 ++++++----- .../configuration/browser/configurationService.ts | 2 +- .../common/configurationEditingService.ts | 12 ++++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 2bd9b092c9a9b..49af00baad3a6 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -26,6 +26,7 @@ import { joinPath } from 'vs/base/common/resources'; import { Registry } from 'vs/platform/registry/common/platform'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { isObject } from 'vs/base/common/types'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; export class DefaultConfiguration extends Disposable { @@ -137,7 +138,7 @@ export class UserConfiguration extends Disposable { get hasTasksLoaded(): boolean { return this.userConfiguration.value instanceof FileServiceBasedConfiguration; } constructor( - private readonly userSettingsResource: URI, + private readonly userDataProfile: IUserDataProfile, scopes: ConfigurationScope[] | undefined, private readonly fileService: IFileService, private readonly uriIdentityService: IUriIdentityService, @@ -145,7 +146,7 @@ export class UserConfiguration extends Disposable { ) { super(); this.configurationParseOptions = { scopes, skipRestricted: false }; - this.userConfiguration.value = new UserSettings(this.userSettingsResource, scopes, uriIdentityService.extUri, this.fileService); + this.userConfiguration.value = new UserSettings(this.userDataProfile.settingsResource, scopes, uriIdentityService.extUri, this.fileService); this._register(this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule())); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50)); } @@ -159,9 +160,9 @@ export class UserConfiguration extends Disposable { return this.userConfiguration.value!.loadConfiguration(); } - const folder = this.uriIdentityService.extUri.dirname(this.userSettingsResource); - const standAloneConfigurationResources: [string, URI][] = [TASKS_CONFIGURATION_KEY].map(name => ([name, this.uriIdentityService.extUri.joinPath(folder, `${name}.json`)])); - const fileServiceBasedConfiguration = new FileServiceBasedConfiguration(folder.toString(), this.userSettingsResource, standAloneConfigurationResources, this.configurationParseOptions, this.fileService, this.uriIdentityService, this.logService); + const folder = this.uriIdentityService.extUri.dirname(this.userDataProfile.settingsResource); + const standAloneConfigurationResources: [string, URI][] = [[TASKS_CONFIGURATION_KEY, this.userDataProfile.tasksResource]]; + const fileServiceBasedConfiguration = new FileServiceBasedConfiguration(folder.toString(), this.userDataProfile.settingsResource, standAloneConfigurationResources, this.configurationParseOptions, this.fileService, this.uriIdentityService, this.logService); const configurationModel = await fileServiceBasedConfiguration.loadConfiguration(); this.userConfiguration.value = fileServiceBasedConfiguration; diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index bee40220f69b2..faa9e6e2b7ed5 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -118,7 +118,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat this.logService = logService; this._configuration = new Configuration(this.defaultConfiguration.configurationModel, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); this.cachedFolderConfigs = new ResourceMap(); - this.localUserConfiguration = this._register(new UserConfiguration(userDataProfilesService.defaultProfile.settingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService)); + this.localUserConfiguration = this._register(new UserConfiguration(userDataProfilesService.defaultProfile, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService)); this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); if (remoteAuthority) { const remoteUserConfiguration = this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, fileService, uriIdentityService, remoteAgentService)); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index 52a210d5d371c..f060f4452c142 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -566,7 +566,7 @@ export class ConfigurationEditingService { const standaloneConfigurationMap = target === EditableConfigurationTarget.USER_LOCAL ? USER_STANDALONE_CONFIGURATIONS : WORKSPACE_STANDALONE_CONFIGURATIONS; const standaloneConfigurationKeys = Object.keys(standaloneConfigurationMap); for (const key of standaloneConfigurationKeys) { - const resource = this.getConfigurationFileResource(target, standaloneConfigurationMap[key], overrides.resource); + const resource = this.getConfigurationFileResource(target, key, standaloneConfigurationMap[key], overrides.resource); // Check for prefix if (config.key === key) { @@ -586,10 +586,10 @@ export class ConfigurationEditingService { let key = config.key; let jsonPath = overrides.overrideIdentifiers?.length ? [keyFromOverrideIdentifiers(overrides.overrideIdentifiers), key] : [key]; if (target === EditableConfigurationTarget.USER_LOCAL || target === EditableConfigurationTarget.USER_REMOTE) { - return { key, jsonPath, value: config.value, resource: withNullAsUndefined(this.getConfigurationFileResource(target, '', null)), target }; + return { key, jsonPath, value: config.value, resource: withNullAsUndefined(this.getConfigurationFileResource(target, undefined, '', null)), target }; } - const resource = this.getConfigurationFileResource(target, FOLDER_SETTINGS_PATH, overrides.resource); + const resource = this.getConfigurationFileResource(target, undefined, FOLDER_SETTINGS_PATH, overrides.resource); if (this.isWorkspaceConfigurationResource(resource)) { jsonPath = ['settings', ...jsonPath]; } @@ -601,10 +601,10 @@ export class ConfigurationEditingService { return !!(workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath); } - private getConfigurationFileResource(target: EditableConfigurationTarget, relativePath: string, resource: URI | null | undefined): URI | null { + private getConfigurationFileResource(target: EditableConfigurationTarget, standAloneConfigurationKey: string | undefined, relativePath: string, resource: URI | null | undefined): URI | null { if (target === EditableConfigurationTarget.USER_LOCAL) { - if (relativePath) { - return this.uriIdentityService.extUri.joinPath(this.uriIdentityService.extUri.dirname(this.userDataProfilesService.defaultProfile.settingsResource), relativePath); + if (standAloneConfigurationKey === TASKS_CONFIGURATION_KEY) { + return this.userDataProfilesService.defaultProfile.tasksResource; } else { return this.userDataProfilesService.defaultProfile.settingsResource; } From 90875504324fdb299a0996931990c4d801dc9e6a Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Fri, 20 May 2022 09:30:25 -0700 Subject: [PATCH 187/942] better z-indexes for title menu and quick pick (#150033) --- src/vs/base/parts/quickinput/browser/media/quickInput.css | 2 +- src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/parts/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css index 802cc6adfe77b..18cf3873a63e1 100644 --- a/src/vs/base/parts/quickinput/browser/media/quickInput.css +++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css @@ -6,7 +6,7 @@ .quick-input-widget { position: absolute; width: 600px; - z-index: 4000; + z-index: 2550; padding: 0 1px 1px 1px; left: 50%; margin-left: -300px; diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 8b516b544a75f..1a1d9490d5c06 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -81,7 +81,7 @@ /* Window Title Menu */ .monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu { - z-index: 3000; + z-index: 2550; -webkit-app-region: no-drag; padding: 0 8px; } From e2dff56c3686f66cead62d4bd6850b15e9c0fb2d Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Fri, 20 May 2022 09:34:41 -0700 Subject: [PATCH 188/942] fixes #150034 (#150035) --- src/vs/base/parts/quickinput/browser/media/quickInput.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/base/parts/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css index 18cf3873a63e1..efab21e77bf3a 100644 --- a/src/vs/base/parts/quickinput/browser/media/quickInput.css +++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css @@ -10,6 +10,7 @@ padding: 0 1px 1px 1px; left: 50%; margin-left: -300px; + -webkit-app-region: no-drag; } .quick-input-titlebar { From de0b9e00eaea0c34abb0e9dc0bb764880e8ba2da Mon Sep 17 00:00:00 2001 From: zz <2418184580@qq.com> Date: Sat, 21 May 2022 00:42:27 +0800 Subject: [PATCH 189/942] Add "pnpm-lock.yaml" to the child patterns of "package.json" (#146869) Co-authored-by: Jackson Kearl --- src/vs/workbench/contrib/files/browser/files.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index b162ad2ead5db..aa794b52df61a 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -500,7 +500,7 @@ configurationRegistry.registerConfiguration({ '*.jsx': '${capture}.js', '*.tsx': '${capture}.ts', 'tsconfig.json': 'tsconfig.*.json', - 'package.json': 'package-lock.json, yarn.lock', + 'package.json': 'package-lock.json, yarn.lock, pnpm-lock.yaml', } } } From 43d3838d007b921ecfbff70b55517fb808d38208 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 20 May 2022 18:58:43 +0200 Subject: [PATCH 190/942] fix tests --- src/vs/workbench/test/browser/workbenchTestServices.ts | 2 ++ .../workbench/test/electron-browser/workbenchTestServices.ts | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 5bd962101c995..1e70814a6bdce 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -160,6 +160,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import { ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { ILayoutOffsetInfo } from 'vs/platform/layout/browser/layoutService'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined, undefined); @@ -242,6 +243,7 @@ export function workbenchInstantiationService( const environmentService = overrides?.environmentService ? overrides.environmentService(instantiationService) : TestEnvironmentService; instantiationService.stub(IEnvironmentService, environmentService); instantiationService.stub(IWorkbenchEnvironmentService, environmentService); + instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, new NullLogService())); const contextKeyService = overrides?.contextKeyService ? overrides.contextKeyService(instantiationService) : instantiationService.createInstance(MockContextKeyService); instantiationService.stub(IContextKeyService, contextKeyService); instantiationService.stub(IProgressService, new TestProgressService()); diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 9b1228a4c6966..191a3829c20cb 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -25,7 +25,7 @@ import { IReadTextFileOptions, ITextFileStreamContent, ITextFileService } from ' import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; import { IOpenEmptyWindowOptions, IWindowOpenable, IOpenWindowOptions, IOpenedWindow, IColorScheme, INativeWindowConfiguration } from 'vs/platform/window/common/window'; import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; -import { LogLevel, ILogService } from 'vs/platform/log/common/log'; +import { LogLevel, ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -48,6 +48,7 @@ import { IElevatedFileService } from 'vs/workbench/services/files/common/elevate import { IDecorationsService } from 'vs/workbench/services/decorations/common/decorations'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; const args = parseArgs(process.argv, OPTIONS); @@ -269,6 +270,7 @@ export function workbenchInstantiationService(disposables = new DisposableStore( instantiationService.stub(INativeEnvironmentService, TestEnvironmentService); instantiationService.stub(IWorkbenchEnvironmentService, TestEnvironmentService); instantiationService.stub(INativeWorkbenchEnvironmentService, TestEnvironmentService); + instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(TestEnvironmentService, new NullLogService())); return instantiationService; } From 4309c752dde6e07c0c0ace2878cc8ff2d44622e1 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Fri, 20 May 2022 10:56:14 -0700 Subject: [PATCH 191/942] use css px increments for view size changes (#150039) fixes #149488 --- src/vs/workbench/browser/actions/layoutActions.ts | 2 +- src/vs/workbench/browser/layout.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 8ace4020a722f..4b4cc8b38f09b 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -967,7 +967,7 @@ registerAction2(class extends Action2 { abstract class BaseResizeViewAction extends Action2 { - protected static readonly RESIZE_INCREMENT = 6.5; // This is a media-size percentage + protected static readonly RESIZE_INCREMENT = 60; // This is a css pixel size protected resizePart(widthChange: number, heightChange: number, layoutService: IWorkbenchLayoutService, partToResize?: Parts): void { diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index acb2fc49198f1..2740f42fde9a3 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, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize } from 'vs/base/browser/dom'; import { onDidChangeFullscreen, isFullscreen } from 'vs/base/browser/browser'; import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; import { isWindows, isLinux, isMacintosh, isWeb, isNative, isIOS } from 'vs/base/common/platform'; @@ -1328,8 +1328,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } resizePart(part: Parts, sizeChangeWidth: number, sizeChangeHeight: number): void { - const sizeChangePxWidth = this.workbenchGrid.width * sizeChangeWidth / 100; - const sizeChangePxHeight = this.workbenchGrid.height * sizeChangeHeight / 100; + const sizeChangePxWidth = Math.sign(sizeChangeWidth) * computeScreenAwareSize(Math.abs(sizeChangeWidth)); + const sizeChangePxHeight = Math.sign(sizeChangeHeight) * computeScreenAwareSize(Math.abs(sizeChangeHeight)); let viewSize: IViewSize; From 279b7a8633558e8c95cba4d02d76e98cf996c741 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 May 2022 11:38:31 -0700 Subject: [PATCH 192/942] Fix DecorationAddon being disposed of immediately Regressed in #148972 Fixes #149866 --- src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 6849cc1ff8493..3285ae2c363b9 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -620,6 +620,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { this._decorationAddon.dispose(); this._decorationAddon = undefined; } + return; } if (this._decorationAddon) { this._decorationAddon.dispose(); From 351b4a914ea57139261d9d574341436ea66d0e8e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 May 2022 11:41:17 -0700 Subject: [PATCH 193/942] Re-enable shell integration tests for macOS Fixes #149757 --- .../smoke/src/areas/terminal/terminal-shellIntegration.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts index a5f5842b4c6bd..b94b40045f7a3 100644 --- a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts +++ b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts @@ -26,8 +26,7 @@ export function setup() { describe('Shell integration', function () { // TODO: Fix on Linux, some distros use sh as the default shell in which case shell integration will fail - // TODO: Fix on macOS, not sure reason for failing https://github.com/microsoft/vscode/issues/149757 - describe.skip('Decorations', function () { + (process.platform === 'win32' || process.platform === 'linux' ? describe.skip : describe)('Decorations', function () { describe('Should show default icons', function () { it('Placeholder', async () => { await terminal.createTerminal(); From 9e1c5d60ae7b68cf12293592fbb88b726a69fbb0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 May 2022 11:29:55 -0700 Subject: [PATCH 194/942] Fix exception that could occur during render Fixes #149867 Fixes #149865 --- package.json | 4 ++-- remote/package.json | 4 ++-- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 ++++---- remote/yarn.lock | 18 +++++++++--------- yarn.lock | 18 +++++++++--------- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index ca6cec1a6bd91..b8992138afc79 100644 --- a/package.json +++ b/package.json @@ -84,12 +84,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.47", + "xterm": "4.19.0-beta.49", "xterm-addon-search": "0.9.0-beta.37", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.36", - "xterm-headless": "4.19.0-beta.47", + "xterm-headless": "4.19.0-beta.49", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 75e83acbde5b0..3e833d3c5f64f 100644 --- a/remote/package.json +++ b/remote/package.json @@ -24,12 +24,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.47", + "xterm": "4.19.0-beta.49", "xterm-addon-search": "0.9.0-beta.37", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.36", - "xterm-headless": "4.19.0-beta.47", + "xterm-headless": "4.19.0-beta.49", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index ece7c79fe5ca3..4ec42b01f1267 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -10,7 +10,7 @@ "tas-client-umd": "0.1.5", "vscode-oniguruma": "1.6.1", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.47", + "xterm": "4.19.0-beta.49", "xterm-addon-search": "0.9.0-beta.37", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.36" diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 97609688b6dd0..d8eac016d98df 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -128,7 +128,7 @@ xterm-addon-webgl@0.12.0-beta.36: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.36.tgz#460f80829a78c979a448d5b764699af3f0366ff1" integrity sha512-sgX7OHSGZQZE5b4xtPqd/5NEcll0Z+00tnTVxKZlXf5XEENcG0tnBF4I4f+k9K3cmjE1UIUVG2yYPrqWlYCdpA== -xterm@4.19.0-beta.47: - version "4.19.0-beta.47" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.47.tgz#f5931201346a485e3bcb29d24e83583727e55707" - integrity sha512-c8EcCSWCHnFYNdRMTnJIpYTV9J2Ze7kdB1GhTcPO2ipMEsqaP/u6Ca8rgBWwV9HeEVfe4rGRqWW2qcSy4U9hMQ== +xterm@4.19.0-beta.49: + version "4.19.0-beta.49" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.49.tgz#96d0d748c97a2f0cfa4c6112bc4de8b0d5d559fe" + integrity sha512-qPhOeGtnB357mZOvfDckVlPDR7EDkSASQrB3aujhjgJlOiBBqcNRJcuSvF7v1DOho7xdEI9UQxiSiT5diHhMlg== diff --git a/remote/yarn.lock b/remote/yarn.lock index f023194fa2689..52a1cc813ed7d 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -934,15 +934,15 @@ xterm-addon-webgl@0.12.0-beta.36: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.36.tgz#460f80829a78c979a448d5b764699af3f0366ff1" integrity sha512-sgX7OHSGZQZE5b4xtPqd/5NEcll0Z+00tnTVxKZlXf5XEENcG0tnBF4I4f+k9K3cmjE1UIUVG2yYPrqWlYCdpA== -xterm-headless@4.19.0-beta.47: - version "4.19.0-beta.47" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.47.tgz#9d5af8145c42b9a6241a040bb244b877c149761b" - integrity sha512-chVTURPMNDEerQIsN4lIRotpXCfJsTHioZGQxDBNdktZO1gMXGvNxDjzsbsJg5ikLN5llz/HLDfXZrjCeb3elg== - -xterm@4.19.0-beta.47: - version "4.19.0-beta.47" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.47.tgz#f5931201346a485e3bcb29d24e83583727e55707" - integrity sha512-c8EcCSWCHnFYNdRMTnJIpYTV9J2Ze7kdB1GhTcPO2ipMEsqaP/u6Ca8rgBWwV9HeEVfe4rGRqWW2qcSy4U9hMQ== +xterm-headless@4.19.0-beta.49: + version "4.19.0-beta.49" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.49.tgz#8e4178620be9a9dee25a586ef992a6fc621a829a" + integrity sha512-uRnHpR2pv1MJPbE3sa7XbP6x0uHubAWVMbiD26e3a5ICtWJQVVpLojkA0t4qU7CoMX85pRL1rIclg9CKY0B0xA== + +xterm@4.19.0-beta.49: + version "4.19.0-beta.49" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.49.tgz#96d0d748c97a2f0cfa4c6112bc4de8b0d5d559fe" + integrity sha512-qPhOeGtnB357mZOvfDckVlPDR7EDkSASQrB3aujhjgJlOiBBqcNRJcuSvF7v1DOho7xdEI9UQxiSiT5diHhMlg== yallist@^4.0.0: version "4.0.0" diff --git a/yarn.lock b/yarn.lock index 382b89816cd81..70623ba39e585 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12205,15 +12205,15 @@ xterm-addon-webgl@0.12.0-beta.36: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.36.tgz#460f80829a78c979a448d5b764699af3f0366ff1" integrity sha512-sgX7OHSGZQZE5b4xtPqd/5NEcll0Z+00tnTVxKZlXf5XEENcG0tnBF4I4f+k9K3cmjE1UIUVG2yYPrqWlYCdpA== -xterm-headless@4.19.0-beta.47: - version "4.19.0-beta.47" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.47.tgz#9d5af8145c42b9a6241a040bb244b877c149761b" - integrity sha512-chVTURPMNDEerQIsN4lIRotpXCfJsTHioZGQxDBNdktZO1gMXGvNxDjzsbsJg5ikLN5llz/HLDfXZrjCeb3elg== - -xterm@4.19.0-beta.47: - version "4.19.0-beta.47" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.47.tgz#f5931201346a485e3bcb29d24e83583727e55707" - integrity sha512-c8EcCSWCHnFYNdRMTnJIpYTV9J2Ze7kdB1GhTcPO2ipMEsqaP/u6Ca8rgBWwV9HeEVfe4rGRqWW2qcSy4U9hMQ== +xterm-headless@4.19.0-beta.49: + version "4.19.0-beta.49" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.49.tgz#8e4178620be9a9dee25a586ef992a6fc621a829a" + integrity sha512-uRnHpR2pv1MJPbE3sa7XbP6x0uHubAWVMbiD26e3a5ICtWJQVVpLojkA0t4qU7CoMX85pRL1rIclg9CKY0B0xA== + +xterm@4.19.0-beta.49: + version "4.19.0-beta.49" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.49.tgz#96d0d748c97a2f0cfa4c6112bc4de8b0d5d559fe" + integrity sha512-qPhOeGtnB357mZOvfDckVlPDR7EDkSASQrB3aujhjgJlOiBBqcNRJcuSvF7v1DOho7xdEI9UQxiSiT5diHhMlg== y18n@^3.2.1: version "3.2.2" From e76ad21903c01bd420949366f8f26fc2a1d1f160 Mon Sep 17 00:00:00 2001 From: Esteban Dalel R Date: Fri, 20 May 2022 14:06:35 -0500 Subject: [PATCH 195/942] Fix typo (#150042) --- extensions/markdown-language-features/package.nls.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index b2958199b91ff..5a97389a86a17 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -28,7 +28,7 @@ "configuration.markdown.links.openLocation.currentGroup": "Open links in the active editor group.", "configuration.markdown.links.openLocation.beside": "Open links beside the active editor.", "configuration.markdown.suggest.paths.enabled.description": "Enable/disable path suggestions for markdown links", - "configuration.markdown.editor.drop.enabled": "Enable/disable dropping into the markdown editor to insert shift. Requires enabling `#workbenck.experimental.editor.dropIntoEditor.enabled#`.", + "configuration.markdown.editor.drop.enabled": "Enable/disable dropping into the markdown editor to insert shift. Requires enabling `#workbench.experimental.editor.dropIntoEditor.enabled#`.", "configuration.markdown.experimental.validate.enabled.description": "Enable/disable all error reporting in Markdown files.", "configuration.markdown.experimental.validate.referenceLinks.enabled.description": "Validate reference links in Markdown files, e.g. `[link][ref]`. Requires enabling `#markdown.experimental.validate.enabled#`.", "configuration.markdown.experimental.validate.headerLinks.enabled.description": "Validate links to headers in Markdown files, e.g. `[link](#header)`. Requires enabling `#markdown.experimental.validate.enabled#`.", From 281f027d0065b407babd86866f0ea1b4c82ec0c6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 May 2022 12:16:53 -0700 Subject: [PATCH 196/942] Fix typo in autoReplies setting --- .../workbench/contrib/terminal/common/terminalConfiguration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index a90e8a5bf3b47..e1e9d7887a9b3 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -521,7 +521,7 @@ const terminalConfiguration: IConfigurationNode = { default: true }, [TerminalSettingId.AutoReplies]: { - markdownDescription: localize('terminal.integrated.autoReplies', "A set of messages that when encountered in the terminal will be automatically responded to. Provided the message is specific enough, this can help automate away common responses.\n\nRemarks:\n\n- Use {0} to automatically respond to the terminate batch job prompt on Windows.\n- The message includes escape sequences so the reply might not happen with styled text.\n- Each reply can only happen once every second.\n- Use {1} in the reply to mean the enter key.\n- To unset a default key, set the value to null.\n- Restart VS Code if new don't apply.", '`"Terminate batch job (Y/N)": "\\r"`', '`"\\r"`'), + markdownDescription: localize('terminal.integrated.autoReplies', "A set of messages that when encountered in the terminal will be automatically responded to. Provided the message is specific enough, this can help automate away common responses.\n\nRemarks:\n\n- Use {0} to automatically respond to the terminate batch job prompt on Windows.\n- The message includes escape sequences so the reply might not happen with styled text.\n- Each reply can only happen once every second.\n- Use {1} in the reply to mean the enter key.\n- To unset a default key, set the value to null.\n- Restart VS Code if new don't apply.", '`"Terminate batch job (Y/N)": "Y\\r"`', '`"\\r"`'), type: 'object', additionalProperties: { oneOf: [{ From 900b8700cb768150989e7f4ac02560bc8e18a4be Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 May 2022 12:31:47 -0700 Subject: [PATCH 197/942] Use correct terminal dimensions when restoring a terminal Fixes #146059 --- src/vs/platform/terminal/node/ptyService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index a3191a3230c11..d9f8ae2b092a8 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -766,8 +766,8 @@ class XtermSerializer implements ITerminalSerializer { return { events: [ { - cols: this._xterm.getOption('cols'), - rows: this._xterm.getOption('rows'), + cols: this._xterm.cols, + rows: this._xterm.rows, data: serialized } ], From 3f5fed084c81964b92eea299bba0d534f1f0166b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 May 2022 12:52:21 -0700 Subject: [PATCH 198/942] Normalize slashes on Windows to correct link URI Fixes #143832 --- .../contrib/terminal/browser/links/terminalLinkOpeners.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts index 043a8472b5e0d..e03ec4351a547 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts @@ -170,7 +170,8 @@ export class TerminalSearchLinkOpener implements ITerminalLinkOpener { let resourceMatch: IResourceMatch | undefined; if (osPathModule(this._os).isAbsolute(sanitizedLink)) { const scheme = this._workbenchEnvironmentService.remoteAuthority ? Schemas.vscodeRemote : Schemas.file; - const uri = URI.from({ scheme, path: sanitizedLink }); + const slashNormalizedPath = this._os === OperatingSystem.Windows ? sanitizedLink.replace(/\\/g, '/') : sanitizedLink; + const uri = URI.from({ scheme, path: slashNormalizedPath }); try { const fileStat = await this._fileService.stat(uri); resourceMatch = { uri, isDirectory: fileStat.isDirectory }; From da180bced6d4c6228649325a11b55a8f8aa8443a Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 20 May 2022 16:24:04 -0400 Subject: [PATCH 199/942] Hide explorer entries based on git ignore file (#149967) * Hide explorer entries based on git ignore file * Remove dependency on search * Address PR feedback * Add setting and make multi workspace aware * Use `isPathIncludedInTraversal` --- src/vs/platform/files/common/files.ts | 1 + .../files/browser/files.contribution.ts | 5 + .../files/browser/views/explorerViewer.ts | 92 ++++++++++++++++++- 3 files changed, 93 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 47e7c042f8fa5..f278a476e0e03 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -1218,6 +1218,7 @@ export interface IFilesConfiguration { autoSaveDelay: number; eol: string; enableTrash: boolean; + excludeGitIgnore: boolean; hotExit: string; saveConflictResolution: 'askUser' | 'overwriteFileOnDisk'; }; diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index aa794b52df61a..15e889898d2bc 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -186,6 +186,11 @@ configurationRegistry.registerConfiguration({ 'markdownDescription': nls.localize('autoGuessEncoding', "When enabled, the editor will attempt to guess the character set encoding when opening files. This setting can also be configured per language. Note, this setting is not respected by text search. Only `#files.encoding#` is respected."), 'scope': ConfigurationScope.LANGUAGE_OVERRIDABLE }, + 'files.excludeGitIgnore': { + type: 'boolean', + description: nls.localize('excludeGitignore', "Controls whether entries in .gitignore should be parsed and excluded from the explorer. Similar to `files.exclude`."), + default: false + }, 'files.eol': { 'type': 'string', 'enum': [ diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index a24f390cf51f3..a857b3eec7d8d 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -9,7 +9,7 @@ import * as glob from 'vs/base/common/glob'; import { IListVirtualDelegate, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; import { IProgressService, ProgressLocation, } from 'vs/platform/progress/common/progress'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { IFileService, FileKind, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; +import { IFileService, FileKind, FileOperationError, FileOperationResult, FileChangeType } from 'vs/platform/files/common/files'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { isTemporaryWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDisposable, Disposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -58,6 +58,8 @@ import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; import { BrowserFileUpload, ExternalFileImport, getMultipleFilesOverwriteConfirm } from 'vs/workbench/contrib/files/browser/fileImportExport'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; +import { IgnoreFile } from 'vs/workbench/services/search/common/ignoreFile'; +import { ResourceSet, TernarySearchTree } from 'vs/base/common/map'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -605,20 +607,39 @@ export class FilesFilter implements ITreeFilter { private editorsAffectingFilter = new Set(); private _onDidChange = new Emitter(); private toDispose: IDisposable[] = []; + // List of ignoreFile resources. Used to detect changes to the ignoreFiles. + private ignoreFileResourcesPerRoot = new Map(); + // Ignore tree per root. Similar to `hiddenExpressionPerRoot` + private ignoreTreesPerRoot = new Map>(); constructor( @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IConfigurationService private readonly configurationService: IConfigurationService, @IExplorerService private readonly explorerService: IExplorerService, @IEditorService private readonly editorService: IEditorService, - @IUriIdentityService private readonly uriIdentityService: IUriIdentityService + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @IFileService private readonly fileService: IFileService ) { this.toDispose.push(this.contextService.onDidChangeWorkspaceFolders(() => this.updateConfiguration())); this.toDispose.push(this.configurationService.onDidChangeConfiguration((e) => { - if (e.affectsConfiguration('files.exclude')) { + if (e.affectsConfiguration('files.exclude') || e.affectsConfiguration('files.excludeGitIgnore')) { this.updateConfiguration(); } })); + this.toDispose.push(this.fileService.onDidFilesChange(e => { + // Check to see if the update contains any of the ignoreFileResources + for (const [root, ignoreFileResourceSet] of this.ignoreFileResourcesPerRoot.entries()) { + ignoreFileResourceSet.forEach(async ignoreResource => { + if (e.contains(ignoreResource, FileChangeType.UPDATED)) { + await this.processIgnoreFile(root, ignoreResource, true); + } + if (e.contains(ignoreResource, FileChangeType.DELETED)) { + this.ignoreTreesPerRoot.get(root)?.delete(ignoreResource); + ignoreFileResourceSet.delete(ignoreResource); + } + }); + } + })); this.toDispose.push(this.editorService.onDidVisibleEditorsChange(() => { const editors = this.editorService.visibleEditors; let shouldFire = false; @@ -658,9 +679,25 @@ export class FilesFilter implements ITreeFilter { private updateConfiguration(): void { let shouldFire = false; + let updatedGitIgnoreSetting = false; this.contextService.getWorkspace().folders.forEach(folder => { const configuration = this.configurationService.getValue({ resource: folder.uri }); const excludesConfig: glob.IExpression = configuration?.files?.exclude || Object.create(null); + const parseIgnoreFile: boolean = configuration.files.excludeGitIgnore; + + // If we should be parsing ignoreFiles for this workspace and don't have an ignore tree initialize one + if (parseIgnoreFile && !this.ignoreTreesPerRoot.has(folder.uri.toString())) { + updatedGitIgnoreSetting = true; + this.ignoreFileResourcesPerRoot.set(folder.uri.toString(), new ResourceSet()); + this.ignoreTreesPerRoot.set(folder.uri.toString(), TernarySearchTree.forUris((uri) => this.uriIdentityService.extUri.ignorePathCasing(uri))); + } + + // If we shouldn't be parsing ignore files but have an ignore tree, clear the ignore tree + if (!parseIgnoreFile && this.ignoreTreesPerRoot.has(folder.uri.toString())) { + updatedGitIgnoreSetting = true; + this.ignoreFileResourcesPerRoot.delete(folder.uri.toString()); + this.ignoreTreesPerRoot.delete(folder.uri.toString()); + } if (!shouldFire) { const cached = this.hiddenExpressionPerRoot.get(folder.uri.toString()); @@ -672,13 +709,54 @@ export class FilesFilter implements ITreeFilter { this.hiddenExpressionPerRoot.set(folder.uri.toString(), { original: excludesConfigCopy, parsed: glob.parse(excludesConfigCopy) }); }); - if (shouldFire) { + if (shouldFire || updatedGitIgnoreSetting) { this.editorsAffectingFilter.clear(); this._onDidChange.fire(); } } + /** + * Given a .gitignore file resource, processes the resource and adds it to the ignore tree which hides explorer items + * @param root The root folder of the workspace as a string. Used for lookup key for ignore tree and resource list + * @param ignoreFileResource The resource of the .gitignore file + * @param update Whether or not we're updating an existing ignore file. If true it deletes the old entry + */ + private async processIgnoreFile(root: string, ignoreFileResource: URI, update?: boolean) { + // Get the name of the directory which the ignore file is in + const dirUri = dirname(ignoreFileResource); + const ignoreTree = this.ignoreTreesPerRoot.get(root); + if (!ignoreTree) { + return; + } + // If it's an update we remove the stale ignore file as we will be adding a new one + if (update) { + ignoreTree.delete(dirUri); + } + // Don't process a directory if we already have it in the tree + if (ignoreTree.has(dirUri)) { + return; + } + // Maybe we need a cancellation token here in case it's super long? + const content = await this.fileService.readFile(ignoreFileResource); + const ignoreParent = ignoreTree.findSubstr(dirUri); + const ignoreFile = new IgnoreFile(content.value.toString(), dirUri.path + path.sep, ignoreParent); + ignoreTree.set(dirUri, ignoreFile); + // If we haven't seen this resource before then we need to add it to the list of resources we're tracking + if (!this.ignoreFileResourcesPerRoot.get(root)?.has(ignoreFileResource)) { + this.ignoreFileResourcesPerRoot.get(root)?.add(ignoreFileResource); + } + // Notify the explorer of the change so we may ignore these files + this._onDidChange.fire(); + } + filter(stat: ExplorerItem, parentVisibility: TreeVisibility): boolean { + // Add newly visited .gitignore files to the ignore tree + if (stat.name === '.gitignore') { + this.processIgnoreFile(stat.root.resource.toString(), stat.resource, false); + // Never hide .gitignore files + return true; + } + return this.isVisible(stat, parentVisibility); } @@ -694,7 +772,11 @@ export class FilesFilter implements ITreeFilter { // Hide those that match Hidden Patterns const cached = this.hiddenExpressionPerRoot.get(stat.root.resource.toString()); - if ((cached && cached.parsed(path.relative(stat.root.resource.path, stat.resource.path), stat.name, name => !!(stat.parent && stat.parent.getChild(name)))) || stat.parent?.isExcluded) { + const globMatch = cached?.parsed(path.relative(stat.root.resource.path, stat.resource.path), stat.name, name => !!(stat.parent && stat.parent.getChild(name))); + // Small optimization to only traverse gitIgnore if the globMatch from fileExclude returned nothing + const ignoreFile = globMatch ? undefined : this.ignoreTreesPerRoot.get(stat.root.resource.toString())?.findSubstr(stat.resource); + const isIgnoredByIgnoreFile = !ignoreFile?.isPathIncludedInTraversal(stat.resource.path, stat.isDirectory); + if (isIgnoredByIgnoreFile || globMatch || stat.parent?.isExcluded) { stat.isExcluded = true; const editors = this.editorService.visibleEditors; const editor = editors.find(e => e.resource && this.uriIdentityService.extUri.isEqualOrParent(e.resource, stat.resource)); From 5062feaeaa99c45da77574e97a09d3a6f91401ae Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Fri, 20 May 2022 13:27:46 -0700 Subject: [PATCH 200/942] Fix #150028 (#150053) --- src/vs/workbench/contrib/files/browser/explorerService.ts | 2 +- src/vs/workbench/contrib/files/browser/files.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/explorerService.ts b/src/vs/workbench/contrib/files/browser/explorerService.ts index a1d5910f284a6..5b25d2e55903e 100644 --- a/src/vs/workbench/contrib/files/browser/explorerService.ts +++ b/src/vs/workbench/contrib/files/browser/explorerService.ts @@ -156,7 +156,7 @@ export class ExplorerService implements IExplorerService { const items = new Set(this.view.getContext(respectMultiSelection)); items.forEach(item => { - if (this.view?.isItemCollapsed(item) && item.nestedChildren) { + if (respectMultiSelection && this.view?.isItemCollapsed(item) && item.nestedChildren) { for (const child of item.nestedChildren) { items.add(child); } diff --git a/src/vs/workbench/contrib/files/browser/files.ts b/src/vs/workbench/contrib/files/browser/files.ts index b4b01687527dd..83b5e3b6d6b91 100644 --- a/src/vs/workbench/contrib/files/browser/files.ts +++ b/src/vs/workbench/contrib/files/browser/files.ts @@ -23,7 +23,7 @@ export interface IExplorerService { readonly roots: ExplorerItem[]; readonly sortOrderConfiguration: ISortOrderConfiguration; - getContext(respectMultiSelection: boolean, includeNestedChildren?: boolean): ExplorerItem[]; + getContext(respectMultiSelection: boolean): ExplorerItem[]; hasViewFocus(): boolean; setEditable(stat: ExplorerItem, data: IEditableData | null): Promise; getEditable(): { stat: ExplorerItem; data: IEditableData } | undefined; From 1f5563fe8e8a7afabd1e05f938af599d527be753 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 May 2022 13:35:42 -0700 Subject: [PATCH 201/942] Fix unit test to use new correct uri path --- .../terminal/test/browser/links/terminalLinkOpeners.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts index b6efab0951fb1..a67d0f4b0d2c5 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts @@ -177,7 +177,7 @@ suite('Workbench - TerminalLinkOpeners', () => { type: TerminalBuiltinLinkType.Search }); deepStrictEqual(activationResult, { - link: 'file:///c%3A%5CUsers%5Chome%5Cfolder%5Cfile.txt', + link: 'file:///c%3A/Users/home/folder/file.txt', source: 'editor' }); From fdeba7d869aeb247aa5b26d7f02fc5b5c1b392df Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 20 May 2022 13:40:39 -0700 Subject: [PATCH 202/942] fix task status issues (#149976) --- .../tasks/browser/taskTerminalStatus.ts | 39 +++--- .../test/browser/taskTerminalStatus.test.ts | 117 ++++++++++++++++++ 2 files changed, 140 insertions(+), 16 deletions(-) create mode 100644 src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts diff --git a/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts b/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts index ced0fd8cf8f6a..3c6dc5ca45811 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts @@ -5,10 +5,10 @@ import * as nls from 'vs/nls'; import { Codicon } from 'vs/base/common/codicons'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import Severity from 'vs/base/common/severity'; import { AbstractProblemCollector, StartStopProblemCollector } from 'vs/workbench/contrib/tasks/common/problemCollectors'; -import { TaskEvent, TaskEventKind } from 'vs/workbench/contrib/tasks/common/tasks'; +import { TaskEvent, TaskEventKind, TaskRunType } from 'vs/workbench/contrib/tasks/common/tasks'; import { ITaskService, Task } from 'vs/workbench/contrib/tasks/common/taskService'; import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ITerminalStatus } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; @@ -17,15 +17,18 @@ import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; interface TerminalData { terminal: ITerminalInstance; + task: Task; status: ITerminalStatus; problemMatcher: AbstractProblemCollector; + taskRunEnded: boolean; + disposeListener?: IDisposable; } const TASK_TERMINAL_STATUS_ID = 'task_terminal_status'; -const ACTIVE_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: spinningLoading, severity: Severity.Info, tooltip: nls.localize('taskTerminalStatus.active', "Task is running") }; -const SUCCEEDED_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: Codicon.check, severity: Severity.Info, tooltip: nls.localize('taskTerminalStatus.succeeded', "Task succeeded") }; +export const ACTIVE_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: spinningLoading, severity: Severity.Info, tooltip: nls.localize('taskTerminalStatus.active', "Task is running") }; +export const SUCCEEDED_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: Codicon.check, severity: Severity.Info, tooltip: nls.localize('taskTerminalStatus.succeeded', "Task succeeded") }; const SUCCEEDED_INACTIVE_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: Codicon.check, severity: Severity.Info, tooltip: nls.localize('taskTerminalStatus.succeededInactive', "Task succeeded and waiting...") }; -const FAILED_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: Codicon.error, severity: Severity.Error, tooltip: nls.localize('taskTerminalStatus.errors', "Task has errors") }; +export const FAILED_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: Codicon.error, severity: Severity.Error, tooltip: nls.localize('taskTerminalStatus.errors', "Task has errors") }; const FAILED_INACTIVE_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: Codicon.error, severity: Severity.Error, tooltip: nls.localize('taskTerminalStatus.errorsInactive', "Task has errors and is waiting...") }; const WARNING_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: Codicon.warning, severity: Severity.Warning, tooltip: nls.localize('taskTerminalStatus.warnings', "Task has warnings") }; const WARNING_INACTIVE_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: Codicon.warning, severity: Severity.Warning, tooltip: nls.localize('taskTerminalStatus.warningsInactive', "Task has warnings and is waiting...") }; @@ -33,7 +36,7 @@ const INFO_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: C const INFO_INACTIVE_TASK_STATUS: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, icon: Codicon.info, severity: Severity.Info, tooltip: nls.localize('taskTerminalStatus.infosInactive', "Task has infos and is waiting...") }; export class TaskTerminalStatus extends Disposable { - private terminalMap: Map = new Map(); + private terminalMap: Map = new Map(); constructor(taskService: ITaskService) { super(); @@ -50,15 +53,15 @@ export class TaskTerminalStatus extends Disposable { addTerminal(task: Task, terminal: ITerminalInstance, problemMatcher: AbstractProblemCollector) { const status: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, severity: Severity.Info }; terminal.statusList.add(status); - this.terminalMap.set(task, { terminal, status, problemMatcher }); + this.terminalMap.set(task._id, { terminal, task, status, problemMatcher, taskRunEnded: false }); } private terminalFromEvent(event: TaskEvent): TerminalData | undefined { - if (!event.__task || !this.terminalMap.get(event.__task)) { + if (!event.__task) { return undefined; } - return this.terminalMap.get(event.__task); + return this.terminalMap.get(event.__task._id); } private eventEnd(event: TaskEvent) { @@ -66,13 +69,11 @@ export class TaskTerminalStatus extends Disposable { if (!terminalData) { return; } - - this.terminalMap.delete(event.__task!); - + terminalData.taskRunEnded = true; terminalData.terminal.statusList.remove(terminalData.status); if ((event.exitCode === 0) && (terminalData.problemMatcher.numberOfMatches === 0)) { terminalData.terminal.statusList.add(SUCCEEDED_TASK_STATUS); - } else if (terminalData.problemMatcher.maxMarkerSeverity === MarkerSeverity.Error) { + } else if (event.exitCode || terminalData.problemMatcher.maxMarkerSeverity === MarkerSeverity.Error) { terminalData.terminal.statusList.add(FAILED_TASK_STATUS); } else if (terminalData.problemMatcher.maxMarkerSeverity === MarkerSeverity.Warning) { terminalData.terminal.statusList.add(WARNING_TASK_STATUS); @@ -83,7 +84,7 @@ export class TaskTerminalStatus extends Disposable { private eventInactive(event: TaskEvent) { const terminalData = this.terminalFromEvent(event); - if (!terminalData || !terminalData.problemMatcher) { + if (!terminalData || !terminalData.problemMatcher || terminalData.taskRunEnded) { return; } terminalData.terminal.statusList.remove(terminalData.status); @@ -103,10 +104,16 @@ export class TaskTerminalStatus extends Disposable { if (!terminalData) { return; } - + if (!terminalData.disposeListener) { + terminalData.disposeListener = terminalData.terminal.onDisposed(() => { + this.terminalMap.delete(event.__task?._id!); + terminalData.disposeListener?.dispose(); + }); + } + terminalData.taskRunEnded = false; terminalData.terminal.statusList.remove(terminalData.status); // We don't want to show an infinite status for a background task that doesn't have a problem matcher. - if ((terminalData.problemMatcher instanceof StartStopProblemCollector) || (terminalData.problemMatcher?.problemMatchers.length > 0)) { + if ((terminalData.problemMatcher instanceof StartStopProblemCollector) || (terminalData.problemMatcher?.problemMatchers.length > 0) || event.runType === TaskRunType.SingleRun) { terminalData.terminal.statusList.add(ACTIVE_TASK_STATUS); } } diff --git a/src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts b/src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts new file mode 100644 index 0000000000000..3b00f83eaf5fc --- /dev/null +++ b/src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ok } from 'assert'; +import { Emitter, Event } from 'vs/base/common/event'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { ACTIVE_TASK_STATUS, FAILED_TASK_STATUS, SUCCEEDED_TASK_STATUS, TaskTerminalStatus } from 'vs/workbench/contrib/tasks/browser/taskTerminalStatus'; +import { AbstractProblemCollector } from 'vs/workbench/contrib/tasks/common/problemCollectors'; +import { CommonTask, TaskEvent, TaskEventKind, TaskRunType } from 'vs/workbench/contrib/tasks/common/tasks'; +import { ITaskService, Task } from 'vs/workbench/contrib/tasks/common/taskService'; +import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalStatus, ITerminalStatusList, TerminalStatusList } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; + +class TestTaskService implements Partial { + private readonly _onDidStateChange: Emitter = new Emitter(); + public get onDidStateChange(): Event { + return this._onDidStateChange.event; + } + public triggerStateChange(event: TaskEvent): void { + this._onDidStateChange.fire(event); + } +} + +class TestTerminal implements Partial { + statusList: TerminalStatusList = new TerminalStatusList(new TestConfigurationService()); +} + +class TestTask extends CommonTask { + protected getFolderId(): string | undefined { + throw new Error('Method not implemented.'); + } + protected fromObject(object: any): Task { + throw new Error('Method not implemented.'); + } +} + +class TestProblemCollector implements Partial { + +} + +suite('Task Terminal Status', () => { + let instantiationService: TestInstantiationService; + let taskService: TestTaskService; + let taskTerminalStatus: TaskTerminalStatus; + let testTerminal: ITerminalInstance; + let testTask: Task; + let problemCollector: AbstractProblemCollector; + setup(() => { + instantiationService = new TestInstantiationService(); + taskService = new TestTaskService(); + taskTerminalStatus = instantiationService.createInstance(TaskTerminalStatus, taskService); + testTerminal = instantiationService.createInstance(TestTerminal); + testTask = instantiationService.createInstance(TestTask); + problemCollector = instantiationService.createInstance(TestProblemCollector); + }); + test('Should add failed status when there is an exit code on task end', async () => { + taskTerminalStatus.addTerminal(testTask, testTerminal, problemCollector); + taskService.triggerStateChange({ kind: TaskEventKind.ProcessStarted }); + assertStatus(testTerminal.statusList, ACTIVE_TASK_STATUS); + taskService.triggerStateChange({ kind: TaskEventKind.Inactive }); + assertStatus(testTerminal.statusList, SUCCEEDED_TASK_STATUS); + taskService.triggerStateChange({ kind: TaskEventKind.End, exitCode: 2 }); + await poll(async () => Promise.resolve(), () => testTerminal?.statusList.primary?.id === FAILED_TASK_STATUS.id, 'terminal status should be updated'); + }); + test('Should add active status when a non-background task is run for a second time in the same terminal', async () => { + taskTerminalStatus.addTerminal(testTask, testTerminal, problemCollector); + taskService.triggerStateChange({ kind: TaskEventKind.ProcessStarted }); + assertStatus(testTerminal.statusList, ACTIVE_TASK_STATUS); + taskService.triggerStateChange({ kind: TaskEventKind.Inactive }); + assertStatus(testTerminal.statusList, SUCCEEDED_TASK_STATUS); + taskService.triggerStateChange({ kind: TaskEventKind.ProcessStarted, runType: TaskRunType.SingleRun }); + assertStatus(testTerminal.statusList, ACTIVE_TASK_STATUS); + taskService.triggerStateChange({ kind: TaskEventKind.Inactive }); + assertStatus(testTerminal.statusList, SUCCEEDED_TASK_STATUS); + }); +}); + +function assertStatus(actual: ITerminalStatusList, expected: ITerminalStatus): void { + ok(actual.statuses.length === 1, '# of statuses'); + ok(actual.primary?.id === expected.id, 'ID'); + ok(actual.primary?.severity === expected.severity, 'Severity'); +} + +async function poll( + fn: () => Thenable, + acceptFn: (result: T) => boolean, + timeoutMessage: string, + retryCount: number = 200, + retryInterval: number = 10 // millis +): Promise { + let trial = 1; + let lastError: string = ''; + + while (true) { + if (trial > retryCount) { + throw new Error(`Timeout: ${timeoutMessage} after ${(retryCount * retryInterval) / 1000} seconds.\r${lastError}`); + } + + let result; + try { + result = await fn(); + if (acceptFn(result)) { + return result; + } else { + lastError = 'Did not pass accept function'; + } + } catch (e: any) { + lastError = Array.isArray(e.stack) ? e.stack.join('\n') : e.stack; + } + + await new Promise(resolve => setTimeout(resolve, retryInterval)); + trial++; + } +} From ecf7f9cfd9ab5460cc2203fb1eb56ae2569157e6 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 20 May 2022 17:34:17 -0400 Subject: [PATCH 203/942] Add scope resource (#150056) --- src/vs/workbench/contrib/files/browser/files.contribution.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 15e889898d2bc..cd4f1fd7ee56d 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -188,8 +188,9 @@ configurationRegistry.registerConfiguration({ }, 'files.excludeGitIgnore': { type: 'boolean', - description: nls.localize('excludeGitignore', "Controls whether entries in .gitignore should be parsed and excluded from the explorer. Similar to `files.exclude`."), - default: false + markdownDescription: nls.localize('excludeGitignore', "Controls whether entries in .gitignore should be parsed and excluded from the explorer. Similar to `#files.exclude#`."), + default: false, + scope: ConfigurationScope.RESOURCE }, 'files.eol': { 'type': 'string', From 96cae45ba98cf6f221e7933b535cd9e9b6a09e2e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 20 May 2022 15:54:21 -0700 Subject: [PATCH 204/942] Rename skipPaths to ignoreLinks (#150064) This change renames the experimental skipPaths setting to ignoreLinks. This setting applies to all types of links, and can also be used to ignore links to headers --- .../markdown-language-features/package.json | 4 +- .../package.nls.json | 2 +- .../src/languageFeatures/diagnostics.ts | 57 +++++++++++-------- .../src/test/diagnostic.test.ts | 43 +++++++++++--- 4 files changed, 71 insertions(+), 35 deletions(-) diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index b1d4d1bc52976..4ef07a0dd65cd 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -453,10 +453,10 @@ "error" ] }, - "markdown.experimental.validate.fileLinks.skipPaths": { + "markdown.experimental.validate.ignoreLinks": { "type": "array", "scope": "resource", - "markdownDescription": "%configuration.markdown.experimental.validate.fileLinks.skipPaths.description%", + "markdownDescription": "%configuration.markdown.experimental.validate.ignoreLinks.description%", "items": { "type": "string" } diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index 5a97389a86a17..2c8977cf2e015 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -33,6 +33,6 @@ "configuration.markdown.experimental.validate.referenceLinks.enabled.description": "Validate reference links in Markdown files, e.g. `[link][ref]`. Requires enabling `#markdown.experimental.validate.enabled#`.", "configuration.markdown.experimental.validate.headerLinks.enabled.description": "Validate links to headers in Markdown files, e.g. `[link](#header)`. Requires enabling `#markdown.experimental.validate.enabled#`.", "configuration.markdown.experimental.validate.fileLinks.enabled.description": "Validate links to other files in Markdown files, e.g. `[link](/path/to/file.md)`. This checks that the target files exists. Requires enabling `#markdown.experimental.validate.enabled#`.", - "configuration.markdown.experimental.validate.fileLinks.skipPaths.description": "Configure glob patterns for links to treat as valid, even if they don't exist in the workspace. For example `/about` would make the link `[about](/about)` valid, while the glob `/assets/**/*.svg` would let you link to any `.svg` asset under the `assets` directory", + "configuration.markdown.experimental.validate.ignoreLinks.description": "Configure links that should not be validated. For example `/about` would not validate the link `[about](/about)`, while the glob `/assets/**/*.svg` would let you skip validation for any link to `.svg` files under the `assets` directory.", "workspaceTrust": "Required for loading styles configured in the workspace." } diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index eb4c436afd57b..89e6b29f24039 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -39,7 +39,7 @@ export interface DiagnosticOptions { readonly validateReferences: DiagnosticLevel; readonly validateOwnHeaders: DiagnosticLevel; readonly validateFilePaths: DiagnosticLevel; - readonly skipPaths: readonly string[]; + readonly ignoreLinks: readonly string[]; } function toSeverity(level: DiagnosticLevel): vscode.DiagnosticSeverity | undefined { @@ -64,7 +64,7 @@ class VSCodeDiagnosticConfiguration extends Disposable implements DiagnosticConf || e.affectsConfiguration('markdown.experimental.validate.referenceLinks.enabled') || e.affectsConfiguration('markdown.experimental.validate.headerLinks.enabled') || e.affectsConfiguration('markdown.experimental.validate.fileLinks.enabled') - || e.affectsConfiguration('markdown.experimental.validate.fileLinks.skipPaths') + || e.affectsConfiguration('markdown.experimental.validate.ignoreLinks') ) { this._onDidChange.fire(); } @@ -78,7 +78,7 @@ class VSCodeDiagnosticConfiguration extends Disposable implements DiagnosticConf validateReferences: config.get('experimental.validate.referenceLinks.enabled', DiagnosticLevel.ignore), validateOwnHeaders: config.get('experimental.validate.headerLinks.enabled', DiagnosticLevel.ignore), validateFilePaths: config.get('experimental.validate.fileLinks.enabled', DiagnosticLevel.ignore), - skipPaths: config.get('experimental.validate.fileLinks.skipPaths', []), + ignoreLinks: config.get('experimental.validate.ignoreLinks', []), }; } } @@ -216,13 +216,13 @@ class LinkWatcher extends Disposable { } } -class FileDoesNotExistDiagnostic extends vscode.Diagnostic { +class LinkDoesNotExistDiagnostic extends vscode.Diagnostic { - public readonly path: string; + public readonly link: string; - constructor(range: vscode.Range, message: string, severity: vscode.DiagnosticSeverity, path: string) { + constructor(range: vscode.Range, message: string, severity: vscode.DiagnosticSeverity, link: string) { super(range, message, severity); - this.path = path; + this.link = link; } } @@ -424,10 +424,13 @@ export class DiagnosticComputer { && link.href.fragment && !toc.lookup(link.href.fragment) ) { - diagnostics.push(new vscode.Diagnostic( - link.source.hrefRange, - localize('invalidHeaderLink', 'No header found: \'{0}\'', link.href.fragment), - severity)); + if (!this.isIgnoredLink(options, link.source.text)) { + diagnostics.push(new LinkDoesNotExistDiagnostic( + link.source.hrefRange, + localize('invalidHeaderLink', 'No header found: \'{0}\'', link.href.fragment), + severity, + link.source.text)); + } } } @@ -481,8 +484,8 @@ export class DiagnosticComputer { if (!hrefDoc && !await this.workspaceContents.pathExists(path)) { const msg = localize('invalidPathLink', 'File does not exist at path: {0}', path.fsPath); for (const link of links) { - if (!options.skipPaths.some(glob => picomatch.isMatch(link.source.pathText, glob))) { - diagnostics.push(new FileDoesNotExistDiagnostic(link.source.hrefRange, msg, severity, link.source.pathText)); + if (!this.isIgnoredLink(options, link.source.pathText)) { + diagnostics.push(new LinkDoesNotExistDiagnostic(link.source.hrefRange, msg, severity, link.source.pathText)); } } } else if (hrefDoc) { @@ -491,9 +494,9 @@ export class DiagnosticComputer { if (fragmentLinks.length) { const toc = await TableOfContents.create(this.engine, hrefDoc); for (const link of fragmentLinks) { - if (!toc.lookup(link.fragment)) { + if (!toc.lookup(link.fragment) && !this.isIgnoredLink(options, link.source.text)) { const msg = localize('invalidLinkToHeaderInOtherFile', 'Header does not exist in file: {0}', link.fragment); - diagnostics.push(new vscode.Diagnostic(link.source.hrefRange, msg, severity)); + diagnostics.push(new LinkDoesNotExistDiagnostic(link.source.hrefRange, msg, severity, link.source.text)); } } } @@ -502,11 +505,15 @@ export class DiagnosticComputer { })); return diagnostics; } + + private isIgnoredLink(options: DiagnosticOptions, link: string): boolean { + return options.ignoreLinks.some(glob => picomatch.isMatch(link, glob)); + } } -class AddToSkipPathsQuickFixProvider implements vscode.CodeActionProvider { +class AddToIgnoreLinksQuickFixProvider implements vscode.CodeActionProvider { - private static readonly _addToSkipPathsCommandId = '_markdown.addToSkipPaths'; + private static readonly _addToIgnoreLinksCommandId = '_markdown.addToIgnoreLinks'; private static readonly metadata: vscode.CodeActionProviderMetadata = { providedCodeActionKinds: [ @@ -515,11 +522,11 @@ class AddToSkipPathsQuickFixProvider implements vscode.CodeActionProvider { }; public static register(selector: vscode.DocumentSelector, commandManager: CommandManager): vscode.Disposable { - const reg = vscode.languages.registerCodeActionsProvider(selector, new AddToSkipPathsQuickFixProvider(), AddToSkipPathsQuickFixProvider.metadata); + const reg = vscode.languages.registerCodeActionsProvider(selector, new AddToIgnoreLinksQuickFixProvider(), AddToIgnoreLinksQuickFixProvider.metadata); const commandReg = commandManager.register({ - id: AddToSkipPathsQuickFixProvider._addToSkipPathsCommandId, + id: AddToIgnoreLinksQuickFixProvider._addToIgnoreLinksCommandId, execute(resource: vscode.Uri, path: string) { - const settingId = 'experimental.validate.fileLinks.skipPaths'; + const settingId = 'experimental.validate.ignoreLinks'; const config = vscode.workspace.getConfiguration('markdown', resource); const paths = new Set(config.get(settingId, [])); paths.add(path); @@ -533,15 +540,15 @@ class AddToSkipPathsQuickFixProvider implements vscode.CodeActionProvider { const fixes: vscode.CodeAction[] = []; for (const diagnostic of context.diagnostics) { - if (diagnostic instanceof FileDoesNotExistDiagnostic) { + if (diagnostic instanceof LinkDoesNotExistDiagnostic) { const fix = new vscode.CodeAction( - localize('skipPathsQuickFix.title', "Add '{0}' to paths that skip link validation.", diagnostic.path), + localize('ignoreLinksQuickFix.title', "Exclude '{0}' from link validation.", diagnostic.link), vscode.CodeActionKind.QuickFix); fix.command = { - command: AddToSkipPathsQuickFixProvider._addToSkipPathsCommandId, + command: AddToIgnoreLinksQuickFixProvider._addToIgnoreLinksCommandId, title: '', - arguments: [document.uri, diagnostic.path] + arguments: [document.uri, diagnostic.link] }; fixes.push(fix); } @@ -563,5 +570,5 @@ export function register( return vscode.Disposable.from( configuration, manager, - AddToSkipPathsQuickFixProvider.register(selector, commandManager)); + AddToIgnoreLinksQuickFixProvider.register(selector, commandManager)); } diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index e58dabd983e90..1490df29279b5 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -26,7 +26,7 @@ async function getComputedDiagnostics(doc: InMemoryDocument, workspaceContents: validateFilePaths: DiagnosticLevel.warning, validateOwnHeaders: DiagnosticLevel.warning, validateReferences: DiagnosticLevel.warning, - skipPaths: [], + ignoreLinks: [], }, noopToken) ).diagnostics; } @@ -44,7 +44,7 @@ class MemoryDiagnosticConfiguration implements DiagnosticConfiguration { constructor( private readonly enabled: boolean = true, - private readonly skipPaths: string[] = [], + private readonly ignoreLinks: string[] = [], ) { } getOptions(_resource: vscode.Uri): DiagnosticOptions { @@ -54,7 +54,7 @@ class MemoryDiagnosticConfiguration implements DiagnosticConfiguration { validateFilePaths: DiagnosticLevel.ignore, validateOwnHeaders: DiagnosticLevel.ignore, validateReferences: DiagnosticLevel.ignore, - skipPaths: this.skipPaths, + ignoreLinks: this.ignoreLinks, }; } return { @@ -62,7 +62,7 @@ class MemoryDiagnosticConfiguration implements DiagnosticConfiguration { validateFilePaths: DiagnosticLevel.warning, validateOwnHeaders: DiagnosticLevel.warning, validateReferences: DiagnosticLevel.warning, - skipPaths: this.skipPaths, + ignoreLinks: this.ignoreLinks, }; } } @@ -196,7 +196,7 @@ suite('markdown: Diagnostics', () => { assert.deepStrictEqual(diagnostics.length, 0); }); - test('skipPaths should allow skipping non-existent file', async () => { + test('ignoreLinks should allow skipping link to non-existent file', async () => { const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( `[text](/no-such-file#header)`, )); @@ -206,7 +206,7 @@ suite('markdown: Diagnostics', () => { assert.deepStrictEqual(diagnostics.length, 0); }); - test('skipPaths should not consider link fragment', async () => { + test('ignoreLinks should not consider link fragment', async () => { const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( `[text](/no-such-file#header)`, )); @@ -216,7 +216,7 @@ suite('markdown: Diagnostics', () => { assert.deepStrictEqual(diagnostics.length, 0); }); - test('skipPaths should support globs', async () => { + test('ignoreLinks should support globs', async () => { const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( `![i](/images/aaa.png)`, `![i](/images/sub/bbb.png)`, @@ -227,4 +227,33 @@ suite('markdown: Diagnostics', () => { const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); assert.deepStrictEqual(diagnostics.length, 0); }); + + test('ignoreLinks should support ignoring header', async () => { + const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( + `![i](#no-such)`, + )); + + const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration(true, ['#no-such'])); + const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); + assert.deepStrictEqual(diagnostics.length, 0); + }); + + test('ignoreLinks should support ignoring header in file', async () => { + const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( + `![i](/doc2.md#no-such)`, + )); + const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines('')); + + const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + { + const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration(true, ['/doc2.md#no-such'])); + const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); + assert.deepStrictEqual(diagnostics.length, 0); + } + { + const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration(true, ['/doc2.md#*'])); + const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); + assert.deepStrictEqual(diagnostics.length, 0); + } + }); }); From 2796b3425ddf413a33937b1167cb96cee544904f Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 20 May 2022 16:28:03 -0700 Subject: [PATCH 205/942] Allow single source action to be executed without picking --- .../browser/notebookExecutionServiceImpl.ts | 21 +++++++++++- .../viewParts/notebookKernelActionViewItem.ts | 32 ++++++++++++++----- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts index c92696d34ebab..aa57b01e95a9d 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts @@ -14,7 +14,7 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/mode import { CellKind, INotebookTextModel, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; export class NotebookExecutionService implements INotebookExecutionService, IDisposable { declare _serviceBrand: undefined; @@ -39,6 +39,10 @@ export class NotebookExecutionService implements INotebookExecutionService, IDis } let kernel = this._notebookKernelService.getSelectedOrSuggestedKernel(notebook); + if (!kernel) { + kernel = await this.resolveSourceActions(notebook); + } + if (!kernel) { await this._commandService.executeCommand(SELECT_KERNEL_ID); kernel = this._notebookKernelService.getSelectedOrSuggestedKernel(notebook); @@ -86,6 +90,21 @@ export class NotebookExecutionService implements INotebookExecutionService, IDis } } + private async resolveSourceActions(notebook: INotebookTextModel) { + let kernel: INotebookKernel | undefined; + const info = this._notebookKernelService.getMatchingKernel(notebook); + if (info.all.length === 0) { + // no kernel at all + const sourceActions = this._notebookKernelService.getSourceActions(); + if (sourceActions.length === 1) { + await this._notebookKernelService.runSourceAction(sourceActions[0]); + kernel = this._notebookKernelService.getSelectedOrSuggestedKernel(notebook); + } + } + + return kernel; + } + async cancelNotebookCellHandles(notebook: INotebookTextModel, cells: Iterable): Promise { const cellsArr = Array.from(cells); this._logService.debug(`NotebookExecutionService#cancelNotebookCellHandles ${JSON.stringify(cellsArr)}`); diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts index 4f23f001c3a22..060e17ac37a4c 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts @@ -64,25 +64,41 @@ export class NotebooKernelActionViewItem extends ActionViewItem { const runningAction = this._notebookKernelService.getRunningSourceAction(); if (runningAction) { - this._updateActionFromSourceAction(runningAction); - return; - } else { - this.action.class = ThemeIcon.asClassName(selectKernelIcon); - const info = this._notebookKernelService.getMatchingKernel(notebook); - this._updateActionFromKernelInfo(info); + return this._updateActionFromSourceAction(runningAction, true); + } + + const info = this._notebookKernelService.getMatchingKernel(notebook); + if (info.all.length === 0) { + return this._updateActionsFromSourceActions(); } + + this._updateActionFromKernelInfo(info); } - private _updateActionFromSourceAction(sourceAction: IAction) { - this.action.class = ThemeIcon.asClassName(ThemeIcon.modify(executingStateIcon, 'spin')); + private _updateActionFromSourceAction(sourceAction: IAction, running: boolean) { + this.action.class = running ? ThemeIcon.asClassName(ThemeIcon.modify(executingStateIcon, 'spin')) : ThemeIcon.asClassName(selectKernelIcon); this.updateClass(); this._action.label = sourceAction.label; this._action.enabled = true; } + private _updateActionsFromSourceActions() { + this._action.enabled = true; + const sourceActions = this._notebookKernelService.getSourceActions(); + if (sourceActions.length === 1) { + // exact one action + this._updateActionFromSourceAction(sourceActions[0], false); + } else { + this._action.class = ThemeIcon.asClassName(selectKernelIcon); + this._action.label = localize('select', "Select Kernel"); + this._action.tooltip = ''; + } + } + private _updateActionFromKernelInfo(info: INotebookKernelMatchResult): void { this._kernelDisposable.clear(); this._action.enabled = true; + this._action.class = ThemeIcon.asClassName(selectKernelIcon); const selectedOrSuggested = info.selected ?? (info.suggestions.length === 1 ? info.suggestions[0] : undefined); if (selectedOrSuggested) { // selected or suggested kernel From 8c88eb6e5576bc352955f86a5a1c277bfe94f8f1 Mon Sep 17 00:00:00 2001 From: justanotheranonymoususer Date: Sat, 21 May 2022 02:33:43 +0300 Subject: [PATCH 206/942] Add extension output label to url --- src/vs/workbench/api/browser/mainThreadOutputService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/browser/mainThreadOutputService.ts b/src/vs/workbench/api/browser/mainThreadOutputService.ts index 34859bb4235ea..85f8495e80dac 100644 --- a/src/vs/workbench/api/browser/mainThreadOutputService.ts +++ b/src/vs/workbench/api/browser/mainThreadOutputService.ts @@ -44,7 +44,7 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut public async $register(label: string, log: boolean, file: UriComponents, languageId: string, extensionId: string): Promise { const idCounter = (MainThreadOutputService._extensionIdPool.get(extensionId) || 0) + 1; MainThreadOutputService._extensionIdPool.set(extensionId, idCounter); - const id = `extension-output-${extensionId}-#${idCounter}`; + const id = `extension-output-${extensionId}-#${idCounter}-${label}`; Registry.as(Extensions.OutputChannels).registerChannel({ id, label, file: URI.revive(file), log, languageId }); this._register(toDisposable(() => this.$dispose(id))); From 8d503ffaf4ffd70a1ea7329a6cced91661858ff3 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 20 May 2022 16:35:46 -0700 Subject: [PATCH 207/942] connection state in another pr --- .../api/browser/mainThreadNotebookKernels.ts | 16 ++-------------- src/vs/workbench/api/common/extHost.api.impl.ts | 1 - src/vs/workbench/api/common/extHost.protocol.ts | 6 ------ .../api/common/extHostNotebookKernels.ts | 9 --------- src/vs/workbench/api/common/extHostTypes.ts | 4 ---- .../contrib/editorStatusBar/editorStatusBar.ts | 4 ++-- .../browser/notebookExecutionServiceImpl.ts | 12 ------------ .../viewParts/notebookKernelActionViewItem.ts | 8 ++------ .../notebook/common/notebookKernelService.ts | 8 -------- .../browser/notebookExecutionService.test.ts | 4 +--- .../notebookExecutionStateService.test.ts | 1 - .../test/browser/notebookKernelService.test.ts | 4 +--- .../actions/common/menusExtensionPoint.ts | 3 ++- .../vscode.proposed.notebookProxyController.d.ts | 9 --------- 14 files changed, 10 insertions(+), 79 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index 38c81e4ba0e6d..b6ce5aaa0f839 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -17,14 +17,12 @@ import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/no import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookKernel, INotebookKernelChangeEvent, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; -import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape, NotebookControllerState } from '../common/extHost.protocol'; +import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol'; abstract class MainThreadKernel implements INotebookKernel { private readonly _onDidChange = new Emitter(); private readonly preloads: { uri: URI; provides: string[] }[]; readonly onDidChange: Event = this._onDidChange.event; - private readonly _onDispose = new Emitter(); - readonly onDispose = this._onDispose.event; readonly id: string; readonly viewType: string; readonly extension: ExtensionIdentifier; @@ -34,7 +32,6 @@ abstract class MainThreadKernel implements INotebookKernel { description?: string; detail?: string; kind?: string; - state?: NotebookControllerState; supportedLanguages: string[]; implementsExecutionOrder: boolean; localResourceRoot: URI; @@ -57,7 +54,6 @@ abstract class MainThreadKernel implements INotebookKernel { this.description = data.description; this.detail = data.detail; this.kind = data.kind; - this.state = data.state; this.supportedLanguages = isNonEmptyArray(data.supportedLanguages) ? data.supportedLanguages : _languageService.getRegisteredLanguageIds(); this.implementsExecutionOrder = data.supportsExecutionOrder ?? false; this.localResourceRoot = URI.revive(data.extensionLocation); @@ -84,10 +80,6 @@ abstract class MainThreadKernel implements INotebookKernel { this.kind = data.kind; event.kind = true; } - if (data.state !== undefined) { - this.state = data.state; - event.state = true; - } if (data.supportedLanguages !== undefined) { this.supportedLanguages = isNonEmptyArray(data.supportedLanguages) ? data.supportedLanguages : this._languageService.getRegisteredLanguageIds(); event.supportedLanguages = true; @@ -99,10 +91,6 @@ abstract class MainThreadKernel implements INotebookKernel { this._onDidChange.fire(event); } - dispose() { - this._onDispose.fire(); - } - abstract executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; abstract cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; } @@ -228,7 +216,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape }); const registration = this._notebookKernelService.registerKernel(kernel); - this._kernels.set(handle, [kernel, combinedDisposable(kernel, listener, registration)]); + this._kernels.set(handle, [kernel, combinedDisposable(listener, registration)]); } $updateKernel(handle: number, data: Partial): void { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 5140d9770d5e3..e4b23860c6eaf 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1315,7 +1315,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem, NotebookCellStatusBarItem: extHostTypes.NotebookCellStatusBarItem, NotebookControllerAffinity: extHostTypes.NotebookControllerAffinity, - NotebookControllerState: extHostTypes.NotebookControllerState, NotebookEdit: extHostTypes.NotebookEdit, PortAttributes: extHostTypes.PortAttributes, LinkedEditingRanges: extHostTypes.LinkedEditingRanges, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d2e87c6e7c60e..cff9c82244abc 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -970,11 +970,6 @@ export interface MainThreadNotebookDocumentsShape extends IDisposable { $trySaveNotebook(uri: UriComponents): Promise; } -export enum NotebookControllerState { - Idle = 1, - Connecting = 2 -} - export interface INotebookKernelDto2 { id: string; notebookType: string; @@ -984,7 +979,6 @@ export interface INotebookKernelDto2 { detail?: string; description?: string; kind?: string; - state?: NotebookControllerState; supportedLanguages?: string[]; supportsInterrupt?: boolean; supportsExecutionOrder?: boolean; diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 4e4ca9640c5a9..f99ead59ac7d7 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -196,15 +196,6 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { get rendererScripts() { return data.preloads ? data.preloads.map(extHostTypeConverters.NotebookRendererScript.to) : []; }, - get state() { - checkProposedApiEnabled(extension, 'notebookProxyController'); - return data.state; - }, - set state(value) { - checkProposedApiEnabled(extension, 'notebookProxyController'); - data.state = value; - _update(); - }, get executeHandler() { return _executeHandler; }, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index fd1f25581409a..039f43fce8b88 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3426,10 +3426,6 @@ export class NotebookRendererScript { } } -export enum NotebookControllerState { - Idle = 1, - Connecting = 2 -} //#endregion diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index a62de5e8e6590..bb1bd41aeb181 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -29,7 +29,7 @@ import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/note import { configureKernelIcon, selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernel, INotebookKernelService, NotebookControllerState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; @@ -408,7 +408,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { this._kernelInfoElement.add(this._statusbarService.addEntry( { name: nls.localize('notebook.info', "Notebook Kernel Info"), - text: `$(notebook-kernel-select) ${kernel.label}` + (kernel.state === NotebookControllerState.Idle ? '' : ' Connecting...'), + text: `$(notebook-kernel-select) ${kernel.label}`, ariaLabel: kernel.label, tooltip: isSuggested ? nls.localize('tooltop', "{0} (suggestion)", tooltip) : tooltip, command: SELECT_KERNEL_ID, diff --git a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts index aa57b01e95a9d..75b6b1a55dd8f 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts @@ -64,18 +64,6 @@ export class NotebookExecutionService implements INotebookExecutionService, IDis executeCells.push(cell); } - if (kernel.state !== undefined) { - // kernel has connection state management - const kernelId = kernel.id; - kernel.onDispose(() => { - // proxy kernel scenario, kernel disposed, we should now make way for new preferred kernel - const exes = executeCells.map(c => this._notebookExecutionStateService.createCellExecution(kernelId, notebook.uri, c.handle)); - exes.forEach(e => e.complete({})); - - this.executeNotebookCells(notebook, executeCells); - }); - } - if (executeCells.length > 0) { this._notebookKernelService.selectKernelForNotebook(kernel, notebook); diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts index 060e17ac37a4c..8b8f92277e2e5 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts @@ -10,7 +10,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { executingStateIcon, selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; -import { INotebookKernel, INotebookKernelMatchResult, INotebookKernelService, NotebookControllerState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelMatchResult, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { Event } from 'vs/base/common/event'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -121,11 +121,7 @@ export class NotebooKernelActionViewItem extends ActionViewItem { } private _generateKenrelLabel(kernel: INotebookKernel) { - if (kernel.state === NotebookControllerState.Connecting) { - return localize('kernelconnecting', "Connecting..."); - } else { - return kernel.label; - } + return kernel.label; } private _resetAction(): void { diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index e47d930372fa6..0068bd968e1c5 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -28,16 +28,10 @@ export interface INotebookKernelChangeEvent { description?: true; detail?: true; kind?: true; - state?: true; supportedLanguages?: true; hasExecutionOrder?: true; } -export enum NotebookControllerState { - Idle = 1, - Connecting = 2 -} - export interface INotebookKernel { readonly id: string; readonly viewType: string; @@ -52,14 +46,12 @@ export interface INotebookKernel { description?: string; detail?: string; kind?: string; - state?: NotebookControllerState; supportedLanguages: string[]; implementsInterrupt?: boolean; implementsExecutionOrder?: boolean; executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; - onDispose: Event; } export const enum ProxyKernelState { diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts index 9ceec332a7a0f..b0a288f1e8ed1 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts @@ -20,7 +20,7 @@ import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CellKind, IOutputDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernel, INotebookKernelService, ISelectedNotebooksChangeEvent, NotebookControllerState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService, ISelectedNotebooksChangeEvent } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; @@ -178,7 +178,6 @@ class TestNotebookKernel implements INotebookKernel { preloadUris: URI[] = []; preloadProvides: string[] = []; supportedLanguages: string[] = []; - onDispose = Event.None; executeNotebookCellsRequest(): Promise { throw new Error('Method not implemented.'); } @@ -189,7 +188,6 @@ class TestNotebookKernel implements INotebookKernel { this.supportedLanguages = opts?.languages ?? [PLAINTEXT_LANGUAGE_ID]; } kind?: string | undefined; - state?: NotebookControllerState | undefined; implementsInterrupt?: boolean | undefined; implementsExecutionOrder?: boolean | undefined; } diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts index 4763ee855c6df..887a4a054431a 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts @@ -182,7 +182,6 @@ class TestNotebookKernel implements INotebookKernel { preloadUris: URI[] = []; preloadProvides: string[] = []; supportedLanguages: string[] = []; - onDispose = Event.None; async executeNotebookCellsRequest(): Promise { } async cancelNotebookCellExecution(): Promise { } diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts index 54b3b52cb3c48..b1ebabc1db227 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; import { Emitter, Event } from 'vs/base/common/event'; -import { INotebookKernel, INotebookKernelService, NotebookControllerState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookKernelService } from 'vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { mock } from 'vs/base/test/common/mock'; @@ -171,8 +171,6 @@ class TestNotebookKernel implements INotebookKernel { preloadUris: URI[] = []; preloadProvides: string[] = []; supportedLanguages: string[] = []; - state?: NotebookControllerState | undefined = undefined; - onDispose = Event.None; executeNotebookCellsRequest(): Promise { throw new Error('Method not implemented.'); } diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index 409cfb9924c3f..bceef1e438f93 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -174,7 +174,8 @@ const apiMenus: IAPIMenu[] = [ { key: 'notebook/kernelSource', id: MenuId.NotebookKernelSource, - description: localize('notebook.kernelSource', "The contributed notebook kernel sources menu") + description: localize('notebook.kernelSource', "The contributed notebook kernel sources menu"), + proposed: 'notebookProxyController' }, { key: 'notebook/cell/title', diff --git a/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts b/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts index df429123049a7..a0f2c9e2df897 100644 --- a/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts @@ -4,13 +4,4 @@ *--------------------------------------------------------------------------------------------*/ declare module 'vscode' { - - export enum NotebookControllerState { - Idle = 1, - Connecting = 2 - } - - export interface NotebookController { - state?: NotebookControllerState; - } } From 50d802e75ffd879157d631e5a579f6e7b44f38e0 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 20 May 2022 16:40:19 -0700 Subject: [PATCH 208/942] :lipstick: --- src/vs/workbench/api/browser/mainThreadNotebookKernels.ts | 1 + src/vs/workbench/api/common/extHostTypes.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index b6ce5aaa0f839..6b7349827873e 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -23,6 +23,7 @@ abstract class MainThreadKernel implements INotebookKernel { private readonly _onDidChange = new Emitter(); private readonly preloads: { uri: URI; provides: string[] }[]; readonly onDidChange: Event = this._onDidChange.event; + readonly id: string; readonly viewType: string; readonly extension: ExtensionIdentifier; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 039f43fce8b88..dc08ede3d9868 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3426,7 +3426,6 @@ export class NotebookRendererScript { } } - //#endregion //#region Timeline From d1d31a284915522fed87bf922d4181124b7350fe Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 20 May 2022 19:41:56 -0400 Subject: [PATCH 209/942] Fix weird undefined bug (#150063) --- .../workbench/contrib/files/browser/views/explorerViewer.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index a857b3eec7d8d..73c8913b840e8 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -775,7 +775,9 @@ export class FilesFilter implements ITreeFilter { const globMatch = cached?.parsed(path.relative(stat.root.resource.path, stat.resource.path), stat.name, name => !!(stat.parent && stat.parent.getChild(name))); // Small optimization to only traverse gitIgnore if the globMatch from fileExclude returned nothing const ignoreFile = globMatch ? undefined : this.ignoreTreesPerRoot.get(stat.root.resource.toString())?.findSubstr(stat.resource); - const isIgnoredByIgnoreFile = !ignoreFile?.isPathIncludedInTraversal(stat.resource.path, stat.isDirectory); + const isIncludedInTraversal = ignoreFile?.isPathIncludedInTraversal(stat.resource.path, stat.isDirectory); + // Doing !undefined returns true and we want it to be false when undefined because that means it's not included in the ignore file + const isIgnoredByIgnoreFile = isIncludedInTraversal === undefined ? false : !isIncludedInTraversal; if (isIgnoredByIgnoreFile || globMatch || stat.parent?.isExcluded) { stat.isExcluded = true; const editors = this.editorService.visibleEditors; From 03694c8c5ca6fde4e9624a7caca989e39c673564 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Fri, 20 May 2022 17:17:03 -0700 Subject: [PATCH 210/942] Ideas for tracking last output --- .../browser/diff/notebookTextDiffEditor.ts | 4 ++ .../notebook/browser/notebookEditorWidget.ts | 1 + .../view/renderers/backLayerWebView.ts | 21 +++++++++ .../browser/view/renderers/webviewMessages.ts | 17 ++++++- .../browser/view/renderers/webviewPreloads.ts | 45 +++++++++++++++++++ 5 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 1369de699b934..a791887d6396a 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -789,6 +789,10 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD throw new Error('Not implemented'); } + getCellByHandle(cellHandle: number): IGenericCellViewModel | undefined { + throw new Error('Not implemented'); + } + removeInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: DiffNestedCellViewModel, displayOutput: ICellOutputViewModel, diffSide: DiffSide) { this._insetModifyQueueByOutputId.queue(displayOutput.model.outputId + (diffSide === DiffSide.Modified ? '-right' : 'left'), async () => { const activeWebview = diffSide === DiffSide.Modified ? this._modifiedWebview : this._originalWebview; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 8567bdded40a3..7197952d03c13 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1385,6 +1385,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD triggerScroll(event: IMouseWheelEvent) { that._listViewInfoAccessor.triggerScroll(event); }, getCellByInfo: that.getCellByInfo.bind(that), getCellById: that._getCellById.bind(that), + getCellByHandle: that.getCellByHandle.bind(that), toggleNotebookCellSelection: that._toggleNotebookCellSelection.bind(that), focusNotebookCell: that.focusNotebookCell.bind(that), focusNextNotebookCell: that.focusNextNotebookCell.bind(that), diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index af4606ffee6c9..59046e6159e11 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -37,6 +37,7 @@ import { preloadsScriptStr, RendererMetadata } from 'vs/workbench/contrib/notebo import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { CellUri, INotebookRendererInfo, NotebookSetting, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookKernel, IResolvedNotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IScopedRendererMessaging } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -66,6 +67,7 @@ export interface INotebookDelegateForWebview { focusNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output', options?: IFocusNotebookCellOptions): Promise; toggleNotebookCellSelection(cell: IGenericCellViewModel, selectFromPrevious: boolean): void; getCellByInfo(cellInfo: ICommonCellInfo): IGenericCellViewModel; + getCellByHandle(cellHandle: number): IGenericCellViewModel | undefined; focusNextNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): Promise; updateOutputHeight(cellInfo: ICommonCellInfo, output: IDisplayOutputViewModel, height: number, isInit: boolean, source?: string): void; scheduleOutputHeightAck(cellInfo: ICommonCellInfo, outputId: string, height: number): void; @@ -134,6 +136,7 @@ export class BackLayerWebView extends Disposable { @ILanguageService private readonly languageService: ILanguageService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, + @INotebookExecutionStateService notebookExecutionStateService: INotebookExecutionStateService ) { super(); @@ -173,6 +176,19 @@ export class BackLayerWebView extends Disposable { css: getTokenizationCss(), }); })); + + this._register(notebookExecutionStateService.onDidChangeCellExecution((e) => { + // If e.changed is undefined, it means notebook execution just finished. + if (e.changed === undefined && e.affectsNotebook(this.documentUri)) { + const cell = this.notebookEditor.getCellByHandle(e.cellHandle); + if (cell) { + this._sendMessageToWebview({ + type: 'trackFinalOutputRender', + cellId: cell.id + }); + } + } + })); } updateOptions(options: BacklayerWebviewOptions) { @@ -818,6 +834,11 @@ var requirejs = (function() { this._handleHighlightCodeBlock(data.codeBlocks); break; } + + case 'didRenderFinalOutput': { + console.log(`Rendered final output for ${data.cellId}`); + break; + } } })); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index 8b98bba83f860..e37283b180904 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -401,6 +401,16 @@ export interface IDidFindHighlightMessage extends BaseToWebviewMessage { readonly offset: number; } +export interface ITrackFinalOutputRenderMessage { + readonly type: 'trackFinalOutputRender'; + readonly cellId: string; +} + +export interface IDidRenderFinalOutputMessage extends BaseToWebviewMessage { + readonly type: 'didRenderFinalOutput'; + readonly cellId: string; +} + export type FromWebviewMessage = WebviewInitialized | IDimensionMessage | IMouseEnterMessage | @@ -428,7 +438,8 @@ export type FromWebviewMessage = WebviewInitialized | IRenderedMarkupMessage | IRenderedCellOutputMessage | IDidFindMessage | - IDidFindHighlightMessage; + IDidFindHighlightMessage | + IDidRenderFinalOutputMessage; export type ToWebviewMessage = IClearMessage | IFocusOutputMessage | @@ -458,6 +469,8 @@ export type ToWebviewMessage = IClearMessage | IFindMessage | IFindHighlightMessage | IFindUnHighlightMessage | - IFindStopMessage; + IFindStopMessage | + ITrackFinalOutputRenderMessage; + export type AnyMessage = FromWebviewMessage | ToWebviewMessage; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 33155f8dc15a7..786cc9377ce19 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -1172,6 +1172,10 @@ async function webviewPreloads(ctx: PreloadContext) { _highlighter?.dispose(); break; } + case 'trackFinalOutputRender': { + viewModel.trackFinalOutput(event.data.cellId); + break; + } } }); @@ -1394,6 +1398,7 @@ async function webviewPreloads(ctx: PreloadContext) { private readonly _markupCells = new Map(); private readonly _outputCells = new Map(); + private _pendingFinalOutput: string | undefined; public clearAll() { this._markupCells.clear(); @@ -1405,6 +1410,15 @@ async function webviewPreloads(ctx: PreloadContext) { this.renderOutputCells(); } + public trackFinalOutput(cellId: string) { + const cell = this._outputCells.get(cellId); + if (cell) { + cell.addFinalOutputTracker(); + } else { + this._pendingFinalOutput = cellId; + } + } + private async createMarkupCell(init: webviewMessages.IMarkupCellInitialization, top: number, visible: boolean): Promise { const existing = this._markupCells.get(init.cellId); if (existing) { @@ -1517,6 +1531,12 @@ async function webviewPreloads(ctx: PreloadContext) { // don't hide until after this step so that the height is right cellOutput.element.style.visibility = data.initiallyHidden ? 'hidden' : 'visible'; + + // If notebook side has indicated we are done (and we haven't added it yet), stick in the final render + if (data.cellId === this._pendingFinalOutput) { + this._pendingFinalOutput = undefined; + cellOutput.addFinalOutputTracker(); + } } public ensureOutputCell(cellId: string, cellTop: number, skipCellTopUpdateIfExist: boolean): OutputCell { @@ -1817,8 +1837,15 @@ async function webviewPreloads(ctx: PreloadContext) { container.appendChild(this.element); this.element = this.element; + const lowerWrapperElement = createFocusSink(cellId, true); container.appendChild(lowerWrapperElement); + + // New thoughts. + // Send message to indicate force scroll. + // Add resizeObserver on the element + // When resize occurs, scroll lowerWrapperElement into view + // Send message to indicate stop scrolling } public createOutputElement(outputId: string, outputOffset: number, left: number): OutputElement { @@ -1878,6 +1905,24 @@ async function webviewPreloads(ctx: PreloadContext) { this.element.style.visibility = 'visible'; } } + + public addFinalOutputTracker() { + const id = `${this.element.id}-final-output-tracker`; + let tracker: HTMLImageElement | null = document.getElementById(id) as HTMLImageElement; + if (!tracker) { + tracker = document.createElement('img') as HTMLImageElement; + tracker.id = id; + tracker.style.height = '0px'; + tracker.src = ''; + tracker.addEventListener('error', (ev) => { + console.log(`Final cell output has rendered`); + + // Src is empty so this should fire as soon as it renders + postNotebookMessage('didRenderFinalOutput', { cellId: this.element.id }); + }); + this.element.appendChild(tracker); + } + } } class OutputContainer { From 3649387f3fc1f8bbb9d2f58dc1fe66dd01f50a6a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 20 May 2022 17:18:25 -0700 Subject: [PATCH 211/942] Bump php grammar, fix #142824, fix #116103 (#150067) --- extensions/php/cgmanifest.json | 4 +- extensions/php/syntaxes/php.tmLanguage.json | 408 ++++++++++++++------ 2 files changed, 282 insertions(+), 130 deletions(-) diff --git a/extensions/php/cgmanifest.json b/extensions/php/cgmanifest.json index 4b25149ffdd0f..4fe8f5ebcb504 100644 --- a/extensions/php/cgmanifest.json +++ b/extensions/php/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "language-php", "repositoryUrl": "https://github.com/atom/language-php", - "commitHash": "ff64523c94c014d68f5dec189b05557649c5872a" + "commitHash": "eb28b8aea1214dcbc732f3d9b9ed20c089c648bd" } }, "license": "MIT", - "version": "0.48.0" + "version": "0.48.1" } ], "version": 1 diff --git a/extensions/php/syntaxes/php.tmLanguage.json b/extensions/php/syntaxes/php.tmLanguage.json index 69a11ec252940..542b02023a772 100644 --- a/extensions/php/syntaxes/php.tmLanguage.json +++ b/extensions/php/syntaxes/php.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-php/commit/ff64523c94c014d68f5dec189b05557649c5872a", + "version": "https://github.com/atom/language-php/commit/eb28b8aea1214dcbc732f3d9b9ed20c089c648bd", "scopeName": "source.php", "patterns": [ { @@ -13,62 +13,6 @@ { "include": "#comments" }, - { - "begin": "(?i)^\\s*(interface)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)\\s*(extends)?\\s*", - "beginCaptures": { - "1": { - "name": "storage.type.interface.php" - }, - "2": { - "name": "entity.name.type.interface.php" - }, - "3": { - "name": "storage.modifier.extends.php" - } - }, - "end": "(?i)((?:[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*\\s*,\\s*)*)([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)?\\s*(?:(?={)|$)", - "endCaptures": { - "1": { - "patterns": [ - { - "match": "(?i)[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*", - "name": "entity.other.inherited-class.php" - }, - { - "match": ",", - "name": "punctuation.separator.classes.php" - } - ] - }, - "2": { - "name": "entity.other.inherited-class.php" - } - }, - "name": "meta.interface.php", - "patterns": [ - { - "include": "#namespace" - } - ] - }, - { - "begin": "(?i)^\\s*(trait)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)", - "beginCaptures": { - "1": { - "name": "storage.type.trait.php" - }, - "2": { - "name": "entity.name.type.trait.php" - } - }, - "end": "(?={)", - "name": "meta.trait.php", - "patterns": [ - { - "include": "#comments" - } - ] - }, { "match": "(?i)(?:^|(?<=<\\?php))\\s*(namespace)\\s+([a-z0-9_\\x{7f}-\\x{10ffff}\\\\]+)(?=\\s*;)", "name": "meta.namespace.php", @@ -233,123 +177,214 @@ ] }, { - "begin": "(?ix)\n(?:\n \\b(?:(abstract|final)\\s+)?(class)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)\n |\\b(new)\\b\\s*(\\#\\[.*\\])?\\s*\\b(class)\\b # anonymous class\n)", + "begin": "(?ix)\n\\b(trait)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)", "beginCaptures": { "1": { - "name": "storage.modifier.${1:/downcase}.php" + "name": "storage.type.trait.php" }, "2": { - "name": "storage.type.class.php" + "name": "entity.name.type.trait.php" + } + }, + "end": "}|(?=\\?>)", + "endCaptures": { + "0": { + "name": "punctuation.definition.trait.end.bracket.curly.php" + } + }, + "name": "meta.trait.php", + "patterns": [ + { + "include": "#comments" }, - "3": { - "name": "entity.name.type.class.php" + { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.trait.begin.bracket.curly.php" + } + }, + "end": "(?=}|\\?>)", + "contentName": "meta.trait.body.php", + "patterns": [ + { + "include": "$self" + } + ] + } + ] + }, + { + "begin": "(?ix)\n\\b(interface)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)", + "beginCaptures": { + "1": { + "name": "storage.type.interface.php" }, - "4": { - "name": "keyword.other.new.php" + "2": { + "name": "entity.name.type.interface.php" + } + }, + "end": "}|(?=\\?>)", + "endCaptures": { + "0": { + "name": "punctuation.definition.interface.end.bracket.curly.php" + } + }, + "name": "meta.interface.php", + "patterns": [ + { + "include": "#comments" }, - "5": { + { + "include": "#interface-extends" + }, + { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.interface.begin.bracket.curly.php" + } + }, + "end": "(?=}|\\?>)", + "contentName": "meta.interface.body.php", "patterns": [ { - "include": "#attribute" + "include": "#class-constant" + }, + { + "include": "$self" } ] + } + ] + }, + { + "begin": "(?ix)\n\\b(enum)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)\n(?: \\s* (:) \\s* (int | string) \\b )?", + "beginCaptures": { + "1": { + "name": "storage.type.enum.php" }, - "6": { - "name": "storage.type.class.php" + "2": { + "name": "entity.name.type.enum.php" + }, + "3": { + "name": "keyword.operator.return-value.php" + }, + "4": { + "name": "keyword.other.type.php" } }, "end": "}|(?=\\?>)", "endCaptures": { "0": { - "name": "punctuation.definition.class.end.bracket.curly.php" + "name": "punctuation.definition.enum.end.bracket.curly.php" } }, - "name": "meta.class.php", + "name": "meta.enum.php", "patterns": [ { "include": "#comments" }, { - "begin": "(?i)(extends)\\s+", + "include": "#class-implements" + }, + { + "begin": "{", "beginCaptures": { - "1": { - "name": "storage.modifier.extends.php" + "0": { + "name": "punctuation.definition.enum.begin.bracket.curly.php" } }, - "contentName": "meta.other.inherited-class.php", - "end": "(?i)(?=[^a-z0-9_\\x{7f}-\\x{10ffff}\\\\])", + "end": "(?=}|\\?>)", + "contentName": "meta.enum.body.php", "patterns": [ { - "begin": "(?i)(?=\\\\?[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*\\\\)", - "end": "(?i)([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)?(?=[^a-z0-9_\\x{7f}-\\x{10ffff}\\\\])", - "endCaptures": { + "match": "(?i)\\b(case)\\s*([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)", + "captures": { "1": { - "name": "entity.other.inherited-class.php" - } - }, - "patterns": [ - { - "include": "#namespace" + "name": "storage.modifier.php" + }, + "2": { + "name": "constant.enum.php" } - ] + } }, { - "include": "#class-builtin" + "include": "#class-constant" }, { - "include": "#namespace" - }, + "include": "$self" + } + ] + } + ] + }, + { + "begin": "(?ix)\n(?:\n \\b(?:(abstract|final)\\s+)?(class)\\s+([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)\n |\\b(new)\\b\\s*(\\#\\[.*\\])?\\s*\\b(class)\\b # anonymous class\n)", + "beginCaptures": { + "1": { + "name": "storage.modifier.${1:/downcase}.php" + }, + "2": { + "name": "storage.type.class.php" + }, + "3": { + "name": "entity.name.type.class.php" + }, + "4": { + "name": "keyword.other.new.php" + }, + "5": { + "patterns": [ { - "match": "(?i)[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*", - "name": "entity.other.inherited-class.php" + "include": "#attribute" } ] }, + "6": { + "name": "storage.type.class.php" + } + }, + "end": "}|(?=\\?>)", + "endCaptures": { + "0": { + "name": "punctuation.definition.class.end.bracket.curly.php" + } + }, + "name": "meta.class.php", + "patterns": [ { - "begin": "(?i)(implements)\\s+", + "begin": "(?<=class)\\s*(\\()", "beginCaptures": { "1": { - "name": "storage.modifier.implements.php" + "name": "punctuation.definition.arguments.begin.bracket.round.php" } }, - "end": "(?i)(?=[;{])", + "end": "\\)|(?=\\?>)", + "endCaptures": { + "0": { + "name": "punctuation.definition.arguments.end.bracket.round.php" + } + }, + "name": "meta.function-call.php", "patterns": [ { - "include": "#comments" + "include": "#named-arguments" }, { - "begin": "(?i)(?=[a-z0-9_\\x{7f}-\\x{10ffff}\\\\]+)", - "contentName": "meta.other.inherited-class.php", - "end": "(?i)(?:\\s*(?:,|(?=[^a-z0-9_\\x{7f}-\\x{10ffff}\\\\\\s]))\\s*)", - "patterns": [ - { - "begin": "(?i)(?=\\\\?[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*\\\\)", - "end": "(?i)([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)?(?=[^a-z0-9_\\x{7f}-\\x{10ffff}\\\\])", - "endCaptures": { - "1": { - "name": "entity.other.inherited-class.php" - } - }, - "patterns": [ - { - "include": "#namespace" - } - ] - }, - { - "include": "#class-builtin" - }, - { - "include": "#namespace" - }, - { - "match": "(?i)[a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*", - "name": "entity.other.inherited-class.php" - } - ] + "include": "$self" } ] }, + { + "include": "#comments" + }, + { + "include": "#class-extends" + }, + { + "include": "#class-implements" + }, { "begin": "{", "beginCaptures": { @@ -360,6 +395,9 @@ "end": "(?=}|\\?>)", "contentName": "meta.class.body.php", "patterns": [ + { + "include": "#class-constant" + }, { "include": "$self" } @@ -382,7 +420,7 @@ } }, { - "match": "(?x)\n\\s* # FIXME: Removing this causes specs to fail. Investigate.\n\\b(\n break|case|continue|declare|default|die|do|\n else(if)?|end(declare|for(each)?|if|switch|while)|exit|\n for(each)?|if|return|switch|use|while|yield\n)\\b", + "match": "(?x)\n\\b(\n break|case|continue|declare|default|die|do|\n else(if)?|end(declare|for(each)?|if|switch|while)|exit|\n for(each)?|if|return|switch|use|while|yield\n)\\b", "captures": { "1": { "name": "keyword.control.${1:/downcase}.php" @@ -853,7 +891,7 @@ "name": "storage.type.php" }, { - "match": "(?i)\\b(global|abstract|const|extends|implements|final|private|protected|public|static)\\b", + "match": "(?i)\\b(global|abstract|const|final|private|protected|public|static)\\b", "name": "storage.modifier.php" }, { @@ -975,7 +1013,7 @@ "name": "entity.name.goto-label.php" } }, - "match": "(?i)^\\s*([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*)\\s*:(?!:)" + "match": "(?i)^\\s*([a-z_\\x{7f}-\\x{10ffff}][a-z0-9_\\x{7f}-\\x{10ffff}]*(? Date: Sat, 21 May 2022 19:46:06 +0200 Subject: [PATCH 212/942] support scanning, installing and uninstalling extensions in a profile --- .../contrib/extensionsCleaner.ts | 4 +- .../sharedProcess/sharedProcessMain.ts | 2 + src/vs/code/node/cliProcessMain.ts | 2 + .../abstractExtensionManagementService.ts | 69 +++++++++-- .../common/extensionManagement.ts | 15 ++- .../common/extensionManagementIpc.ts | 26 ++-- .../common/extensionsProfileScannerService.ts | 117 ++++++++++++++++++ .../common/extensionsScannerService.ts | 115 ++++++++++------- .../extensionsScannerService.ts | 4 +- .../node/extensionManagementService.ts | 86 +++++++------ .../node/extensionsScannerService.ts | 4 +- .../node/extensionsScannerService.test.ts | 5 +- .../userDataProfile/common/userDataProfile.ts | 4 +- .../server/node/extensionsScannerService.ts | 4 +- .../node/remoteExtensionHostAgentCli.ts | 2 + src/vs/server/node/serverServices.ts | 2 + .../extensionManagementServerService.ts | 4 +- .../common/webExtensionManagementService.ts | 28 +++-- .../extensionManagementServerService.ts | 4 +- .../remoteExtensionManagementService.ts | 2 +- .../cachedExtensionScanner.ts | 4 +- src/vs/workbench/workbench.common.main.ts | 2 + 22 files changed, 374 insertions(+), 131 deletions(-) create mode 100644 src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts index f26355d5692ae..a7ebe66abcae7 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionGalleryService, IGlobalExtensionEnablementService, IServerExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionStorageService, IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; import { migrateUnsupportedExtensions } from 'vs/platform/extensionManagement/common/unsupportedExtensionsMigration'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; @@ -14,7 +14,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; export class ExtensionsCleaner extends Disposable { constructor( - @IExtensionManagementService extensionManagementService: ExtensionManagementService, + @IServerExtensionManagementService extensionManagementService: ExtensionManagementService, @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, @IExtensionStorageService extensionStorageService: IExtensionStorageService, @IGlobalExtensionEnablementService extensionEnablementService: IGlobalExtensionEnablementService, diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 3bd1e64a7a0cc..47afb70f80a76 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -101,6 +101,7 @@ import { IV8InspectProfilingService } from 'vs/platform/profiling/common/profili import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService'; import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; class SharedProcessMain extends Disposable { @@ -300,6 +301,7 @@ class SharedProcessMain extends Disposable { services.set(ICustomEndpointTelemetryService, customEndpointTelemetryService); // Extension Management + services.set(IExtensionsProfileScannerService, new SyncDescriptor(ExtensionsProfileScannerService)); services.set(IExtensionsScannerService, new SyncDescriptor(ExtensionsScannerService)); services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index f69778b5f8500..a643caf406ec3 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -25,6 +25,7 @@ import { NativeEnvironmentService } from 'vs/platform/environment/node/environme import { ExtensionGalleryServiceWithNoStorageService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IExtensionGalleryService, IExtensionManagementCLIService, IExtensionManagementService, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementCLIService } from 'vs/platform/extensionManagement/common/extensionManagementCLIService'; +import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService'; @@ -150,6 +151,7 @@ class CliMain extends Disposable { services.set(IDownloadService, new SyncDescriptor(DownloadService)); // Extensions + services.set(IExtensionsProfileScannerService, new SyncDescriptor(ExtensionsProfileScannerService)); services.set(IExtensionsScannerService, new SyncDescriptor(ExtensionsScannerService)); services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryServiceWithNoStorageService)); diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 94e9a31abc7f8..7f456cfe2c2fd 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -13,10 +13,11 @@ import { isWeb } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { - DidUninstallExtensionEvent, ExtensionManagementError, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementParticipant, IExtensionManagementService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOperation, InstallOptions, - InstallVSIXOptions, IExtensionsControlManifest, StatisticType, UninstallOptions, isTargetPlatformCompatible, TargetPlatformToString, ExtensionManagementErrorCode + DidUninstallExtensionEvent, ExtensionManagementError, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementParticipant, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOperation, + IExtensionsControlManifest, StatisticType, isTargetPlatformCompatible, TargetPlatformToString, ExtensionManagementErrorCode, IServerExtensionManagementService, ServerInstallOptions, ServerInstallVSIXOptions, ServerUninstallOptions, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions, ExtensionKey, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -31,7 +32,7 @@ export interface IInstallExtensionTask { cancel(): void; } -export type UninstallExtensionTaskOptions = { readonly remove?: boolean; readonly versionOnly?: boolean }; +export type UninstallExtensionTaskOptions = { readonly remove?: boolean; readonly versionOnly?: boolean; readonly profileLocation?: URI }; export interface IUninstallExtensionTask { readonly extension: ILocalExtension; @@ -40,7 +41,7 @@ export interface IUninstallExtensionTask { cancel(): void; } -export abstract class AbstractExtensionManagementService extends Disposable implements IExtensionManagementService { +export abstract class AbstractExtensionManagementService extends Disposable implements IServerExtensionManagementService { declare readonly _serviceBrand: undefined; @@ -83,7 +84,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl return extension.allTargetPlatforms.some(targetPlatform => isTargetPlatformCompatible(targetPlatform, extension.allTargetPlatforms, currentTargetPlatform)); } - async installFromGallery(extension: IGalleryExtension, options: InstallOptions = {}): Promise { + async installFromGallery(extension: IGalleryExtension, options: ServerInstallOptions = {}): Promise { try { if (!this.galleryService.isEnabled()) { throw new ExtensionManagementError(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"), ExtensionManagementErrorCode.Internal); @@ -98,7 +99,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl } } - async uninstall(extension: ILocalExtension, options: UninstallOptions = {}): Promise { + async uninstall(extension: ILocalExtension, options: ServerUninstallOptions = {}): Promise { this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id); return this.unininstallExtension(extension, options); } @@ -134,7 +135,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl this.participants.push(participant); } - protected async installExtension(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions & InstallVSIXOptions): Promise { + protected async installExtension(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): Promise { // only cache gallery extensions tasks if (!URI.isUri(extension)) { let installExtensionTask = this.installingExtensions.get(ExtensionKey.create(extension).toString()); @@ -252,7 +253,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl // rollback installed extensions if (installResults.length) { try { - const result = await Promise.allSettled(installResults.map(({ local }) => this.createUninstallExtensionTask(local, { versionOnly: true }).run())); + const result = await Promise.allSettled(installResults.map(({ local }) => this.createUninstallExtensionTask(local, { versionOnly: true, profileLocation: options.profileLocation }).run())); for (let index = 0; index < result.length; index++) { const r = result[index]; const { identifier } = installResults[index]; @@ -420,7 +421,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl return compatibleExtension; } - private async unininstallExtension(extension: ILocalExtension, options: UninstallOptions): Promise { + private async unininstallExtension(extension: ILocalExtension, options: ServerUninstallOptions): Promise { const uninstallExtensionTask = this.uninstallingExtensions.get(extension.identifier.id.toLowerCase()); if (uninstallExtensionTask) { this.logService.info('Extensions is already requested to uninstall', extension.identifier.id); @@ -449,7 +450,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl const processedTasks: IUninstallExtensionTask[] = []; try { - allTasks.push(createUninstallExtensionTask(extension, {})); + allTasks.push(createUninstallExtensionTask(extension, { profileLocation: options.profileLocation })); const installed = await this.getInstalled(ExtensionType.User); if (options.donotIncludePack) { @@ -590,13 +591,13 @@ export abstract class AbstractExtensionManagementService extends Disposable impl abstract zip(extension: ILocalExtension): Promise; abstract unzip(zipLocation: URI): Promise; abstract getManifest(vsix: URI): Promise; - abstract install(vsix: URI, options?: InstallVSIXOptions): Promise; - abstract getInstalled(type?: ExtensionType): Promise; + abstract install(vsix: URI, options?: ServerInstallVSIXOptions): Promise; + abstract getInstalled(type?: ExtensionType, profileLocation?: URI): Promise; abstract updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise; abstract updateExtensionScope(local: ILocalExtension, isMachineScoped: boolean): Promise; - protected abstract createInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions & InstallVSIXOptions): IInstallExtensionTask; + protected abstract createInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask; protected abstract createUninstallExtensionTask(extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask; } @@ -691,3 +692,45 @@ export abstract class AbstractExtensionTask { protected abstract doRun(token: CancellationToken): Promise; } + +export abstract class AbstractInstallExtensionTask extends AbstractExtensionTask { + + constructor( + protected readonly options: ServerInstallOptions, + private readonly extensionsProfileScannerService: IExtensionsProfileScannerService, + ) { + super(); + } + + protected async doRun(token: CancellationToken): Promise { + const { local, metadata } = await this.install(token); + if (this.options.profileLocation) { + await this.extensionsProfileScannerService.addExtensionToProfile(local, metadata, this.options.profileLocation); + } + return local; + } + + protected abstract install(token: CancellationToken): Promise<{ local: ILocalExtension; metadata: Metadata }>; + +} + +export abstract class AbstractUninstallExtensionTask extends AbstractExtensionTask { + + constructor( + readonly extension: ILocalExtension, + protected readonly options: UninstallExtensionTaskOptions, + private readonly extensionsProfileScannerService: IExtensionsProfileScannerService, + ) { + super(); + } + + protected async doRun(token: CancellationToken): Promise { + await this.uninstall(token); + if (this.options.profileLocation) { + await this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension.identifier, this.options.profileLocation); + } + } + + protected abstract uninstall(token: CancellationToken): Promise; + +} diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index d041814268bb6..ee103be4247b8 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -11,7 +11,7 @@ import { Platform } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ExtensionType, IExtension, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { createDecorator, refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation'; export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$'; export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN); @@ -419,6 +419,19 @@ export interface IExtensionManagementService { getTargetPlatform(): Promise; } +export type ServerInstallOptions = InstallOptions & { profileLocation?: URI }; +export type ServerInstallVSIXOptions = InstallVSIXOptions & { profileLocation?: URI }; +export type ServerUninstallOptions = UninstallOptions & { profileLocation?: URI }; + +export const IServerExtensionManagementService = refineServiceDecorator(IExtensionManagementService); +export interface IServerExtensionManagementService extends IExtensionManagementService { + readonly _serviceBrand: undefined; + getInstalled(type?: ExtensionType, profileLocation?: URI): Promise; + install(vsix: URI, options?: ServerInstallVSIXOptions): Promise; + installFromGallery(extension: IGalleryExtension, options?: ServerInstallOptions): Promise; + uninstall(extension: ILocalExtension, options?: ServerUninstallOptions): Promise; +} + export const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/disabled'; export const ENABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/enabled'; export const IGlobalExtensionEnablementService = createDecorator('IGlobalExtensionEnablementService'); diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index b6e72ec8140f6..06b5af8f3b32d 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -5,12 +5,14 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { revive } from 'vs/base/common/marshalling'; import { cloneAndChange } from 'vs/base/common/objects'; import { URI, UriComponents } from 'vs/base/common/uri'; import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, IExtensionTipsService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOptions, InstallVSIXOptions, IExtensionsControlManifest, isTargetPlatformCompatible, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, IExtensionTipsService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOptions, InstallVSIXOptions, IExtensionsControlManifest, isTargetPlatformCompatible, UninstallOptions, IServerExtensionManagementService, ServerInstallOptions, ServerInstallVSIXOptions, ServerUninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI { return URI.revive(transformer ? transformer.transformIncoming(uri) : uri); @@ -38,7 +40,7 @@ export class ExtensionManagementChannel implements IServerChannel { onUninstallExtension: Event; onDidUninstallExtension: Event; - constructor(private service: IExtensionManagementService, private getUriTransformer: (requestContext: any) => IURITransformer | null) { + constructor(private service: IServerExtensionManagementService, private getUriTransformer: (requestContext: any) => IURITransformer | null) { this.onInstallExtension = Event.buffer(service.onInstallExtension, true); this.onDidInstallExtensions = Event.buffer(service.onDidInstallExtensions, true); this.onUninstallExtension = Event.buffer(service.onUninstallExtension, true); @@ -62,14 +64,14 @@ export class ExtensionManagementChannel implements IServerChannel { switch (command) { case 'zip': return this.service.zip(transformIncomingExtension(args[0], uriTransformer)).then(uri => transformOutgoingURI(uri, uriTransformer)); case 'unzip': return this.service.unzip(transformIncomingURI(args[0], uriTransformer)); - case 'install': return this.service.install(transformIncomingURI(args[0], uriTransformer), args[1]); + case 'install': return this.service.install(transformIncomingURI(args[0], uriTransformer), revive(args[1])); case 'getManifest': return this.service.getManifest(transformIncomingURI(args[0], uriTransformer)); case 'getTargetPlatform': return this.service.getTargetPlatform(); case 'canInstall': return this.service.canInstall(args[0]); - case 'installFromGallery': return this.service.installFromGallery(args[0], args[1]); - case 'uninstall': return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), args[1]); + case 'installFromGallery': return this.service.installFromGallery(args[0], revive(args[1])); + case 'uninstall': return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), revive(args[1])); case 'reinstallFromGallery': return this.service.reinstallFromGallery(transformIncomingExtension(args[0], uriTransformer)); - case 'getInstalled': return this.service.getInstalled(args[0]).then(extensions => extensions.map(e => transformOutgoingExtension(e, uriTransformer))); + case 'getInstalled': return this.service.getInstalled(args[0], URI.revive(args[1])).then(extensions => extensions.map(e => transformOutgoingExtension(e, uriTransformer))); case 'updateMetadata': return this.service.updateMetadata(transformIncomingExtension(args[0], uriTransformer), args[1]).then(e => transformOutgoingExtension(e, uriTransformer)); case 'updateExtensionScope': return this.service.updateExtensionScope(transformIncomingExtension(args[0], uriTransformer), args[1]).then(e => transformOutgoingExtension(e, uriTransformer)); case 'getExtensionsControlManifest': return this.service.getExtensionsControlManifest(); @@ -97,6 +99,7 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt constructor( private readonly channel: IChannel, + private readonly userDataProfilesService: IUserDataProfilesService | undefined ) { super(); this._register(this.channel.listen('onInstallExtension')(e => this._onInstallExtension.fire({ identifier: e.identifier, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source }))); @@ -135,7 +138,8 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt } install(vsix: URI, options?: InstallVSIXOptions): Promise { - return Promise.resolve(this.channel.call('install', [vsix, options])).then(local => transformIncomingExtension(local, null)); + const serverInstallOptions: ServerInstallVSIXOptions = { ...options, profileLocation: this.userDataProfilesService?.defaultProfile.extensionsResource }; + return Promise.resolve(this.channel.call('install', [vsix, serverInstallOptions])).then(local => transformIncomingExtension(local, null)); } getManifest(vsix: URI): Promise { @@ -143,11 +147,13 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt } installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise { - return Promise.resolve(this.channel.call('installFromGallery', [extension, installOptions])).then(local => transformIncomingExtension(local, null)); + const serverInstallOptions: ServerInstallOptions = { ...installOptions, profileLocation: this.userDataProfilesService?.defaultProfile.extensionsResource }; + return Promise.resolve(this.channel.call('installFromGallery', [extension, serverInstallOptions])).then(local => transformIncomingExtension(local, null)); } uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { - return Promise.resolve(this.channel.call('uninstall', [extension!, options])); + const serverUninstallOptions: ServerUninstallOptions = { ...options, profileLocation: this.userDataProfilesService?.defaultProfile.extensionsResource }; + return Promise.resolve(this.channel.call('uninstall', [extension!, serverUninstallOptions])); } reinstallFromGallery(extension: ILocalExtension): Promise { @@ -155,7 +161,7 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt } getInstalled(type: ExtensionType | null = null): Promise { - return Promise.resolve(this.channel.call('getInstalled', [type])) + return Promise.resolve(this.channel.call('getInstalled', [type, this.userDataProfilesService?.defaultProfile.extensionsResource])) .then(extensions => extensions.map(extension => transformIncomingExtension(extension, null))); } diff --git a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts new file mode 100644 index 0000000000000..b7a0693fde7dd --- /dev/null +++ b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Queue } from 'vs/base/common/async'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ResourceMap } from 'vs/base/common/map'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { ILocalExtension, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; + +interface IStoredProfileExtension { + readonly identifier: IExtensionIdentifier; + readonly location: UriComponents; + readonly metadata: Metadata; +} + +export interface IScannedProfileExtension { + readonly identifier: IExtensionIdentifier; + readonly location: URI; + readonly metadata: Metadata; +} + +export const IExtensionsProfileScannerService = createDecorator('IExtensionsProfileScannerService'); +export interface IExtensionsProfileScannerService { + readonly _serviceBrand: undefined; + + scanProfileExtensions(profileLocation: URI): Promise; + addExtensionToProfile(extension: ILocalExtension, metadata: Metadata, profileLocation: URI): Promise; + removeExtensionFromProfile(identifier: IExtensionIdentifier, profileLocation: URI): Promise; +} + +export class ExtensionsProfileScannerService extends Disposable implements IExtensionsProfileScannerService { + readonly _serviceBrand: undefined; + + private readonly resourcesAccessQueueMap = new ResourceMap>(); + + constructor( + @IFileService private readonly fileService: IFileService, + @ILogService private readonly logService: ILogService, + ) { + super(); + } + + scanProfileExtensions(profileLocation: URI): Promise { + return this.withProfileExtensions(profileLocation); + } + + addExtensionToProfile(extension: ILocalExtension, metadata: Metadata, profileLocation: URI): Promise { + return this.withProfileExtensions(profileLocation, profileExtensions => { + // Remove the existing extension to avoid duplicates + profileExtensions = profileExtensions.filter(e => !areSameExtensions(e.identifier, extension.identifier)); + profileExtensions.push({ identifier: extension.identifier, location: extension.location, metadata }); + return profileExtensions; + }); + } + + removeExtensionFromProfile(identifier: IExtensionIdentifier, profileLocation: URI): Promise { + return this.withProfileExtensions(profileLocation, profileExtensions => profileExtensions.filter(extension => !(areSameExtensions(extension.identifier, identifier)))); + } + + private async withProfileExtensions(file: URI, updateFn?: (extensions: IScannedProfileExtension[]) => IScannedProfileExtension[]): Promise { + return this.getResourceAccessQueue(file).queue(async () => { + let extensions: IScannedProfileExtension[] = []; + + // Read + try { + const content = await this.fileService.readFile(file); + const storedWebExtensions: IStoredProfileExtension[] = JSON.parse(content.value.toString()); + for (const e of storedWebExtensions) { + if (!e.location || !e.identifier) { + this.logService.info('Ignoring invalid extension while scanning', storedWebExtensions); + continue; + } + extensions.push({ + identifier: e.identifier, + location: URI.revive(e.location), + metadata: e.metadata, + }); + } + } catch (error) { + /* Ignore */ + if ((error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) { + this.logService.error(error); + } + } + + // Update + if (updateFn) { + extensions = updateFn(extensions); + const storedWebExtensions: IStoredProfileExtension[] = extensions.map(e => ({ + identifier: e.identifier, + location: e.location.toJSON(), + metadata: e.metadata + })); + await this.fileService.writeFile(file, VSBuffer.fromString(JSON.stringify(storedWebExtensions))); + } + + return extensions; + }); + } + + private getResourceAccessQueue(file: URI): Queue { + let resourceQueue = this.resourcesAccessQueueMap.get(file); + if (!resourceQueue) { + resourceQueue = new Queue(); + this.resourcesAccessQueueMap.set(file, resourceQueue); + } + return resourceQueue; + } +} diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index 93936e423359d..419327d391062 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -18,7 +18,7 @@ import * as platform from 'vs/base/common/platform'; import { basename, isEqual, joinPath } from 'vs/base/common/resources'; import * as semver from 'vs/base/common/semver/semver'; import Severity from 'vs/base/common/severity'; -import { isArray, isObject, isString } from 'vs/base/common/types'; +import { isArray, isEmptyObject, isObject, isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -32,6 +32,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { Emitter, Event } from 'vs/base/common/event'; import { revive } from 'vs/base/common/marshalling'; +import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; export type IScannedExtensionManifest = IRelaxedExtensionManifest & { __metadata?: Metadata }; @@ -97,6 +98,7 @@ interface IBuiltInExtensionControl { } export type ScanOptions = { + readonly profileLocation?: URI; readonly includeInvalid?: boolean; readonly includeAllVersions?: boolean; readonly includeUninstalled?: boolean; @@ -115,7 +117,7 @@ export interface IExtensionsScannerService { getTargetPlatform(): Promise; - scanAllExtensions(scanOptions: ScanOptions): Promise; + scanAllExtensions(systemScanOptions: ScanOptions, userScanOptions: ScanOptions): Promise; scanSystemExtensions(scanOptions: ScanOptions): Promise; scanUserExtensions(scanOptions: ScanOptions): Promise; scanExtensionsUnderDevelopment(scanOptions: ScanOptions, existingExtensions: IScannedExtension[]): Promise; @@ -134,15 +136,17 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem private readonly _onDidChangeCache = this._register(new Emitter()); readonly onDidChangeCache = this._onDidChangeCache.event; - private readonly systemExtensionsCachedScanner = this._register(new CachedExtensionsScanner(joinPath(this.cacheLocation, BUILTIN_MANIFEST_CACHE_FILE), this.fileService, this.logService)); - private readonly userExtensionsCachedScanner = this._register(new CachedExtensionsScanner(joinPath(this.cacheLocation, USER_MANIFEST_CACHE_FILE), this.fileService, this.logService)); - private readonly extensionsScanner = this._register(new ExtensionsScanner(this.fileService, this.logService)); + private readonly obsoleteFile = joinPath(this.userExtensionsLocation, '.obsolete'); + private readonly systemExtensionsCachedScanner = this._register(new CachedExtensionsScanner(joinPath(this.cacheLocation, BUILTIN_MANIFEST_CACHE_FILE), this.obsoleteFile, this.extensionsProfileScannerService, this.fileService, this.logService)); + private readonly userExtensionsCachedScanner = this._register(new CachedExtensionsScanner(joinPath(this.cacheLocation, USER_MANIFEST_CACHE_FILE), this.obsoleteFile, this.extensionsProfileScannerService, this.fileService, this.logService)); + private readonly extensionsScanner = this._register(new ExtensionsScanner(this.obsoleteFile, this.extensionsProfileScannerService, this.fileService, this.logService)); constructor( readonly systemExtensionsLocation: URI, readonly userExtensionsLocation: URI, private readonly extensionsControlLocation: URI, private readonly cacheLocation: URI, + @IExtensionsProfileScannerService protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService, @IFileService protected readonly fileService: IFileService, @ILogService protected readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @@ -162,12 +166,12 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem return this._targetPlatformPromise; } - async scanAllExtensions(scanOptions: ScanOptions): Promise { + async scanAllExtensions(systemScanOptions: ScanOptions, userScanOptions: ScanOptions): Promise { const [system, user] = await Promise.all([ - this.scanSystemExtensions(scanOptions), - this.scanUserExtensions(scanOptions), + this.scanSystemExtensions(systemScanOptions), + this.scanUserExtensions(userScanOptions), ]); - const development = await this.scanExtensionsUnderDevelopment(scanOptions, [...system, ...user]); + const development = await this.scanExtensionsUnderDevelopment(systemScanOptions, [...system, ...user]); return this.dedupExtensions([...system, ...user, ...development], await this.getTargetPlatform(), true); } @@ -181,7 +185,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem async scanUserExtensions(scanOptions: ScanOptions): Promise { this.logService.trace('Started scanning user extensions'); - const extensionsScannerInput = await this.createExtensionScannerInput(this.userExtensionsLocation, ExtensionType.User, !scanOptions.includeUninstalled, scanOptions.language); + const extensionsScannerInput = await this.createExtensionScannerInput(scanOptions.profileLocation ?? this.userExtensionsLocation, !!scanOptions.profileLocation, ExtensionType.User, !scanOptions.includeUninstalled, scanOptions.language); const extensionsScanner = scanOptions.useCache && !extensionsScannerInput.devMode && extensionsScannerInput.excludeObsolete ? this.userExtensionsCachedScanner : this.extensionsScanner; let extensions = await extensionsScanner.scanExtensions(extensionsScannerInput); extensions = await this.applyScanOptions(extensions, scanOptions, true); @@ -193,7 +197,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem if (this.environmentService.isExtensionDevelopment && this.environmentService.extensionDevelopmentLocationURI) { const extensions = (await Promise.all(this.environmentService.extensionDevelopmentLocationURI.filter(extLoc => extLoc.scheme === Schemas.file) .map(async extensionDevelopmentLocationURI => { - const input = await this.createExtensionScannerInput(extensionDevelopmentLocationURI, ExtensionType.User, true, scanOptions.language, false /* do not validate */); + const input = await this.createExtensionScannerInput(extensionDevelopmentLocationURI, false, ExtensionType.User, true, scanOptions.language, false /* do not validate */); const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(input); return extensions.map(extension => { // Override the extension type from the existing extensions @@ -209,7 +213,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem } async scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise { - const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, extensionType, true, scanOptions.language); + const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language); const extension = await this.extensionsScanner.scanExtension(extensionsScannerInput); if (!extension) { return null; @@ -221,7 +225,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem } async scanOneOrMultipleExtensions(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise { - const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, extensionType, true, scanOptions.language); + const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language); const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(extensionsScannerInput); return this.applyScanOptions(extensions, scanOptions, true); } @@ -292,7 +296,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem private async scanDefaultSystemExtensions(useCache: boolean, language: string | undefined): Promise { this.logService.trace('Started scanning system extensions'); - const extensionsScannerInput = await this.createExtensionScannerInput(this.systemExtensionsLocation, ExtensionType.System, true, language); + const extensionsScannerInput = await this.createExtensionScannerInput(this.systemExtensionsLocation, false, ExtensionType.System, true, language); const extensionsScanner = useCache && !extensionsScannerInput.devMode ? this.systemExtensionsCachedScanner : this.extensionsScanner; const result = await extensionsScanner.scanExtensions(extensionsScannerInput); this.logService.trace('Scanned system extensions:', result.length); @@ -322,7 +326,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem break; } } - const result = await Promise.all(devSystemExtensionsLocations.map(async location => this.extensionsScanner.scanExtension((await this.createExtensionScannerInput(location, ExtensionType.System, true, language))))); + const result = await Promise.all(devSystemExtensionsLocations.map(async location => this.extensionsScanner.scanExtension((await this.createExtensionScannerInput(location, false, ExtensionType.System, true, language))))); this.logService.trace('Scanned dev system extensions:', result.length); return coalesce(result); } @@ -336,7 +340,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem } } - private async createExtensionScannerInput(location: URI, type: ExtensionType, excludeObsolete: boolean, language: string | undefined, validate: boolean = true): Promise { + private async createExtensionScannerInput(location: URI, profile: boolean, type: ExtensionType, excludeObsolete: boolean, language: string | undefined, validate: boolean = true): Promise { const translations = await this.getTranslations(language ?? platform.language); let mtime: number | undefined; try { @@ -350,6 +354,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem return new ExtensionScannerInput( location, mtime, + profile, type, excludeObsolete, validate, @@ -369,6 +374,7 @@ class ExtensionScannerInput { constructor( public readonly location: URI, public readonly mtime: number | undefined, + public readonly profile: boolean, public readonly type: ExtensionType, public readonly excludeObsolete: boolean, public readonly validate: boolean, @@ -395,6 +401,7 @@ class ExtensionScannerInput { return ( isEqual(a.location, b.location) && a.mtime === b.mtime + && a.profile === b.profile && a.type === b.type && a.excludeObsolete === b.excludeObsolete && a.validate === b.validate @@ -418,6 +425,8 @@ type NlsConfiguration = { class ExtensionsScanner extends Disposable { constructor( + private readonly obsoleteFile: URI, + protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService, protected readonly fileService: IFileService, protected readonly logService: ILogService ) { @@ -425,31 +434,48 @@ class ExtensionsScanner extends Disposable { } async scanExtensions(input: ExtensionScannerInput): Promise { + const extensions = input.profile ? await this.scanExtensionsFromProfile(input) : await this.scanExtensionsFromLocation(input); + let obsolete: IStringDictionary = {}; + if (input.excludeObsolete && input.type === ExtensionType.User) { + try { + const raw = (await this.fileService.readFile(this.obsoleteFile)).value.toString(); + obsolete = JSON.parse(raw); + } catch (error) { /* ignore */ } + } + return isEmptyObject(obsolete) ? extensions : extensions.filter(e => !obsolete[ExtensionKey.create(e).toString()]); + } + + private async scanExtensionsFromLocation(input: ExtensionScannerInput): Promise { const stat = await this.fileService.resolve(input.location); - if (stat.children) { - let obsolete: IStringDictionary = {}; - if (input.excludeObsolete && input.type === ExtensionType.User) { - try { - const raw = (await this.fileService.readFile(joinPath(input.location, '.obsolete'))).value.toString(); - obsolete = JSON.parse(raw); - } catch (error) { /* ignore */ } - } - const extensions = await Promise.all( - stat.children.map(async c => { - if (!c.isDirectory) { - return null; - } - // Do not consider user extension folder starting with `.` - if (input.type === ExtensionType.User && basename(c.resource).indexOf('.') === 0) { - return null; - } - const extensionScannerInput = new ExtensionScannerInput(c.resource, input.mtime, input.type, input.excludeObsolete, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); - const extension = await this.scanExtension(extensionScannerInput); - return extension && !obsolete[ExtensionKey.create(extension).toString()] ? extension : null; - })); - return coalesce(extensions); + if (!stat.children?.length) { + return []; } - return []; + const extensions = await Promise.all( + stat.children.map(async c => { + if (!c.isDirectory) { + return null; + } + // Do not consider user extension folder starting with `.` + if (input.type === ExtensionType.User && basename(c.resource).indexOf('.') === 0) { + return null; + } + const extensionScannerInput = new ExtensionScannerInput(c.resource, input.mtime, input.profile, input.type, input.excludeObsolete, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); + return this.scanExtension(extensionScannerInput); + })); + return coalesce(extensions); + } + + private async scanExtensionsFromProfile(input: ExtensionScannerInput): Promise { + const scannedProfileExtensions = await this.extensionsProfileScannerService.scanProfileExtensions(input.location); + if (!scannedProfileExtensions.length) { + return []; + } + const extensions = await Promise.all( + scannedProfileExtensions.map(async extensionInfo => { + const extensionScannerInput = new ExtensionScannerInput(extensionInfo.location, input.mtime, input.profile, input.type, input.excludeObsolete, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); + return this.scanExtension(extensionScannerInput); + })); + return coalesce(extensions); } async scanOneOrMultipleExtensions(input: ExtensionScannerInput): Promise { @@ -466,7 +492,7 @@ class ExtensionsScanner extends Disposable { } } - async scanExtension(input: ExtensionScannerInput): Promise { + async scanExtension(input: ExtensionScannerInput, metadata?: Metadata): Promise { try { let manifest = await this.scanExtensionManifest(input.location); if (manifest) { @@ -474,7 +500,7 @@ class ExtensionsScanner extends Disposable { if (!manifest.publisher) { manifest.publisher = UNDEFINED_PUBLISHER; } - const metadata = manifest.__metadata; + metadata = metadata ?? manifest.__metadata; delete manifest.__metadata; const id = getGalleryExtensionId(manifest.publisher, manifest.name); const identifier = metadata?.id ? { id, uuid: metadata.id } : { id }; @@ -751,10 +777,12 @@ class CachedExtensionsScanner extends ExtensionsScanner { constructor( private readonly cacheFile: URI, + obsoleteFile: URI, + extensionsProfileScannerService: IExtensionsProfileScannerService, fileService: IFileService, logService: ILogService ) { - super(fileService, logService); + super(obsoleteFile, extensionsProfileScannerService, fileService, logService); } override async scanExtensions(input: ExtensionScannerInput): Promise { @@ -846,6 +874,7 @@ export class NativeExtensionsScannerService extends AbstractExtensionsScannerSer userExtensionsLocation: URI, userHome: URI, userDataPath: URI, + extensionsProfileScannerService: IExtensionsProfileScannerService, fileService: IFileService, logService: ILogService, environmentService: IEnvironmentService, @@ -856,7 +885,7 @@ export class NativeExtensionsScannerService extends AbstractExtensionsScannerSer userExtensionsLocation, joinPath(userHome, '.vscode-oss-dev', 'extensions', 'control.json'), joinPath(userDataPath, MANIFEST_CACHE_FOLDER), - fileService, logService, environmentService, productService); + extensionsProfileScannerService, fileService, logService, environmentService, productService); this.translationsPromise = (async () => { if (platform.translationsConfigFile) { try { diff --git a/src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts b/src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts index 7914d9aff79b1..fc1a83a7aafca 100644 --- a/src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts @@ -5,6 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { IExtensionsScannerService, NativeExtensionsScannerService, } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -14,6 +15,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; export class ExtensionsScannerService extends NativeExtensionsScannerService implements IExtensionsScannerService { constructor( + @IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService, @IFileService fileService: IFileService, @ILogService logService: ILogService, @INativeEnvironmentService environmentService: INativeEnvironmentService, @@ -24,7 +26,7 @@ export class ExtensionsScannerService extends NativeExtensionsScannerService imp URI.file(environmentService.extensionsPath), environmentService.userHome, URI.file(environmentService.userDataPath), - fileService, logService, environmentService, productService); + extensionsProfileScannerService, fileService, logService, environmentService, productService); } } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 2e52feaa54fa2..ed0f6f97b2aca 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -22,12 +22,13 @@ import { extract, ExtractError, IFile, zip } from 'vs/base/node/zip'; import * as nls from 'vs/nls'; import { IDownloadService } from 'vs/platform/download/common/download'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -import { AbstractExtensionManagementService, AbstractExtensionTask, IInstallExtensionTask, IUninstallExtensionTask, joinErrors, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService'; +import { AbstractExtensionManagementService, AbstractInstallExtensionTask, AbstractUninstallExtensionTask, IInstallExtensionTask, IUninstallExtensionTask, joinErrors, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService'; import { - ExtensionManagementError, ExtensionManagementErrorCode, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallOperation, InstallOptions, - InstallVSIXOptions, Metadata + ExtensionManagementError, ExtensionManagementErrorCode, IExtensionGalleryService, IExtensionIdentifier, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallOperation, + IServerExtensionManagementService, Metadata, ServerInstallOptions, ServerInstallVSIXOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions, computeTargetPlatform, ExtensionKey, getGalleryExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { IExtensionsScannerService, IScannedExtension, ScanOptions } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsDownloader } from 'vs/platform/extensionManagement/node/extensionDownloader'; import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle'; @@ -49,7 +50,7 @@ interface InstallableExtension { metadata?: Metadata; } -export class ExtensionManagementService extends AbstractExtensionManagementService implements IExtensionManagementService { +export class ExtensionManagementService extends AbstractExtensionManagementService implements IServerExtensionManagementService { private readonly extensionsScanner: ExtensionsScanner; private readonly manifestCache: ExtensionsManifestCache; @@ -60,11 +61,12 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi @ITelemetryService telemetryService: ITelemetryService, @ILogService logService: ILogService, @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, + @IExtensionsProfileScannerService protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService, @IDownloadService private downloadService: IDownloadService, @IInstantiationService instantiationService: IInstantiationService, @IFileService private readonly fileService: IFileService, @IProductService productService: IProductService, - @IUriIdentityService uriIdentityService: IUriIdentityService + @IUriIdentityService uriIdentityService: IUriIdentityService, ) { super(galleryService, telemetryService, logService, productService); const extensionLifecycle = this._register(instantiationService.createInstance(ExtensionsLifecycle)); @@ -108,11 +110,11 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi return getManifest(zipPath); } - getInstalled(type: ExtensionType | null = null): Promise { - return this.extensionsScanner.scanExtensions(type); + getInstalled(type?: ExtensionType, profileLocation?: URI): Promise { + return this.extensionsScanner.scanExtensions(type ?? null, profileLocation); } - async install(vsix: URI, options: InstallVSIXOptions = {}): Promise { + async install(vsix: URI, options: ServerInstallVSIXOptions = {}): Promise { this.logService.trace('ExtensionManagementService#install', vsix.toString()); const downloadLocation = await this.downloadVsix(vsix); @@ -155,12 +157,12 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi return downloadedLocation; } - protected createInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions & InstallVSIXOptions): IInstallExtensionTask { - return URI.isUri(extension) ? new InstallVSIXTask(manifest, extension, options, this.galleryService, this.extensionsScanner, this.logService) : new InstallGalleryExtensionTask(extension, options, this.extensionsDownloader, this.extensionsScanner, this.logService); + protected createInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask { + return URI.isUri(extension) ? new InstallVSIXTask(manifest, extension, options, this.galleryService, this.extensionsScanner, this.logService, this.extensionsProfileScannerService) : new InstallGalleryExtensionTask(extension, options, this.extensionsDownloader, this.extensionsScanner, this.logService, this.extensionsProfileScannerService); } protected createUninstallExtensionTask(extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask { - return new UninstallExtensionTask(extension, options, this.extensionsScanner); + return new UninstallExtensionTask(extension, options, this.extensionsScanner, this.extensionsProfileScannerService); } private async collectFiles(extension: ILocalExtension): Promise { @@ -211,13 +213,13 @@ class ExtensionsScanner extends Disposable { await this.removeOutdatedExtensions(); } - async scanExtensions(type: ExtensionType | null): Promise { - const scannedOptions: ScanOptions = { includeInvalid: true }; + async scanExtensions(type: ExtensionType | null, profileLocation: URI | undefined): Promise { + const userScanOptions: ScanOptions = { includeInvalid: true, profileLocation }; let scannedExtensions: IScannedExtension[] = []; if (type === null || type === ExtensionType.System) { - scannedExtensions.push(...await this.extensionsScannerService.scanAllExtensions(scannedOptions)); + scannedExtensions.push(...await this.extensionsScannerService.scanAllExtensions({ includeInvalid: true }, userScanOptions)); } else if (type === ExtensionType.User) { - scannedExtensions.push(...await this.extensionsScannerService.scanUserExtensions(scannedOptions)); + scannedExtensions.push(...await this.extensionsScannerService.scanUserExtensions(userScanOptions)); } scannedExtensions = type !== null ? scannedExtensions.filter(r => r.type === type) : scannedExtensions; return Promise.all(scannedExtensions.map(extension => this.toLocalExtension(extension))); @@ -240,7 +242,8 @@ class ExtensionsScanner extends Disposable { } await this.extractAtLocation(extensionKey, zipPath, tempPath, token); - await this.extensionsScannerService.updateMetadata(URI.file(tempPath), { ...metadata, installedTimestamp: Date.now() }); + metadata = { ...metadata, installedTimestamp: Date.now() }; + await this.extensionsScannerService.updateMetadata(URI.file(tempPath), metadata); try { await this.rename(extensionKey, tempPath, extensionPath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */); @@ -456,7 +459,7 @@ class ExtensionsScanner extends Disposable { } -abstract class AbstractInstallExtensionTask extends AbstractExtensionTask implements IInstallExtensionTask { +abstract class InstallExtensionTask extends AbstractInstallExtensionTask implements IInstallExtensionTask { protected _operation = InstallOperation.Install; get operation() { return isUndefined(this.options.operation) ? this._operation : this.options.operation; } @@ -464,11 +467,12 @@ abstract class AbstractInstallExtensionTask extends AbstractExtensionTask { @@ -514,20 +518,21 @@ abstract class AbstractInstallExtensionTask extends AbstractExtensionTask { - const installed = await this.extensionsScanner.scanExtensions(null); + protected async install(token: CancellationToken): Promise<{ local: ILocalExtension; metadata: Metadata }> { + const installed = await this.extensionsScanner.scanExtensions(null, undefined); const existingExtension = installed.find(i => areSameExtensions(i.identifier, this.gallery.identifier)); if (existingExtension) { this._operation = InstallOperation.Update; @@ -549,7 +554,7 @@ class InstallGalleryExtensionTask extends AbstractInstallExtensionTask { if (existingExtension && (existingExtension.targetPlatform !== local.targetPlatform || semver.neq(existingExtension.manifest.version, local.manifest.version))) { await this.extensionsScanner.setUninstalled(existingExtension); } - return local; + return { local, metadata: installableExtension.metadata }; } catch (error) { await this.deleteDownloadedVSIX(installableExtension.zipPath); throw error; @@ -592,22 +597,23 @@ class InstallGalleryExtensionTask extends AbstractInstallExtensionTask { } } -class InstallVSIXTask extends AbstractInstallExtensionTask { +class InstallVSIXTask extends InstallExtensionTask { constructor( private readonly manifest: IExtensionManifest, private readonly location: URI, - options: InstallOptions, + options: ServerInstallOptions, private readonly galleryService: IExtensionGalleryService, extensionsScanner: ExtensionsScanner, - logService: ILogService + logService: ILogService, + extensionsProfileScannerService: IExtensionsProfileScannerService, ) { - super({ id: getGalleryExtensionId(manifest.publisher, manifest.name) }, location, options, extensionsScanner, logService); + super({ id: getGalleryExtensionId(manifest.publisher, manifest.name) }, location, options, extensionsScanner, logService, extensionsProfileScannerService); } - protected async doRun(token: CancellationToken): Promise { + protected async install(token: CancellationToken): Promise<{ local: ILocalExtension; metadata: Metadata }> { const extensionKey = new ExtensionKey(this.identifier, this.manifest.version); - const installedExtensions = await this.extensionsScanner.scanExtensions(ExtensionType.User); + const installedExtensions = await this.extensionsScanner.scanExtensions(ExtensionType.User, undefined); const existing = installedExtensions.find(i => areSameExtensions(this.identifier, i.identifier)); const metadata = await this.getMetadata(this.identifier.id, this.manifest.version, token); metadata.isMachineScoped = this.options.isMachineScoped || existing?.isMachineScoped; @@ -637,7 +643,8 @@ class InstallVSIXTask extends AbstractInstallExtensionTask { } } - return this.installExtension({ zipPath: path.resolve(this.location.fsPath), key: extensionKey, metadata }, token); + const local = await this.installExtension({ zipPath: path.resolve(this.location.fsPath), key: extensionKey, metadata }, token); + return { local, metadata }; } private async getMetadata(id: string, version: string, token: CancellationToken): Promise { @@ -662,15 +669,18 @@ class InstallVSIXTask extends AbstractInstallExtensionTask { } } -class UninstallExtensionTask extends AbstractExtensionTask implements IUninstallExtensionTask { +class UninstallExtensionTask extends AbstractUninstallExtensionTask implements IUninstallExtensionTask { constructor( - readonly extension: ILocalExtension, - private readonly options: UninstallExtensionTaskOptions, - private readonly extensionsScanner: ExtensionsScanner - ) { super(); } + extension: ILocalExtension, + options: UninstallExtensionTaskOptions, + private readonly extensionsScanner: ExtensionsScanner, + extensionsProfileScannerService: IExtensionsProfileScannerService, + ) { + super(extension, options, extensionsProfileScannerService); + } - protected async doRun(token: CancellationToken): Promise { + protected async uninstall(token: CancellationToken): Promise { const toUninstall: ILocalExtension[] = []; const userExtensions = await this.extensionsScanner.scanUserExtensions(false); if (this.options.versionOnly) { diff --git a/src/vs/platform/extensionManagement/node/extensionsScannerService.ts b/src/vs/platform/extensionManagement/node/extensionsScannerService.ts index 500220cfb5801..c0d58fdc82234 100644 --- a/src/vs/platform/extensionManagement/node/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/node/extensionsScannerService.ts @@ -5,6 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { IExtensionsScannerService, NativeExtensionsScannerService, } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; @@ -13,6 +14,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; export class ExtensionsScannerService extends NativeExtensionsScannerService implements IExtensionsScannerService { constructor( + @IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService, @IFileService fileService: IFileService, @ILogService logService: ILogService, @INativeEnvironmentService environmentService: INativeEnvironmentService, @@ -23,7 +25,7 @@ export class ExtensionsScannerService extends NativeExtensionsScannerService imp URI.file(environmentService.extensionsPath), environmentService.userHome, URI.file(environmentService.userDataPath), - fileService, logService, environmentService, productService); + extensionsProfileScannerService, fileService, logService, environmentService, productService); } } diff --git a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts index 825f7ab275ae5..2c52622d18470 100644 --- a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts +++ b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts @@ -8,6 +8,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { dirname, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { AbstractExtensionsScannerService, IExtensionsScannerService, IScannedExtensionManifest, Translations } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionType, IExtensionManifest, MANIFEST_CACHE_FOLDER, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; @@ -23,6 +24,7 @@ const ROOT = URI.file('/ROOT'); class ExtensionsScannerService extends AbstractExtensionsScannerService implements IExtensionsScannerService { constructor( + @IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService, @IFileService fileService: IFileService, @ILogService logService: ILogService, @INativeEnvironmentService nativeEnvironmentService: INativeEnvironmentService, @@ -33,7 +35,7 @@ class ExtensionsScannerService extends AbstractExtensionsScannerService implemen URI.file(nativeEnvironmentService.extensionsPath), joinPath(nativeEnvironmentService.userHome, '.vscode-oss-dev', 'extensions', 'control.json'), joinPath(ROOT, MANIFEST_CACHE_FOLDER), - fileService, logService, nativeEnvironmentService, productService); + extensionsProfileScannerService, fileService, logService, nativeEnvironmentService, productService); } protected async getTranslations(language: string): Promise { @@ -64,6 +66,7 @@ suite('NativeExtensionsScanerService Test', () => { extensionsPath: userExtensionsLocation.fsPath, }); instantiationService.stub(IProductService, { version: '1.66.0' }); + instantiationService.stub(IExtensionsProfileScannerService, new ExtensionsProfileScannerService(fileService, logService)); await fileService.createFolder(systemExtensionsLocation); await fileService.createFolder(userExtensionsLocation); }); diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index 721fcdabcb12a..0c12c065a8d35 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -17,7 +17,7 @@ export interface IUserDataProfile { readonly keybindingsResource: URI; readonly tasksResource: URI; readonly snippetsHome: URI; - readonly extensionsResource: URI; + readonly extensionsResource: URI | undefined; } export const IUserDataProfilesService = createDecorator('IUserDataProfilesService'); @@ -53,7 +53,7 @@ export class UserDataProfilesService extends Disposable implements IUserDataProf keybindingsResource: joinPath(defaultProfileLocation, 'keybindings.json'), tasksResource: joinPath(defaultProfileLocation, 'tasks.json'), snippetsHome: joinPath(defaultProfileLocation, 'snippets'), - extensionsResource: joinPath(defaultProfileLocation, 'extensions.json') + extensionsResource: undefined }; } } diff --git a/src/vs/server/node/extensionsScannerService.ts b/src/vs/server/node/extensionsScannerService.ts index 7f551bf55289e..5638c70ad0154 100644 --- a/src/vs/server/node/extensionsScannerService.ts +++ b/src/vs/server/node/extensionsScannerService.ts @@ -6,6 +6,7 @@ import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { AbstractExtensionsScannerService, IExtensionsScannerService, Translations } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { MANIFEST_CACHE_FOLDER } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; @@ -16,6 +17,7 @@ import { getNLSConfiguration, InternalNLSConfiguration } from 'vs/server/node/re export class ExtensionsScannerService extends AbstractExtensionsScannerService implements IExtensionsScannerService { constructor( + @IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService, @IFileService fileService: IFileService, @ILogService logService: ILogService, @INativeEnvironmentService private readonly nativeEnvironmentService: INativeEnvironmentService, @@ -26,7 +28,7 @@ export class ExtensionsScannerService extends AbstractExtensionsScannerService i URI.file(nativeEnvironmentService.extensionsPath), joinPath(nativeEnvironmentService.userHome, '.vscode-oss-dev', 'extensions', 'control.json'), joinPath(URI.file(nativeEnvironmentService.userDataPath), MANIFEST_CACHE_FOLDER), - fileService, logService, nativeEnvironmentService, productService); + extensionsProfileScannerService, fileService, logService, nativeEnvironmentService, productService); } protected async getTranslations(language: string): Promise { diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index e496c3e765cfa..ba99f886efad3 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -43,6 +43,7 @@ import { isWindows } from 'vs/base/common/platform'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService'; import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; class CliMain extends Disposable { @@ -105,6 +106,7 @@ class CliMain extends Disposable { services.set(IDownloadService, new SyncDescriptor(DownloadService)); services.set(ITelemetryService, NullTelemetryService); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryServiceWithNoStorageService)); + services.set(IExtensionsProfileScannerService, new SyncDescriptor(ExtensionsProfileScannerService)); services.set(IExtensionsScannerService, new SyncDescriptor(ExtensionsScannerService)); services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionManagementCLIService, new SyncDescriptor(ExtensionManagementCLIService)); diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index a072fa8bb0e11..5fa2e40541c3b 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -70,6 +70,7 @@ import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from 'vs/workbench/services/remote/co import { ExtensionHostStatusService, IExtensionHostStatusService } from 'vs/server/node/extensionHostStatusService'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService'; +import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; const eventPrefix = 'monacoworkbench'; @@ -154,6 +155,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken const downloadChannel = socketServer.getChannel('download', router); services.set(IDownloadService, new DownloadServiceChannelClient(downloadChannel, () => getUriTransformer('renderer') /* TODO: @Sandy @Joao need dynamic context based router */)); + services.set(IExtensionsProfileScannerService, new SyncDescriptor(ExtensionsProfileScannerService)); services.set(IExtensionsScannerService, new SyncDescriptor(ExtensionsScannerService)); services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts index 4a42f77188a07..ef4da6db30df9 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts @@ -15,7 +15,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { WebExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/webExtensionManagementService'; import { IExtension } from 'vs/platform/extensions/common/extensions'; import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; -import { ILogService } from 'vs/platform/log/common/log'; export class ExtensionManagementServerService implements IExtensionManagementServerService { @@ -29,11 +28,10 @@ export class ExtensionManagementServerService implements IExtensionManagementSer @IRemoteAgentService remoteAgentService: IRemoteAgentService, @ILabelService labelService: ILabelService, @IInstantiationService instantiationService: IInstantiationService, - @ILogService logService: ILogService, ) { const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { - const extensionManagementService = new ExtensionManagementChannelClient(remoteAgentConnection.getChannel('extensions')); + const extensionManagementService = new ExtensionManagementChannelClient(remoteAgentConnection.getChannel('extensions'), undefined); this.remoteExtensionManagementServer = { id: 'remote', extensionManagementService, diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts index 28a5b281506ed..54558b2cdcca3 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts @@ -10,11 +10,12 @@ import { areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionM import { IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { AbstractExtensionManagementService, AbstractExtensionTask, IInstallExtensionTask, IUninstallExtensionTask, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService'; +import { AbstractExtensionManagementService, AbstractInstallExtensionTask, AbstractUninstallExtensionTask, IInstallExtensionTask, IUninstallExtensionTask, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { IProductService } from 'vs/platform/product/common/productService'; import { isBoolean, isUndefined } from 'vs/base/common/types'; +import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; export class WebExtensionManagementService extends AbstractExtensionManagementService implements IExtensionManagementService { @@ -26,6 +27,7 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe @ILogService logService: ILogService, @IWebExtensionsScannerService private readonly webExtensionsScannerService: IWebExtensionsScannerService, @IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService, + @IExtensionsProfileScannerService private readonly extensionsProfileScannerService: IExtensionsProfileScannerService, @IProductService productService: IProductService ) { super(extensionGalleryService, telemetryService, logService, productService); @@ -88,11 +90,11 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe } protected createInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions): IInstallExtensionTask { - return new InstallExtensionTask(manifest, extension, options, this.webExtensionsScannerService); + return new InstallExtensionTask(manifest, extension, options, this.webExtensionsScannerService, this.extensionsProfileScannerService); } protected createUninstallExtensionTask(extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask { - return new UninstallExtensionTask(extension, options, this.webExtensionsScannerService); + return new UninstallExtensionTask(extension, options, this.webExtensionsScannerService, this.extensionsProfileScannerService); } zip(extension: ILocalExtension): Promise { throw new Error('unsupported'); } @@ -123,7 +125,7 @@ function getMetadata(options?: InstallOptions, existingExtension?: IExtension): return metadata; } -class InstallExtensionTask extends AbstractExtensionTask implements IInstallExtensionTask { +class InstallExtensionTask extends AbstractInstallExtensionTask implements IInstallExtensionTask { readonly identifier: IExtensionIdentifier; readonly source: URI | IGalleryExtension; @@ -134,15 +136,16 @@ class InstallExtensionTask extends AbstractExtensionTask implem constructor( manifest: IExtensionManifest, private readonly extension: URI | IGalleryExtension, - private readonly options: InstallOptions, + options: InstallOptions, private readonly webExtensionsScannerService: IWebExtensionsScannerService, + extensionsProfileScannerService: IExtensionsProfileScannerService, ) { - super(); + super(options, extensionsProfileScannerService); this.identifier = URI.isUri(extension) ? { id: getGalleryExtensionId(manifest.publisher, manifest.name) } : extension.identifier; this.source = extension; } - protected async doRun(token: CancellationToken): Promise { + protected async install(token: CancellationToken): Promise<{ local: ILocalExtension; metadata: Metadata }> { const userExtensions = await this.webExtensionsScannerService.scanUserExtensions(); const existingExtension = userExtensions.find(e => areSameExtensions(e.identifier, this.identifier)); if (existingExtension) { @@ -167,21 +170,22 @@ class InstallExtensionTask extends AbstractExtensionTask implem const scannedExtension = URI.isUri(this.extension) ? await this.webExtensionsScannerService.addExtension(this.extension, metadata) : await this.webExtensionsScannerService.addExtensionFromGallery(this.extension, metadata); - return toLocalExtension(scannedExtension); + return { local: toLocalExtension(scannedExtension), metadata }; } } -class UninstallExtensionTask extends AbstractExtensionTask implements IUninstallExtensionTask { +class UninstallExtensionTask extends AbstractUninstallExtensionTask implements IUninstallExtensionTask { constructor( - readonly extension: ILocalExtension, + extension: ILocalExtension, options: UninstallExtensionTaskOptions, private readonly webExtensionsScannerService: IWebExtensionsScannerService, + extensionsProfileScannerService: IExtensionsProfileScannerService, ) { - super(); + super(extension, options, extensionsProfileScannerService); } - protected doRun(token: CancellationToken): Promise { + protected uninstall(token: CancellationToken): Promise { return this.webExtensionsScannerService.removeExtension(this.extension.identifier); } } diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts index 007e0a3fc732a..8379d5f86d6d7 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts @@ -15,6 +15,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IExtension } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export class ExtensionManagementServerService implements IExtensionManagementServerService { @@ -29,9 +30,10 @@ export class ExtensionManagementServerService implements IExtensionManagementSer @ISharedProcessService sharedProcessService: ISharedProcessService, @IRemoteAgentService remoteAgentService: IRemoteAgentService, @ILabelService labelService: ILabelService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @IInstantiationService instantiationService: IInstantiationService, ) { - const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions')); + const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions'), userDataProfilesService); this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") }; const remoteAgentConnection = remoteAgentService.getConnection(); diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts index 9888caf5571a3..1c8af554c31f4 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts @@ -35,7 +35,7 @@ export class NativeRemoteExtensionManagementService extends ExtensionManagementC @INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, @IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService, ) { - super(channel); + super(channel, undefined); } override async install(vsix: URI, options?: InstallVSIXOptions): Promise { diff --git a/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts index 852ea7702e942..059f508842617 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts @@ -15,6 +15,7 @@ import { localize } from 'vs/nls'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { timeout } from 'vs/base/common/async'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export class CachedExtensionScanner { @@ -26,6 +27,7 @@ export class CachedExtensionScanner { @INotificationService private readonly _notificationService: INotificationService, @IHostService private readonly _hostService: IHostService, @IExtensionsScannerService private readonly _extensionsScannerService: IExtensionsScannerService, + @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService, @ILogService private readonly _logService: ILogService, ) { this.scannedExtensions = new Promise((resolve, reject) => { @@ -54,7 +56,7 @@ export class CachedExtensionScanner { const language = platform.language; const [scannedSystemExtensions, scannedUserExtensions] = await Promise.all([ this._extensionsScannerService.scanSystemExtensions({ language, useCache: true, checkControlFile: true }), - this._extensionsScannerService.scanUserExtensions({ language, useCache: true })]); + this._extensionsScannerService.scanUserExtensions({ language, profileLocation: this._userDataProfilesService.defaultProfile.extensionsResource, useCache: true })]); const scannedDevelopedExtensions = await this._extensionsScannerService.scanExtensionsUnderDevelopment({ language }, [...scannedSystemExtensions, ...scannedUserExtensions]); const system = scannedSystemExtensions.map(e => toExtensionDescription(e, false)); const user = scannedUserExtensions.map(e => toExtensionDescription(e, false)); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 992aec9c53fb8..933cf07be3a0d 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -126,6 +126,7 @@ import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService import { ExtensionStorageService, IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; import { IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; +import { IExtensionsProfileScannerService, ExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; registerSingleton(IUserDataSyncLogService, UserDataSyncLogService); registerSingleton(IIgnoredExtensionsManagementService, IgnoredExtensionsManagementService); @@ -142,6 +143,7 @@ registerSingleton(ITextResourceConfigurationService, TextResourceConfigurationSe registerSingleton(IMenuService, MenuService, true); registerSingleton(IDownloadService, DownloadService, true); registerSingleton(IOpenerService, OpenerService, true); +registerSingleton(IExtensionsProfileScannerService, ExtensionsProfileScannerService); //#endregion From 80865fedf62248c5a49cec88ee211732438cfa94 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sat, 21 May 2022 19:54:13 +0200 Subject: [PATCH 213/942] use current profile instead of default profile --- .../sharedProcess/sharedProcessMain.ts | 2 +- src/vs/code/electron-main/main.ts | 2 +- src/vs/code/node/cliProcessMain.ts | 2 +- .../common/extensionManagementIpc.ts | 8 +- .../test/browser/fileUserDataProvider.test.ts | 50 ++++---- .../browser/keybindingsEditorContribution.ts | 2 +- .../browser/preferences.contribution.ts | 4 +- .../common/preferencesContribution.ts | 2 +- .../snippets/browser/configureSnippets.ts | 4 +- .../snippets/browser/snippetsService.ts | 2 +- .../browser/telemetry.contribution.ts | 6 +- .../browser/configurationService.ts | 2 +- .../common/configurationEditingService.ts | 4 +- .../configurationEditingService.test.ts | 18 +-- .../test/browser/configurationService.test.ts | 108 +++++++++--------- .../cachedExtensionScanner.ts | 2 +- .../keybinding/browser/keybindingService.ts | 2 +- .../keybinding/common/keybindingEditing.ts | 2 +- .../test/browser/keybindingEditing.test.ts | 12 +- .../preferences/browser/preferencesService.ts | 4 +- .../profiles/common/settingsProfile.ts | 8 +- 21 files changed, 123 insertions(+), 123 deletions(-) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 47afb70f80a76..e237e90e941d6 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -228,7 +228,7 @@ class SharedProcessMain extends Disposable { services.set(IUserDataProfilesService, userDataProfilesService); // Configuration - const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(userDataProfilesService.currentProfile.settingsResource, fileService)); services.set(IConfigurationService, configurationService); // Storage (global access only) diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index be023e5f03a25..7b5bdb97e5435 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -172,7 +172,7 @@ class CodeMain { services.set(IUserDataProfilesService, userDataProfilesService); // Configuration - const configurationService = new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService); + const configurationService = new ConfigurationService(userDataProfilesService.currentProfile.settingsResource, fileService); services.set(IConfigurationService, configurationService); // Lifecycle diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index a643caf406ec3..e18fbdab46e86 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -135,7 +135,7 @@ class CliMain extends Disposable { services.set(IUserDataProfilesService, userDataProfilesService); // Configuration - const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(userDataProfilesService.currentProfile.settingsResource, fileService)); services.set(IConfigurationService, configurationService); // Init config diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index 06b5af8f3b32d..5f51a32473e90 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -138,7 +138,7 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt } install(vsix: URI, options?: InstallVSIXOptions): Promise { - const serverInstallOptions: ServerInstallVSIXOptions = { ...options, profileLocation: this.userDataProfilesService?.defaultProfile.extensionsResource }; + const serverInstallOptions: ServerInstallVSIXOptions = { ...options, profileLocation: this.userDataProfilesService?.currentProfile.extensionsResource }; return Promise.resolve(this.channel.call('install', [vsix, serverInstallOptions])).then(local => transformIncomingExtension(local, null)); } @@ -147,12 +147,12 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt } installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise { - const serverInstallOptions: ServerInstallOptions = { ...installOptions, profileLocation: this.userDataProfilesService?.defaultProfile.extensionsResource }; + const serverInstallOptions: ServerInstallOptions = { ...installOptions, profileLocation: this.userDataProfilesService?.currentProfile.extensionsResource }; return Promise.resolve(this.channel.call('installFromGallery', [extension, serverInstallOptions])).then(local => transformIncomingExtension(local, null)); } uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { - const serverUninstallOptions: ServerUninstallOptions = { ...options, profileLocation: this.userDataProfilesService?.defaultProfile.extensionsResource }; + const serverUninstallOptions: ServerUninstallOptions = { ...options, profileLocation: this.userDataProfilesService?.currentProfile.extensionsResource }; return Promise.resolve(this.channel.call('uninstall', [extension!, serverUninstallOptions])); } @@ -161,7 +161,7 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt } getInstalled(type: ExtensionType | null = null): Promise { - return Promise.resolve(this.channel.call('getInstalled', [type, this.userDataProfilesService?.defaultProfile.extensionsResource])) + return Promise.resolve(this.channel.call('getInstalled', [type, this.userDataProfilesService?.currentProfile.extensionsResource])) .then(extensions => extensions.map(extension => transformIncomingExtension(extension, null))); } diff --git a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts index 6317d43b979bd..5b281b68e3721 100644 --- a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts +++ b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts @@ -62,25 +62,25 @@ suite('FileUserDataProvider', () => { teardown(() => disposables.clear()); test('exists return false when file does not exist', async () => { - const exists = await testObject.exists(userDataProfilesService.defaultProfile.settingsResource); + const exists = await testObject.exists(userDataProfilesService.currentProfile.settingsResource); assert.strictEqual(exists, false); }); test('read file throws error if not exist', async () => { try { - await testObject.readFile(userDataProfilesService.defaultProfile.settingsResource); + await testObject.readFile(userDataProfilesService.currentProfile.settingsResource); assert.fail('Should fail since file does not exist'); } catch (e) { } }); test('read existing file', async () => { await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('{}')); - const result = await testObject.readFile(userDataProfilesService.defaultProfile.settingsResource); + const result = await testObject.readFile(userDataProfilesService.currentProfile.settingsResource); assert.strictEqual(result.value.toString(), '{}'); }); test('create file', async () => { - const resource = userDataProfilesService.defaultProfile.settingsResource; + const resource = userDataProfilesService.currentProfile.settingsResource; const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'settings.json')); @@ -88,7 +88,7 @@ suite('FileUserDataProvider', () => { }); test('write file creates the file if not exist', async () => { - const resource = userDataProfilesService.defaultProfile.settingsResource; + const resource = userDataProfilesService.currentProfile.settingsResource; const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'settings.json')); @@ -96,7 +96,7 @@ suite('FileUserDataProvider', () => { }); test('write to existing file', async () => { - const resource = userDataProfilesService.defaultProfile.settingsResource; + const resource = userDataProfilesService.currentProfile.settingsResource; await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('{}')); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); @@ -106,33 +106,33 @@ suite('FileUserDataProvider', () => { test('delete file', async () => { await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('')); - await testObject.del(userDataProfilesService.defaultProfile.settingsResource); + await testObject.del(userDataProfilesService.currentProfile.settingsResource); const result = await testObject.exists(joinPath(userDataHomeOnDisk, 'settings.json')); assert.strictEqual(false, result); }); test('resolve file', async () => { await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('')); - const result = await testObject.resolve(userDataProfilesService.defaultProfile.settingsResource); + const result = await testObject.resolve(userDataProfilesService.currentProfile.settingsResource); assert.ok(!result.isDirectory); assert.ok(result.children === undefined); }); test('exists return false for folder that does not exist', async () => { - const exists = await testObject.exists(userDataProfilesService.defaultProfile.snippetsHome); + const exists = await testObject.exists(userDataProfilesService.currentProfile.snippetsHome); assert.strictEqual(exists, false); }); test('exists return true for folder that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); - const exists = await testObject.exists(userDataProfilesService.defaultProfile.snippetsHome); + const exists = await testObject.exists(userDataProfilesService.currentProfile.snippetsHome); assert.strictEqual(exists, true); }); test('read file throws error for folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); try { - await testObject.readFile(userDataProfilesService.defaultProfile.snippetsHome); + await testObject.readFile(userDataProfilesService.currentProfile.snippetsHome); assert.fail('Should fail since read file is not supported for folders'); } catch (e) { } }); @@ -140,7 +140,7 @@ suite('FileUserDataProvider', () => { test('read file under folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); const actual = await testObject.readFile(resource); assert.strictEqual(actual.resource.toString(), resource.toString()); assert.strictEqual(actual.value.toString(), '{}'); @@ -149,7 +149,7 @@ suite('FileUserDataProvider', () => { test('read file under sub folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets', 'java')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'java', 'settings.json'), VSBuffer.fromString('{}')); - const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'java/settings.json'); + const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'java/settings.json'); const actual = await testObject.readFile(resource); assert.strictEqual(actual.resource.toString(), resource.toString()); assert.strictEqual(actual.value.toString(), '{}'); @@ -157,7 +157,7 @@ suite('FileUserDataProvider', () => { test('create file under folder that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); - const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -165,7 +165,7 @@ suite('FileUserDataProvider', () => { }); test('create file under folder that does not exist', async () => { - const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -174,7 +174,7 @@ suite('FileUserDataProvider', () => { test('write to not existing file under container that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); - const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -182,7 +182,7 @@ suite('FileUserDataProvider', () => { }); test('write to not existing file under container that does not exists', async () => { - const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -192,7 +192,7 @@ suite('FileUserDataProvider', () => { test('write to existing file under container', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -200,7 +200,7 @@ suite('FileUserDataProvider', () => { }); test('write file under sub container', async () => { - const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'java/settings.json'); + const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'java/settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'java', 'settings.json')); @@ -209,7 +209,7 @@ suite('FileUserDataProvider', () => { test('delete throws error for folder that does not exist', async () => { try { - await testObject.del(userDataProfilesService.defaultProfile.snippetsHome); + await testObject.del(userDataProfilesService.currentProfile.snippetsHome); assert.fail('Should fail the folder does not exist'); } catch (e) { } }); @@ -217,14 +217,14 @@ suite('FileUserDataProvider', () => { test('delete not existing file under container that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); try { - await testObject.del(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json')); + await testObject.del(joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json')); assert.fail('Should fail since file does not exist'); } catch (e) { } }); test('delete not existing file under container that does not exists', async () => { try { - await testObject.del(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json')); + await testObject.del(joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json')); assert.fail('Should fail since file does not exist'); } catch (e) { } }); @@ -232,7 +232,7 @@ suite('FileUserDataProvider', () => { test('delete existing file under folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - await testObject.del(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json')); + await testObject.del(joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json')); const exists = await testObject.exists(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); assert.strictEqual(exists, false); }); @@ -240,11 +240,11 @@ suite('FileUserDataProvider', () => { test('resolve folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - const result = await testObject.resolve(userDataProfilesService.defaultProfile.snippetsHome); + const result = await testObject.resolve(userDataProfilesService.currentProfile.snippetsHome); assert.ok(result.isDirectory); assert.ok(result.children !== undefined); assert.strictEqual(result.children!.length, 1); - assert.strictEqual(result.children![0].resource.toString(), joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json').toString()); + assert.strictEqual(result.children![0].resource.toString(), joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json').toString()); }); test('read backup file', async () => { diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts index 04afba87a05a6..acab7ebe22088 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts @@ -380,7 +380,7 @@ function isInterestingEditorModel(editor: ICodeEditor, userDataProfilesService: if (!model) { return false; } - return isEqual(model.uri, userDataProfilesService.defaultProfile.keybindingsResource); + return isEqual(model.uri, userDataProfilesService.currentProfile.keybindingsResource); } registerEditorContribution(DefineKeybindingController.ID, DefineKeybindingController); diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index f7c5eb3353593..41a5b9fbaa54d 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -252,7 +252,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon icon: preferencesOpenSettingsIcon, menu: [{ id: MenuId.EditorTitle, - when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(that.userDataProfileService.defaultProfile.settingsResource.toString()), ContextKeyExpr.not('isInDiffEditor')), + when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(that.userDataProfileService.currentProfile.settingsResource.toString()), ContextKeyExpr.not('isInDiffEditor')), group: 'navigation', order: 1 }] @@ -744,7 +744,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon { id: MenuId.CommandPalette }, { id: MenuId.EditorTitle, - when: ResourceContextKey.Resource.isEqualTo(that.userDataProfileService.defaultProfile.keybindingsResource.toString()), + when: ResourceContextKey.Resource.isEqualTo(that.userDataProfileService.currentProfile.keybindingsResource.toString()), group: 'navigation', order: 1, } diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index cba9f70de2849..1aad47f77dc0a 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -71,7 +71,7 @@ export class PreferencesContribution implements IWorkbenchContribution { }, ({ resource, options }): EditorInputWithOptions => { // Global User Settings File - if (isEqual(resource, this.userDataProfilesService.defaultProfile.settingsResource)) { + if (isEqual(resource, this.userDataProfilesService.currentProfile.settingsResource)) { return { editor: this.preferencesService.createSplitJsonEditorInput(ConfigurationTarget.USER_LOCAL, resource), options }; } diff --git a/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts b/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts index b533257b37553..2e3c6bd3216e2 100644 --- a/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts +++ b/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts @@ -85,7 +85,7 @@ async function computePicks(snippetService: ISnippetsService, userDataProfilesSe } } - const dir = userDataProfilesService.defaultProfile.snippetsHome; + const dir = userDataProfilesService.currentProfile.snippetsHome; for (const languageId of languageService.getRegisteredLanguageIds()) { const label = languageService.getLanguageName(languageId); if (label && !seen.has(languageId)) { @@ -239,7 +239,7 @@ registerAction2(class ConfigureSnippets extends Action2 { const globalSnippetPicks: SnippetPick[] = [{ scope: nls.localize('new.global_scope', 'global'), label: nls.localize('new.global', "New Global Snippets file..."), - uri: userDataProfilesService.defaultProfile.snippetsHome + uri: userDataProfilesService.currentProfile.snippetsHome }]; const workspaceSnippetPicks: SnippetPick[] = []; diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts index 53451fb26d33e..28a410b98c3d2 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts @@ -350,7 +350,7 @@ class SnippetsService implements ISnippetsService { } private async _initUserSnippets(): Promise { - const userSnippetsFolder = this._userDataProfilesService.defaultProfile.snippetsHome; + const userSnippetsFolder = this._userDataProfilesService.currentProfile.snippetsHome; await this._fileService.createFolder(userSnippetsFolder); return await this._initFolderSnippets(SnippetSource.User, userSnippetsFolder, this._disposables); } diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index f84c4d822afa8..3ee46318427ae 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -166,17 +166,17 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr } // Check for global settings file - if (isEqual(resource, this.userDataProfilesService.defaultProfile.settingsResource)) { + if (isEqual(resource, this.userDataProfilesService.currentProfile.settingsResource)) { return 'global-settings'; } // Check for keybindings file - if (isEqual(resource, this.userDataProfilesService.defaultProfile.keybindingsResource)) { + if (isEqual(resource, this.userDataProfilesService.currentProfile.keybindingsResource)) { return 'keybindings'; } // Check for snippets - if (isEqualOrParent(resource, this.userDataProfilesService.defaultProfile.snippetsHome)) { + if (isEqualOrParent(resource, this.userDataProfilesService.currentProfile.snippetsHome)) { return 'snippets'; } diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index faa9e6e2b7ed5..0d2651554ce41 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -118,7 +118,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat this.logService = logService; this._configuration = new Configuration(this.defaultConfiguration.configurationModel, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); this.cachedFolderConfigs = new ResourceMap(); - this.localUserConfiguration = this._register(new UserConfiguration(userDataProfilesService.defaultProfile, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService)); + this.localUserConfiguration = this._register(new UserConfiguration(userDataProfilesService.currentProfile, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService)); this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); if (remoteAuthority) { const remoteUserConfiguration = this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, fileService, uriIdentityService, remoteAgentService)); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index f060f4452c142..7a2d1420e659e 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -604,9 +604,9 @@ export class ConfigurationEditingService { private getConfigurationFileResource(target: EditableConfigurationTarget, standAloneConfigurationKey: string | undefined, relativePath: string, resource: URI | null | undefined): URI | null { if (target === EditableConfigurationTarget.USER_LOCAL) { if (standAloneConfigurationKey === TASKS_CONFIGURATION_KEY) { - return this.userDataProfilesService.defaultProfile.tasksResource; + return this.userDataProfilesService.currentProfile.tasksResource; } else { - return this.userDataProfilesService.defaultProfile.settingsResource; + return this.userDataProfilesService.currentProfile.settingsResource; } } if (target === EditableConfigurationTarget.USER_REMOTE) { diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts index 09c980dd644f4..b39a2901a883d 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts @@ -136,7 +136,7 @@ suite('ConfigurationEditingService', () => { }); test('errors cases - invalid configuration', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); try { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }, { donotNotifyError: true }); } catch (error) { @@ -185,36 +185,36 @@ suite('ConfigurationEditingService', () => { test('write one setting - empty file', async () => { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }); - const contents = await fileService.readFile(userDataProfilesService.defaultProfile.settingsResource); + const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.strictEqual(parsed['configurationEditing.service.testSetting'], 'value'); }); test('write one setting - existing file', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }); - const contents = await fileService.readFile(userDataProfilesService.defaultProfile.settingsResource); + const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.strictEqual(parsed['configurationEditing.service.testSetting'], 'value'); assert.strictEqual(parsed['my.super.setting'], 'my.super.value'); }); test('remove an existing setting - existing file', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value", "configurationEditing.service.testSetting": "value" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value", "configurationEditing.service.testSetting": "value" }')); await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: undefined }); - const contents = await fileService.readFile(userDataProfilesService.defaultProfile.settingsResource); + const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.deepStrictEqual(Object.keys(parsed), ['my.super.setting']); assert.strictEqual(parsed['my.super.setting'], 'my.super.value'); }); test('remove non existing setting - existing file', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: undefined }); - const contents = await fileService.readFile(userDataProfilesService.defaultProfile.settingsResource); + const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.deepStrictEqual(Object.keys(parsed), ['my.super.setting']); assert.strictEqual(parsed['my.super.setting'], 'my.super.value'); @@ -225,7 +225,7 @@ suite('ConfigurationEditingService', () => { const value = { 'configurationEditing.service.testSetting': 'overridden value' }; await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key, value }); - const contents = await fileService.readFile(userDataProfilesService.defaultProfile.settingsResource); + const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.deepStrictEqual(parsed[key], value); }); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 7ffce88d01be5..a1cd6a67918c7 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -503,7 +503,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a folder workspace from an empty workspace with no configuration changes', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -528,7 +528,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a folder workspace from an empty workspace with configuration changes', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -555,7 +555,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a multi root workspace from an empty workspace with no configuration changes', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -578,7 +578,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a multi root workspace from an empty workspace with configuration changes', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -605,7 +605,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a folder workspace from a folder workspace with no configuration changes', async () => { await testObject.initialize(convertToWorkspacePayload(joinPath(ROOT, 'a'))); - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); testObject.onDidChangeWorkbenchState(target); @@ -757,13 +757,13 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('globals override defaults', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'userValue'); }); test('globals', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('testworkbench.editor.tabs'), true); }); @@ -775,21 +775,21 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('workspace settings override user settings', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'workspaceValue'); }); test('machine overridable settings override user Settings', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.machineOverridableSetting'), 'workspaceValue'); }); test('workspace settings override user settings after defaults are registered ', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.newSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); configurationRegistry.registerConfiguration({ @@ -806,7 +806,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('machine overridable settings override user settings after defaults are registered ', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); configurationRegistry.registerConfiguration({ @@ -824,7 +824,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('application settings are not read from workspace', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -833,7 +833,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('application settings are not read from workspace when workspace folder uri is passed', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -842,7 +842,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('machine settings are not read from workspace', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -851,7 +851,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('machine settings are not read from workspace when workspace folder uri is passed', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -860,7 +860,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get application scope settings are not loaded after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -885,7 +885,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get application scope settings are not loaded after defaults are registered when workspace folder uri is passed', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -910,7 +910,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get machine scope settings are not loaded after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -935,7 +935,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get machine scope settings are not loaded after defaults are registered when workspace folder uri is passed', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -960,7 +960,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('reload configuration emits events after global configuraiton changes', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); const target = sinon.spy(); testObject.onDidChangeConfiguration(target); await testObject.reloadConfiguration(); @@ -976,7 +976,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('reload configuration should not emit event if no changes', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -1000,7 +1000,7 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.strictEqual(actual.workspaceFolderValue, undefined); assert.strictEqual(actual.value, 'isSet'); - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); actual = testObject.inspect('configurationService.folder.testSetting'); assert.strictEqual(actual.defaultValue, 'isSet'); @@ -1026,7 +1026,7 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.deepStrictEqual(actual.workspace, []); assert.deepStrictEqual(actual.workspaceFolder, []); - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); actual = testObject.keys(); assert.ok(actual.default.indexOf('configurationService.folder.testSetting') !== -1); @@ -1181,7 +1181,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('creating workspace settings', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); await new Promise((c, e) => { const disposable = testObject.onDidChangeConfiguration(e => { @@ -1195,7 +1195,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('deleting workspace settings', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); const workspaceSettingsResource = joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'); await fileService.writeFile(workspaceSettingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1210,7 +1210,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is read from workspace when workspace is trusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1226,7 +1226,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is not read from workspace when workspace is changed to trusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1244,7 +1244,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('change event is triggered when workspace is changed to untrusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1259,7 +1259,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is not read from workspace when workspace is not trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1275,7 +1275,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is read when workspace is changed to trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1293,7 +1293,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('change event is triggered when workspace is changed to trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1306,7 +1306,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('adding an restricted setting triggers change event', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); testObject.updateWorkspaceTrust(false); const promise = Event.toPromise(testObject.onDidChangeRestrictedSettings); @@ -1317,7 +1317,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('remove an unregistered setting', async () => { const key = 'configurationService.folder.unknownSetting'; - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1431,7 +1431,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { teardown(() => disposables.clear()); test('application settings are not read from workspace', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1440,7 +1440,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace when folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1449,7 +1449,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1458,7 +1458,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace when folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1467,7 +1467,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('get application scope settings are not loaded after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1492,7 +1492,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('get application scope settings are not loaded after defaults are registered when workspace folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting-2": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting-2": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newSetting-2': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1517,7 +1517,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('workspace settings override user settings after defaults are registered for machine overridable settings ', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newMachineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newMachineOverridableSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newMachineOverridableSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1543,7 +1543,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1552,7 +1552,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder when workspace folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1561,7 +1561,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace folder', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1570,7 +1570,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace folder when workspace folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1579,7 +1579,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1604,7 +1604,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1697,7 +1697,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.strictEqual(actual.workspaceFolderValue, undefined); assert.strictEqual(actual.value, 'isSet'); - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testResourceSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testResourceSetting": "userValue" }')); await testObject.reloadConfiguration(); actual = testObject.inspect('configurationService.workspace.testResourceSetting'); assert.strictEqual(actual.defaultValue, 'isSet'); @@ -1952,7 +1952,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('restricted setting is read from workspace folders when workspace is trusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testRestrictedSetting1': 'workspaceValue' } }], true); await fileService.writeFile(joinPath(testObject.getWorkspace().folders[1].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting2": "workspaceFolder2Value" }')); await testObject.reloadConfiguration(); @@ -1972,7 +1972,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('restricted setting is not read from workspace when workspace is not trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testRestrictedSetting1': 'workspaceValue' } }], true); await fileService.writeFile(joinPath(testObject.getWorkspace().folders[1].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting2": "workspaceFolder2Value" }')); await testObject.reloadConfiguration(); @@ -1991,7 +1991,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('remove an unregistered setting', async () => { const key = 'configurationService.workspace.unknownSetting'; - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.unknownSetting': 'workspaceValue' } }], true); await fileService.writeFile(joinPath(workspaceContextService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "workspaceFolderValue1" }')); await fileService.writeFile(joinPath(workspaceContextService.getWorkspace().folders[1].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "workspaceFolderValue2" }')); @@ -2157,7 +2157,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine settings in local user settings does not override defaults', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "globalValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "globalValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); @@ -2165,7 +2165,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine overridable settings in local user settings does not override defaults', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineOverridableSetting": "globalValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineOverridableSetting": "globalValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); @@ -2200,7 +2200,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine settings in local user settings does not override defaults after defalts are registered ', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineSetting": "userValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); @@ -2219,7 +2219,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine overridable settings in local user settings does not override defaults after defaults are registered ', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineOverridableSetting": "userValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); diff --git a/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts index 059f508842617..69a2d8f5d5393 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts @@ -56,7 +56,7 @@ export class CachedExtensionScanner { const language = platform.language; const [scannedSystemExtensions, scannedUserExtensions] = await Promise.all([ this._extensionsScannerService.scanSystemExtensions({ language, useCache: true, checkControlFile: true }), - this._extensionsScannerService.scanUserExtensions({ language, profileLocation: this._userDataProfilesService.defaultProfile.extensionsResource, useCache: true })]); + this._extensionsScannerService.scanUserExtensions({ language, profileLocation: this._userDataProfilesService.currentProfile.extensionsResource, useCache: true })]); const scannedDevelopedExtensions = await this._extensionsScannerService.scanExtensionsUnderDevelopment({ language }, [...scannedSystemExtensions, ...scannedUserExtensions]); const system = scannedSystemExtensions.map(e => toExtensionDescription(e, false)); const user = scannedUserExtensions.map(e => toExtensionDescription(e, false)); diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 5de2f1a6084e3..889df0a9c9569 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -224,7 +224,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { this._cachedResolver = null; - this.userKeybindings = this._register(new UserKeybindings(userDataProfilesService.defaultProfile.keybindingsResource, fileService, logService)); + this.userKeybindings = this._register(new UserKeybindings(userDataProfilesService.currentProfile.keybindingsResource, fileService, logService)); this.userKeybindings.initialize().then(() => { if (this.userKeybindings.keybindings.length) { this.updateResolver({ source: KeybindingSource.User }); diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index e939fb074457d..2224a1f02a1a0 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -47,7 +47,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding public _serviceBrand: undefined; private queue: Queue; - private resource: URI = this.userDataProfilesService.defaultProfile.keybindingsResource; + private resource: URI = this.userDataProfilesService.currentProfile.keybindingsResource; constructor( @ITextModelService private readonly textModelResolverService: ITextModelService, diff --git a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts index f1a828d3057a3..702ff90b22cb5 100644 --- a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts @@ -78,7 +78,7 @@ suite('KeybindingsEditing', () => { teardown(() => disposables.clear()); test('errors cases - parse errors', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); + await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); try { await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape } }), 'alt+c', undefined); assert.fail('Should fail with parse errors'); @@ -88,7 +88,7 @@ suite('KeybindingsEditing', () => { }); test('errors cases - parse errors 2', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString('[{"key": }]')); + await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString('[{"key": }]')); try { await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape } }), 'alt+c', undefined); assert.fail('Should fail with parse errors'); @@ -105,7 +105,7 @@ suite('KeybindingsEditing', () => { }); test('errors cases - did not find an array', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString('{"key": "alt+c", "command": "hello"}')); + await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString('{"key": "alt+c", "command": "hello"}')); try { await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape } }), 'alt+c', undefined); assert.fail('Should fail'); @@ -115,7 +115,7 @@ suite('KeybindingsEditing', () => { }); test('edit a default keybinding to an empty file', async () => { - await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString('')); + await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString('')); const expected: IUserFriendlyKeybinding[] = [{ key: 'alt+c', command: 'a' }, { key: 'escape', command: '-a' }]; await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape }, command: 'a' }), 'alt+c', undefined); assert.deepStrictEqual(await getUserKeybindings(), expected); @@ -245,11 +245,11 @@ suite('KeybindingsEditing', () => { }); async function writeToKeybindingsFile(...keybindings: IUserFriendlyKeybinding[]): Promise { - await fileService.writeFile(userDataProfilesService.defaultProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify(keybindings || []))); + await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify(keybindings || []))); } async function getUserKeybindings(): Promise { - return json.parse((await fileService.readFile(userDataProfilesService.defaultProfile.keybindingsResource)).value.toString()); + return json.parse((await fileService.readFile(userDataProfilesService.currentProfile.keybindingsResource)).value.toString()); } function aResolvedKeybindingItem({ command, when, isDefault, firstPart, chordPart }: { command?: string; when?: string; isDefault?: boolean; firstPart?: { keyCode: KeyCode; modifiers?: Modifiers }; chordPart?: { keyCode: KeyCode; modifiers?: Modifiers } }): ResolvedKeybindingItem { diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index a99c536c48023..246eb4a822eba 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -95,7 +95,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic private readonly defaultSettingsRawResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/defaultSettings.json' }); get userSettingsResource(): URI { - return this.userDataProfilesService.defaultProfile.settingsResource; + return this.userDataProfilesService.currentProfile.settingsResource; } get workspaceSettingsResource(): URI | null { @@ -311,7 +311,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic options = { pinned: true, revealIfOpened: true, ...options }; if (textual) { const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + '\n[\n]'; - const editableKeybindings = this.userDataProfilesService.defaultProfile.keybindingsResource; + const editableKeybindings = this.userDataProfilesService.currentProfile.keybindingsResource; const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings'); // Create as needed and open in editor diff --git a/src/vs/workbench/services/profiles/common/settingsProfile.ts b/src/vs/workbench/services/profiles/common/settingsProfile.ts index d6e564c8216df..8bfd681b11496 100644 --- a/src/vs/workbench/services/profiles/common/settingsProfile.ts +++ b/src/vs/workbench/services/profiles/common/settingsProfile.ts @@ -29,7 +29,7 @@ export class SettingsProfile implements IResourceProfile { async getProfileContent(options?: ProfileCreationOptions): Promise { const ignoredSettings = this.getIgnoredSettings(); - const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfilesService.defaultProfile.settingsResource); + const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfilesService.currentProfile.settingsResource); const localContent = await this.getLocalFileContent(); let settingsProfileContent = updateIgnoredSettings(localContent || '{}', '{}', ignoredSettings, formattingOptions); if (options?.skipComments) { @@ -45,9 +45,9 @@ export class SettingsProfile implements IResourceProfile { const settingsContent: ISettingsContent = JSON.parse(content); this.logService.trace(`Profile: Applying settings...`); const localSettingsContent = await this.getLocalFileContent(); - const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfilesService.defaultProfile.settingsResource); + const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfilesService.currentProfile.settingsResource); const contentToUpdate = updateIgnoredSettings(settingsContent.settings, localSettingsContent || '{}', this.getIgnoredSettings(), formattingOptions); - await this.fileService.writeFile(this.userDataProfilesService.defaultProfile.settingsResource, VSBuffer.fromString(contentToUpdate)); + await this.fileService.writeFile(this.userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString(contentToUpdate)); this.logService.info(`Profile: Applied settings`); } @@ -59,7 +59,7 @@ export class SettingsProfile implements IResourceProfile { private async getLocalFileContent(): Promise { try { - const content = await this.fileService.readFile(this.userDataProfilesService.defaultProfile.settingsResource); + const content = await this.fileService.readFile(this.userDataProfilesService.currentProfile.settingsResource); return content.value.toString(); } catch (error) { return null; From 93ec6bd572c0530fe4133d1620b1eed8156e7307 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Sat, 21 May 2022 22:49:45 -0700 Subject: [PATCH 214/942] Screen reader should read quick pick title field if set (#149992) --- src/vs/base/parts/quickinput/browser/quickInput.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 49fb9f194559b..e7099f2655bed 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -979,7 +979,15 @@ class QuickPick extends QuickInput implements IQuickPi if (this.ui.inputBox.placeholder !== (this.placeholder || '')) { this.ui.inputBox.placeholder = (this.placeholder || ''); } - const ariaLabel = this.ariaLabel || this.placeholder || QuickPick.DEFAULT_ARIA_LABEL; + + let ariaLabel = this.ariaLabel; + if (!ariaLabel) { + ariaLabel = this.placeholder || QuickPick.DEFAULT_ARIA_LABEL; + // If we have a title, include it in the aria label. + if (this.title) { + ariaLabel += ` - ${this.title}`; + } + } if (this.ui.inputBox.ariaLabel !== ariaLabel) { this.ui.inputBox.ariaLabel = ariaLabel; } From 0ccc294e64d56398ad8e0ada4521039ba28b747a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 22 May 2022 09:22:48 +0200 Subject: [PATCH 215/942] move globalStorageHome to userDataProfileService --- .../sharedProcess/sharedProcessMain.ts | 6 +-- src/vs/code/electron-main/main.ts | 22 ++++---- src/vs/code/node/cliProcessMain.ts | 6 +-- .../common/configurationService.ts | 32 +++++++---- .../test/common/configurationService.test.ts | 53 ++++++++++--------- src/vs/platform/environment/common/argv.ts | 3 ++ .../environment/common/environment.ts | 1 - .../environment/common/environmentService.ts | 3 -- src/vs/platform/environment/node/argv.ts | 1 + .../node/extensionLifecycle.ts | 6 +-- .../electron-main/protocolMainService.ts | 4 +- .../electron-main/sharedProcess.ts | 3 ++ .../sharedProcess/node/sharedProcess.ts | 2 + .../state/electron-main/stateMainService.ts | 4 +- .../storage/electron-main/storageMain.ts | 5 +- .../electron-main/storageMainService.ts | 9 ++-- .../electron-sandbox/storageService.ts | 6 ++- .../electron-main/storageMainService.test.ts | 4 +- .../test/browser/fileUserDataProvider.test.ts | 2 +- .../userDataProfile/common/userDataProfile.ts | 29 ++++++---- .../test/common/userDataSyncClient.ts | 4 +- src/vs/platform/window/common/window.ts | 1 + .../electron-main/windowsMainService.ts | 3 ++ .../server/node/remoteAgentEnvironmentImpl.ts | 4 +- .../node/remoteExtensionHostAgentCli.ts | 4 +- src/vs/server/node/serverServices.ts | 8 ++- src/vs/workbench/browser/web.main.ts | 2 +- .../electron-sandbox/desktop.main.ts | 8 +-- .../configurationEditingService.test.ts | 2 +- .../test/browser/configurationService.test.ts | 18 +++---- .../environment/browser/environmentService.ts | 3 -- .../browser/webWorkerExtensionHost.ts | 4 +- .../common/extensionStorageMigration.ts | 4 +- .../localProcessExtensionHost.ts | 4 +- .../browser/extensionStorageMigration.test.ts | 20 +++---- .../test/browser/keybindingEditing.test.ts | 2 +- .../test/browser/workbenchTestServices.ts | 2 +- .../electron-browser/workbenchTestServices.ts | 2 +- 38 files changed, 176 insertions(+), 120 deletions(-) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index e237e90e941d6..3abecf7e57cdf 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -224,15 +224,15 @@ class SharedProcessMain extends Disposable { fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider); // User Data Profiles - const userDataProfilesService = this._register(new UserDataProfilesService(environmentService, logService)); + const userDataProfilesService = this._register(new UserDataProfilesService(this.configuration.profile, environmentService, logService)); services.set(IUserDataProfilesService, userDataProfilesService); // Configuration - const configurationService = this._register(new ConfigurationService(userDataProfilesService.currentProfile.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(userDataProfilesService, fileService)); services.set(IConfigurationService, configurationService); // Storage (global access only) - const storageService = new NativeStorageService(undefined, mainProcessService, environmentService); + const storageService = new NativeStorageService(undefined, mainProcessService, userDataProfilesService, environmentService); services.set(IStorageService, storageService); this._register(toDisposable(() => storageService.flush())); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 7b5bdb97e5435..943a5943535b3 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -90,13 +90,13 @@ class CodeMain { setUnexpectedErrorHandler(err => console.error(err)); // Create services - const [instantiationService, instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService] = this.createServices(); + const [instantiationService, instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService, userDataProfilesService] = this.createServices(); try { // Init services try { - await this.initServices(environmentMainService, configurationService, stateMainService); + await this.initServices(environmentMainService, userDataProfilesService, configurationService, stateMainService); } catch (error) { // Show a dialog for errors that can be resolved by the user @@ -138,7 +138,7 @@ class CodeMain { } } - private createServices(): [IInstantiationService, IProcessEnvironment, IEnvironmentMainService, ConfigurationService, StateMainService, BufferLogService, IProductService] { + private createServices(): [IInstantiationService, IProcessEnvironment, IEnvironmentMainService, ConfigurationService, StateMainService, BufferLogService, IProductService, IUserDataProfilesService] { const services = new ServiceCollection(); // Product @@ -146,7 +146,8 @@ class CodeMain { services.set(IProductService, productService); // Environment - const environmentMainService = new EnvironmentMainService(this.resolveArgs(), productService); + const args = this.resolveArgs(); + const environmentMainService = new EnvironmentMainService(args, productService); const instanceEnvironment = this.patchEnvironment(environmentMainService); // Patch `process.env` with the instance's environment services.set(IEnvironmentMainService, environmentMainService); @@ -168,18 +169,18 @@ class CodeMain { services.set(ILoggerService, new LoggerService(logService, fileService)); // User Data Profiles - const userDataProfilesService = new UserDataProfilesService(environmentMainService, logService); + const userDataProfilesService = new UserDataProfilesService(args['__profile'], environmentMainService, logService); services.set(IUserDataProfilesService, userDataProfilesService); // Configuration - const configurationService = new ConfigurationService(userDataProfilesService.currentProfile.settingsResource, fileService); + const configurationService = new ConfigurationService(userDataProfilesService, fileService); services.set(IConfigurationService, configurationService); // Lifecycle services.set(ILifecycleMainService, new SyncDescriptor(LifecycleMainService)); // State - const stateMainService = new StateMainService(environmentMainService, logService, fileService); + const stateMainService = new StateMainService(environmentMainService, userDataProfilesService, logService, fileService); services.set(IStateMainService, stateMainService); // Request @@ -197,7 +198,7 @@ class CodeMain { // Protocol services.set(IProtocolMainService, new SyncDescriptor(ProtocolMainService)); - return [new InstantiationService(services, true), instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService]; + return [new InstantiationService(services, true), instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService, userDataProfilesService]; } private patchEnvironment(environmentMainService: IEnvironmentMainService): IProcessEnvironment { @@ -217,7 +218,7 @@ class CodeMain { return instanceEnvironment; } - private initServices(environmentMainService: IEnvironmentMainService, configurationService: ConfigurationService, stateMainService: StateMainService): Promise { + private initServices(environmentMainService: IEnvironmentMainService, userDataProfilesService: IUserDataProfilesService, configurationService: ConfigurationService, stateMainService: StateMainService): Promise { return Promises.settled([ // Environment service (paths) @@ -225,7 +226,8 @@ class CodeMain { environmentMainService.extensionsPath, environmentMainService.codeCachePath, environmentMainService.logsPath, - environmentMainService.globalStorageHome.fsPath, + userDataProfilesService.defaultProfile.globalStorageHome.fsPath, + userDataProfilesService.currentProfile.globalStorageHome.fsPath, environmentMainService.workspaceStorageHome.fsPath, environmentMainService.localHistoryHome.fsPath, environmentMainService.backupHome diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index e18fbdab46e86..8a76844d1fb6b 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -131,11 +131,11 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, diskFileSystemProvider); // User Data Profiles - const userDataProfilesService = new UserDataProfilesService(environmentService, logService); + const userDataProfilesService = new UserDataProfilesService(undefined, environmentService, logService); services.set(IUserDataProfilesService, userDataProfilesService); // Configuration - const configurationService = this._register(new ConfigurationService(userDataProfilesService.currentProfile.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(userDataProfilesService, fileService)); services.set(IConfigurationService, configurationService); // Init config @@ -175,7 +175,7 @@ class CliMain extends Disposable { commonProperties: (async () => { let machineId: string | undefined = undefined; try { - const storageContents = await Promises.readFile(joinPath(environmentService.globalStorageHome, 'storage.json').fsPath); + const storageContents = await Promises.readFile(joinPath(userDataProfilesService.defaultProfile.globalStorageHome, 'storage.json').fsPath); machineId = JSON.parse(storageContents.toString())[machineIdKey]; } catch (error) { if (error.code !== 'ENOENT') { diff --git a/src/vs/platform/configuration/common/configurationService.ts b/src/vs/platform/configuration/common/configurationService.ts index 7860c72ea8e95..29a2f518b372f 100644 --- a/src/vs/platform/configuration/common/configurationService.ts +++ b/src/vs/platform/configuration/common/configurationService.ts @@ -5,7 +5,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ConfigurationTarget, IConfigurationChange, IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; @@ -13,34 +13,42 @@ import { Configuration, ConfigurationChangeEvent, ConfigurationModel, DefaultCon import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { IFileService } from 'vs/platform/files/common/files'; import { Registry } from 'vs/platform/registry/common/platform'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable { declare readonly _serviceBrand: undefined; private configuration: Configuration; - private userConfiguration: UserSettings; + private userConfiguration: MutableDisposable; private readonly reloadConfigurationScheduler: RunOnceScheduler; private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; constructor( - private readonly settingsResource: URI, - fileService: IFileService + private readonly userDataProfilesService: IUserDataProfilesService, + private readonly fileService: IFileService ) { super(); - this.userConfiguration = this._register(new UserSettings(this.settingsResource, undefined, extUriBiasedIgnorePathCase, fileService)); this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel()); + this.userConfiguration = this._register(new MutableDisposable()); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reloadConfiguration(), 50)); this._register(Registry.as(Extensions.Configuration).onDidUpdateConfiguration(({ properties }) => this.onDidDefaultConfigurationChange(properties))); - this._register(this.userConfiguration.onDidChange(() => this.reloadConfigurationScheduler.schedule())); } - async initialize(): Promise { - const userConfiguration = await this.userConfiguration.loadConfiguration(); - this.configuration = new Configuration(new DefaultConfigurationModel(), userConfiguration); + private initPromise: Promise | undefined; + initialize(settingsResource?: URI): Promise { + if (!this.initPromise) { + this.initPromise = (async () => { + this.userConfiguration.value = new UserSettings(settingsResource ?? this.userDataProfilesService.currentProfile.settingsResource, undefined, extUriBiasedIgnorePathCase, this.fileService); + this._register(this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule())); + const userConfiguration = await this.userConfiguration.value.loadConfiguration(); + this.configuration = new Configuration(new DefaultConfigurationModel(), userConfiguration); + })(); + } + return this.initPromise; } getConfigurationData(): IConfigurationData { @@ -79,8 +87,10 @@ export class ConfigurationService extends Disposable implements IConfigurationSe } async reloadConfiguration(): Promise { - const configurationModel = await this.userConfiguration.loadConfiguration(); - this.onDidChangeUserConfiguration(configurationModel); + if (this.userConfiguration.value) { + const configurationModel = await this.userConfiguration.value.loadConfiguration(); + this.onDidChangeUserConfiguration(configurationModel); + } } private onDidChangeUserConfiguration(userConfigurationModel: ConfigurationModel): void { diff --git a/src/vs/platform/configuration/test/common/configurationService.test.ts b/src/vs/platform/configuration/test/common/configurationService.test.ts index 559803b0588c8..bc560f07ab3df 100644 --- a/src/vs/platform/configuration/test/common/configurationService.test.ts +++ b/src/vs/platform/configuration/test/common/configurationService.test.ts @@ -12,31 +12,33 @@ import { URI } from 'vs/base/common/uri'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { NullLogService } from 'vs/platform/log/common/log'; import { Registry } from 'vs/platform/registry/common/platform'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; suite('ConfigurationService', () => { let fileService: IFileService; - let settingsResource: URI; + let userDataProfileService: IUserDataProfilesService; const disposables: DisposableStore = new DisposableStore(); setup(async () => { fileService = disposables.add(new FileService(new NullLogService())); const diskFileSystemProvider = disposables.add(new InMemoryFileSystemProvider()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - settingsResource = URI.file('settings.json'); + userDataProfileService = new UserDataProfilesService(undefined, { userRoamingDataHome: URI.file('User') }, new NullLogService()); }); teardown(() => disposables.clear()); test('simple', async () => { - await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); + const testObject = disposables.add(new ConfigurationService(userDataProfileService, fileService)); await testObject.initialize(); const config = testObject.getValue<{ foo: string; @@ -47,9 +49,9 @@ suite('ConfigurationService', () => { }); test('config gets flattened', async () => { - await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(userDataProfileService, fileService)); await testObject.initialize(); const config = testObject.getValue<{ testworkbench: { @@ -66,9 +68,9 @@ suite('ConfigurationService', () => { }); test('error case does not explode', async () => { - await fileService.writeFile(settingsResource, VSBuffer.fromString(',,,,')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString(',,,,')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(userDataProfileService, fileService)); await testObject.initialize(); const config = testObject.getValue<{ foo: string; @@ -78,7 +80,7 @@ suite('ConfigurationService', () => { }); test('missing file does not explode', async () => { - const testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService)); + const testObject = disposables.add(new ConfigurationService(userDataProfileService, fileService)); await testObject.initialize(); const config = testObject.getValue<{ foo: string }>(); @@ -87,21 +89,21 @@ suite('ConfigurationService', () => { }); test('trigger configuration change event when file does not exist', async () => { - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(userDataProfileService, fileService)); await testObject.initialize(); return new Promise((c, e) => { disposables.add(Event.filter(testObject.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(() => { assert.strictEqual(testObject.getValue('foo'), 'bar'); c(); })); - fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')).catch(e); + fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "foo": "bar" }')).catch(e); }); }); test('trigger configuration change event when file exists', async () => { - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); - await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); + const testObject = disposables.add(new ConfigurationService(userDataProfileService, fileService)); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); await testObject.initialize(); return new Promise((c) => { @@ -109,21 +111,21 @@ suite('ConfigurationService', () => { assert.strictEqual(testObject.getValue('foo'), 'barz'); c(); })); - fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "barz" }')); + fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "foo": "barz" }')); }); }); test('reloadConfiguration', async () => { - await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "foo": "bar" }')); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(userDataProfileService, fileService)); await testObject.initialize(); let config = testObject.getValue<{ foo: string; }>(); assert.ok(config); assert.strictEqual(config.foo, 'bar'); - await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "foo": "changed" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "foo": "changed" }')); // force a reload to get latest await testObject.reloadConfiguration(); @@ -155,22 +157,23 @@ suite('ConfigurationService', () => { } }); - let testObject = disposables.add(new ConfigurationService(URI.file('__testFile'), fileService)); + let testObject = disposables.add(new ConfigurationService(new UserDataProfilesService(undefined, { userRoamingDataHome: URI.file('User1') }, new NullLogService()), fileService)); await testObject.initialize(); let setting = testObject.getValue(); assert.ok(setting); assert.strictEqual(setting.configuration.service.testSetting, 'isSet'); - await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); - testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + testObject = disposables.add(new ConfigurationService(userDataProfileService, fileService)); + await testObject.initialize(); setting = testObject.getValue(); assert.ok(setting); assert.strictEqual(setting.configuration.service.testSetting, 'isSet'); - await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "configuration.service.testSetting": "isChanged" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configuration.service.testSetting": "isChanged" }')); await testObject.reloadConfiguration(); setting = testObject.getValue(); @@ -191,7 +194,7 @@ suite('ConfigurationService', () => { } }); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(userDataProfileService, fileService)); testObject.initialize(); let res = testObject.inspect('something.missing'); @@ -204,7 +207,7 @@ suite('ConfigurationService', () => { assert.strictEqual(res.value, 'isSet'); assert.strictEqual(res.userValue, undefined); - await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "lookup.service.testSetting": "bar" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "lookup.service.testSetting": "bar" }')); await testObject.reloadConfiguration(); res = testObject.inspect('lookup.service.testSetting'); @@ -226,7 +229,7 @@ suite('ConfigurationService', () => { } }); - const testObject = disposables.add(new ConfigurationService(settingsResource, fileService)); + const testObject = disposables.add(new ConfigurationService(userDataProfileService, fileService)); testObject.initialize(); let res = testObject.inspect('lookup.service.testNullSetting'); @@ -234,7 +237,7 @@ suite('ConfigurationService', () => { assert.strictEqual(res.value, null); assert.strictEqual(res.userValue, undefined); - await fileService.writeFile(settingsResource, VSBuffer.fromString('{ "lookup.service.testNullSetting": null }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "lookup.service.testNullSetting": null }')); await testObject.reloadConfiguration(); diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index b2b4500a61468..b37686cadb192 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -110,4 +110,7 @@ export interface NativeParsedArgs { // MS Build command line arg 'ms-enable-electron-run-as-node'?: boolean; + + // Internal + '__profile'?: string; } diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 52a16c3fa3f9e..bba16fd7d788e 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -54,7 +54,6 @@ export interface IEnvironmentService { // --- data paths untitledWorkspacesHome: URI; - globalStorageHome: URI; workspaceStorageHome: URI; localHistoryHome: URI; cacheHome: URI; diff --git a/src/vs/platform/environment/common/environmentService.ts b/src/vs/platform/environment/common/environmentService.ts index 2edc3238cdda5..21f8087efeaef 100644 --- a/src/vs/platform/environment/common/environmentService.ts +++ b/src/vs/platform/environment/common/environmentService.ts @@ -83,9 +83,6 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron @memoize get machineSettingsResource(): URI { return joinPath(URI.file(join(this.userDataPath, 'Machine')), 'settings.json'); } - @memoize - get globalStorageHome(): URI { return joinPath(this.appSettingsHome, 'globalStorage'); } - @memoize get workspaceStorageHome(): URI { return joinPath(this.appSettingsHome, 'workspaceStorage'); } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 062ce48a5808c..0748875195adc 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -149,6 +149,7 @@ export const OPTIONS: OptionDescriptions> = { 'vmodule': { type: 'string' }, '_urls': { type: 'string[]' }, 'disable-dev-shm-usage': { type: 'boolean' }, + '__profile': { type: 'string' }, _: { type: 'string[]' } // main arguments }; diff --git a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts index 5ebcea3b9665a..0933c2390f573 100644 --- a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts +++ b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts @@ -11,16 +11,16 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { join } from 'vs/base/common/path'; import { Promises } from 'vs/base/node/pfs'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ILogService } from 'vs/platform/log/common/log'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export class ExtensionsLifecycle extends Disposable { private processesLimiter: Limiter = new Limiter(5); // Run max 5 processes in parallel constructor( - @IEnvironmentService private environmentService: IEnvironmentService, + @IUserDataProfilesService private userDataProfilesService: IUserDataProfilesService, @ILogService private readonly logService: ILogService ) { super(); @@ -130,6 +130,6 @@ export class ExtensionsLifecycle extends Disposable { } private getExtensionStoragePath(extension: ILocalExtension): string { - return join(this.environmentService.globalStorageHome.fsPath, extension.identifier.id.toLowerCase()); + return join(this.userDataProfilesService.defaultProfile.globalStorageHome.fsPath, extension.identifier.id.toLowerCase()); } } diff --git a/src/vs/platform/protocol/electron-main/protocolMainService.ts b/src/vs/platform/protocol/electron-main/protocolMainService.ts index e414b377c6ed0..9ac374962f64c 100644 --- a/src/vs/platform/protocol/electron-main/protocolMainService.ts +++ b/src/vs/platform/protocol/electron-main/protocolMainService.ts @@ -15,6 +15,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; import { IIPCObjectUrl, IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; type ProtocolCallback = { (result: string | Electron.FilePathWithHeaders | { error: number }): void }; @@ -27,6 +28,7 @@ export class ProtocolMainService extends Disposable implements IProtocolMainServ constructor( @INativeEnvironmentService environmentService: INativeEnvironmentService, + @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, @ILogService private readonly logService: ILogService ) { super(); @@ -37,7 +39,7 @@ export class ProtocolMainService extends Disposable implements IProtocolMainServ // - storage : all files in global and workspace storage (https://github.com/microsoft/vscode/issues/116735) this.addValidFileRoot(environmentService.appRoot); this.addValidFileRoot(environmentService.extensionsPath); - this.addValidFileRoot(environmentService.globalStorageHome.fsPath); + this.addValidFileRoot(userDataProfilesService.currentProfile.globalStorageHome.fsPath); this.addValidFileRoot(environmentService.workspaceStorageHome.fsPath); // Handle protocols diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index f7046a4d7565e..78508f934977f 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -22,6 +22,7 @@ import { ISharedProcessWorkerConfiguration } from 'vs/platform/sharedProcess/com import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { WindowError } from 'vs/platform/window/electron-main/window'; import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export class SharedProcess extends Disposable implements ISharedProcess { @@ -37,6 +38,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { private readonly machineId: string, private userEnv: IProcessEnvironment, @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @ILogService private readonly logService: ILogService, @IThemeMainService private readonly themeMainService: IThemeMainService, @@ -239,6 +241,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { appRoot: this.environmentMainService.appRoot, codeCachePath: this.environmentMainService.codeCachePath, backupWorkspacesPath: this.environmentMainService.backupWorkspacesPath, + profile: this.userDataProfilesService.currentProfile.name, userEnv: this.userEnv, args: this.environmentMainService.args, logLevel: this.logService.getLevel(), diff --git a/src/vs/platform/sharedProcess/node/sharedProcess.ts b/src/vs/platform/sharedProcess/node/sharedProcess.ts index 70c028b7a116d..7b9b45970fcbf 100644 --- a/src/vs/platform/sharedProcess/node/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/node/sharedProcess.ts @@ -24,4 +24,6 @@ export interface ISharedProcessConfiguration extends ISandboxConfiguration { readonly logLevel: LogLevel; readonly backupWorkspacesPath: string; + + readonly profile?: string; } diff --git a/src/vs/platform/state/electron-main/stateMainService.ts b/src/vs/platform/state/electron-main/stateMainService.ts index 3c9e0d74eb37f..5d4305c5a378f 100644 --- a/src/vs/platform/state/electron-main/stateMainService.ts +++ b/src/vs/platform/state/electron-main/stateMainService.ts @@ -14,6 +14,7 @@ import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/e import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { IStateMainService } from 'vs/platform/state/electron-main/state'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; type StorageDatabase = { [key: string]: unknown }; @@ -154,12 +155,13 @@ export class StateMainService implements IStateMainService { private static readonly STATE_FILE = 'storage.json'; private readonly legacyStateFilePath = URI.file(join(this.environmentMainService.userDataPath, StateMainService.STATE_FILE)); - private readonly stateFilePath = joinPath(this.environmentMainService.globalStorageHome, StateMainService.STATE_FILE); + private readonly stateFilePath = joinPath(this.userDataProfilesService.defaultProfile.globalStorageHome, StateMainService.STATE_FILE); private readonly fileStorage: FileStorage; constructor( @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @ILogService private readonly logService: ILogService, @IFileService fileService: IFileService ) { diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 384deb88135c3..35dec15b20be6 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -18,6 +18,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; import { currentSessionDateStorageKey, firstSessionDateStorageKey, ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export interface IStorageMainOptions { @@ -281,7 +282,7 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { get path(): string | undefined { if (!this.options.useInMemoryStorage) { - return join(this.environmentService.globalStorageHome.fsPath, GlobalStorageMain.STORAGE_NAME); + return join(this.userDataProfilesService.currentProfile.globalStorageHome.fsPath, GlobalStorageMain.STORAGE_NAME); } return undefined; @@ -290,7 +291,7 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { constructor( private readonly options: IStorageMainOptions, logService: ILogService, - private readonly environmentService: IEnvironmentService, + private readonly userDataProfilesService: IUserDataProfilesService, fileService: IFileService, telemetryService: ITelemetryService ) { diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index aee6d47d75803..cae7ae40a33f8 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -7,7 +7,6 @@ import { once } from 'vs/base/common/functional'; import { Disposable } from 'vs/base/common/lifecycle'; import { IStorage } from 'vs/base/parts/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { IFileService } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleMainService, LifecycleMainPhase, ShutdownReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; @@ -15,6 +14,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { AbstractStorageService, IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { GlobalStorageMain, InMemoryStorageMain, IStorageMain, IStorageMainOptions, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; //#region Storage Main Service (intent: make global and workspace storage accessible to windows from main process) @@ -51,6 +51,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic constructor( @ILogService private readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IFileService private readonly fileService: IFileService, @ITelemetryService private readonly telemetryService: ITelemetryService @@ -106,7 +107,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic private createGlobalStorage(): IStorageMain { this.logService.trace(`StorageMainService: creating global storage`); - const globalStorage = new GlobalStorageMain(this.getStorageOptions(), this.logService, this.environmentService, this.fileService, this.telemetryService); + const globalStorage = new GlobalStorageMain(this.getStorageOptions(), this.logService, this.userDataProfilesService, this.fileService, this.telemetryService); once(globalStorage.onDidCloseStorage)(() => { this.logService.trace(`StorageMainService: closed global storage`); @@ -209,7 +210,7 @@ export class GlobalStorageMainService extends AbstractStorageService implements readonly whenReady = this.storageMainService.globalStorage.whenInit; constructor( - @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IStorageMainService private readonly storageMainService: IStorageMainService ) { super(); @@ -233,7 +234,7 @@ export class GlobalStorageMainService extends AbstractStorageService implements } protected getLogDetails(scope: StorageScope): string | undefined { - return scope === StorageScope.GLOBAL ? this.environmentMainService.globalStorageHome.fsPath : undefined; + return scope === StorageScope.GLOBAL ? this.userDataProfilesService.currentProfile.globalStorageHome.fsPath : undefined; } protected override shouldFlushWhenIdle(): boolean { diff --git a/src/vs/platform/storage/electron-sandbox/storageService.ts b/src/vs/platform/storage/electron-sandbox/storageService.ts index 2eea47208b35e..95a0a110a9abb 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService.ts @@ -11,6 +11,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { AbstractStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { StorageDatabaseChannelClient } from 'vs/platform/storage/common/storageIpc'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class NativeStorageService extends AbstractStorageService { @@ -27,7 +28,8 @@ export class NativeStorageService extends AbstractStorageService { constructor( workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined, private readonly mainProcessService: IMainProcessService, - private readonly environmentService: IEnvironmentService + private readonly userDataProfilesService: IUserDataProfilesService, + private readonly environmentService: IEnvironmentService, ) { super(); @@ -79,7 +81,7 @@ export class NativeStorageService extends AbstractStorageService { } protected getLogDetails(scope: StorageScope): string | undefined { - return scope === StorageScope.GLOBAL ? this.environmentService.globalStorageHome.fsPath : this.workspaceStorageId ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspaceStorageId, 'state.vscdb').fsPath}` : undefined; + return scope === StorageScope.GLOBAL ? this.userDataProfilesService.currentProfile.globalStorageHome.fsPath : this.workspaceStorageId ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspaceStorageId, 'state.vscdb').fsPath}` : undefined; } async close(): Promise { diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 60b6b259eaad4..1ff463569a6c5 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -20,6 +20,7 @@ import { IStorageChangeEvent, IStorageMain, IStorageMainOptions } from 'vs/platf import { StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { currentSessionDateStorageKey, firstSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ICodeWindow, UnloadReason } from 'vs/platform/window/electron-main/window'; suite('StorageMainService', function () { @@ -126,7 +127,8 @@ suite('StorageMainService', function () { } function createStorageService(lifecycleMainService: ILifecycleMainService = new StorageTestLifecycleMainService()): TestStorageMainService { - return new TestStorageMainService(new NullLogService(), new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService), lifecycleMainService, new FileService(new NullLogService()), NullTelemetryService); + const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService); + return new TestStorageMainService(new NullLogService(), environmentService, new UserDataProfilesService(undefined, environmentService, new NullLogService()), lifecycleMainService, new FileService(new NullLogService()), NullTelemetryService); } test('basics (global)', function () { diff --git a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts index 5b281b68e3721..f76714a1fca79 100644 --- a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts +++ b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts @@ -52,7 +52,7 @@ suite('FileUserDataProvider', () => { await testObject.createFolder(backupWorkspaceHomeOnDisk); environmentService = new TestEnvironmentService(userDataHomeOnDisk); - userDataProfilesService = new UserDataProfilesService(environmentService, logService); + userDataProfilesService = new UserDataProfilesService(undefined, environmentService, logService); fileUserDataProvider = new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService); disposables.add(fileUserDataProvider); diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index 0c12c065a8d35..18634094d25fa 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -12,7 +12,9 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { ILogService } from 'vs/platform/log/common/log'; export interface IUserDataProfile { + readonly name: string | undefined; readonly location: URI; + readonly globalStorageHome: URI; readonly settingsResource: URI; readonly keybindingsResource: URI; readonly tasksResource: URI; @@ -42,18 +44,27 @@ export class UserDataProfilesService extends Disposable implements IUserDataProf readonly onDidChangeCurrentProfile = this._onDidChangeCurrentProfile.event; constructor( - @IEnvironmentService environmentService: IEnvironmentService, + profile: string | undefined, + @IEnvironmentService private readonly environmentService: IEnvironmentService, @ILogService logService: ILogService ) { super(); - const defaultProfileLocation = environmentService.userRoamingDataHome; - this._currentProfile = this.defaultProfile = { - location: defaultProfileLocation, - settingsResource: joinPath(defaultProfileLocation, 'settings.json'), - keybindingsResource: joinPath(defaultProfileLocation, 'keybindings.json'), - tasksResource: joinPath(defaultProfileLocation, 'tasks.json'), - snippetsHome: joinPath(defaultProfileLocation, 'snippets'), - extensionsResource: undefined + this.defaultProfile = this.createProfile(undefined); + this._currentProfile = profile ? this.createProfile(profile) : this.defaultProfile; + } + + private createProfile(name: string | undefined): IUserDataProfile { + const location = name ? joinPath(this.environmentService.userRoamingDataHome, 'profiles', name) : this.environmentService.userRoamingDataHome; + return { + name, + location: location, + globalStorageHome: joinPath(location, 'globalStorage'), + settingsResource: joinPath(location, 'settings.json'), + keybindingsResource: joinPath(location, 'keybindings.json'), + tasksResource: joinPath(location, 'tasks.json'), + snippetsHome: joinPath(location, 'snippets'), + extensionsResource: name ? joinPath(location, 'extensions') : undefined }; } + } diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 0d335cb51d8dd..f61a75daf83cc 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -66,7 +66,7 @@ export class UserDataSyncClient extends Disposable { sync: 'on', }); - const userDataProfilesService = this.instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, logService)); + const userDataProfilesService = this.instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, logService)); this.instantiationService.stub(IProductService, { _serviceBrand: undefined, ...product, ...{ @@ -86,7 +86,7 @@ export class UserDataSyncClient extends Disposable { this.instantiationService.stub(IStorageService, this._register(new InMemoryStorageService())); - const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(userDataProfilesService, fileService)); await configurationService.initialize(); this.instantiationService.stub(IConfigurationService, configurationService); this.instantiationService.stub(IUriIdentityService, this.instantiationService.createInstance(UriIdentityService)); diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index 2c9591a2beca1..ca963cadff92a 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -266,6 +266,7 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native execPath: string; backupPath?: string; + profile?: string; homeDir: string; tmpDir: string; userDataDir: string; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index ec6d18489560b..00d9a41019101 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -51,6 +51,7 @@ import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electro import { ICodeWindow, UnloadReason } from 'vs/platform/window/electron-main/window'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; //#region Helper Interfaces @@ -193,6 +194,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic @ILogService private readonly logService: ILogService, @IStateMainService private readonly stateMainService: IStateMainService, @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IBackupMainService private readonly backupMainService: IBackupMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -1296,6 +1298,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // loading the window. backupPath: options.emptyWindowBackupInfo ? join(this.environmentMainService.backupHome, options.emptyWindowBackupInfo.backupFolder) : undefined, + profile: this.userDataProfilesService.currentProfile.name, homeDir: this.environmentMainService.userHome.fsPath, tmpDir: this.environmentMainService.tmpDir.fsPath, userDataDir: this.environmentMainService.userDataPath, diff --git a/src/vs/server/node/remoteAgentEnvironmentImpl.ts b/src/vs/server/node/remoteAgentEnvironmentImpl.ts index dd7ebc3daa62e..eb7008ea0af1b 100644 --- a/src/vs/server/node/remoteAgentEnvironmentImpl.ts +++ b/src/vs/server/node/remoteAgentEnvironmentImpl.ts @@ -27,6 +27,7 @@ import { cwd } from 'vs/base/common/process'; import { ServerConnectionToken, ServerConnectionTokenType } from 'vs/server/node/serverConnectionToken'; import { IExtensionHostStatusService } from 'vs/server/node/extensionHostStatusService'; import { IExtensionsScannerService, toExtensionDescription } from 'vs/platform/extensionManagement/common/extensionsScannerService'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export class RemoteAgentEnvironmentChannel implements IServerChannel { @@ -37,6 +38,7 @@ export class RemoteAgentEnvironmentChannel implements IServerChannel { constructor( private readonly _connectionToken: ServerConnectionToken, private readonly _environmentService: IServerEnvironmentService, + private readonly _userDataProfilesService: IUserDataProfilesService, extensionManagementCLIService: IExtensionManagementCLIService, private readonly _logService: ILogService, private readonly _extensionHostStatusService: IExtensionHostStatusService, @@ -290,7 +292,7 @@ export class RemoteAgentEnvironmentChannel implements IServerChannel { logsPath: URI.file(this._environmentService.logsPath), extensionsPath: URI.file(this._environmentService.extensionsPath!), extensionHostLogsPath: URI.file(join(this._environmentService.logsPath, `exthost${RemoteAgentEnvironmentChannel._namePool++}`)), - globalStorageHome: this._environmentService.globalStorageHome, + globalStorageHome: this._userDataProfilesService.defaultProfile.globalStorageHome, workspaceStorageHome: this._environmentService.workspaceStorageHome, localHistoryHome: this._environmentService.localHistoryHome, userHome: this._environmentService.userHome, diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index ba99f886efad3..79e341bb9bfd0 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -93,11 +93,11 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, this._register(new DiskFileSystemProvider(logService))); // User Data Profiles - const userDataProfilesService = this._register(new UserDataProfilesService(environmentService, logService)); + const userDataProfilesService = this._register(new UserDataProfilesService(undefined, environmentService, logService)); services.set(IUserDataProfilesService, userDataProfilesService); // Configuration - const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService)); + const configurationService = this._register(new ConfigurationService(userDataProfilesService, fileService)); await configurationService.initialize(); services.set(IConfigurationService, configurationService); diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 5fa2e40541c3b..406e51d6cdbcc 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -71,6 +71,7 @@ import { ExtensionHostStatusService, IExtensionHostStatusService } from 'vs/serv import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService'; import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; +import { UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; const eventPrefix = 'monacoworkbench'; @@ -108,8 +109,11 @@ export async function setupServerServices(connectionToken: ServerConnectionToken services.set(IFileService, fileService); fileService.registerProvider(Schemas.file, disposables.add(new DiskFileSystemProvider(logService))); - const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService); + // Configuration + const userDataProfilesService = new UserDataProfilesService(undefined, environmentService, logService); + const configurationService = new ConfigurationService(userDataProfilesService, fileService); services.set(IConfigurationService, configurationService); + configurationService.initialize(environmentService.machineSettingsResource); const extensionHostStatusService = new ExtensionHostStatusService(); services.set(IExtensionHostStatusService, extensionHostStatusService); @@ -182,7 +186,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken instantiationService.invokeFunction(accessor => { const extensionManagementService = accessor.get(IExtensionManagementService); const extensionsScannerService = accessor.get(IExtensionsScannerService); - const remoteExtensionEnvironmentChannel = new RemoteAgentEnvironmentChannel(connectionToken, environmentService, extensionManagementCLIService, logService, extensionHostStatusService, extensionsScannerService); + const remoteExtensionEnvironmentChannel = new RemoteAgentEnvironmentChannel(connectionToken, environmentService, userDataProfilesService, extensionManagementCLIService, logService, extensionHostStatusService, extensionsScannerService); socketServer.registerChannel('remoteextensionsenvironment', remoteExtensionEnvironmentChannel); const telemetryChannel = new ServerTelemetryChannel(accessor.get(IServerTelemetryService), appInsightsAppender); diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 917fe072d5f11..b7fd242063f49 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -204,7 +204,7 @@ export class BrowserMain extends Disposable { serviceCollection.set(ILogService, logService); // User Data Profiles - const userDataProfilesService = new UserDataProfilesService(environmentService, logService); + const userDataProfilesService = new UserDataProfilesService(undefined, environmentService, logService); serviceCollection.set(IUserDataProfilesService, userDataProfilesService); // Remote diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 3531cfd21fae5..78ae758c31b84 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -180,7 +180,7 @@ export class DesktopMain extends Disposable { } // User Data Profiles - const userDataProfilesService = new UserDataProfilesService(environmentService, logService); + const userDataProfilesService = new UserDataProfilesService(this.configuration.profile, environmentService, logService); serviceCollection.set(IUserDataProfilesService, userDataProfilesService); // Shared Process @@ -263,7 +263,7 @@ export class DesktopMain extends Disposable { return service; }), - this.createStorageService(payload, environmentService, mainProcessService).then(service => { + this.createStorageService(payload, environmentService, userDataProfilesService, mainProcessService).then(service => { // Storage serviceCollection.set(IStorageService, service); @@ -345,8 +345,8 @@ export class DesktopMain extends Disposable { } } - private async createStorageService(payload: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, mainProcessService: IMainProcessService): Promise { - const storageService = new NativeStorageService(payload, mainProcessService, environmentService); + private async createStorageService(payload: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService, mainProcessService: IMainProcessService): Promise { + const storageService = new NativeStorageService(payload, mainProcessService, userDataProfilesService, environmentService); try { await storageService.initialize(); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts index b39a2901a883d..b326d3902e2a4 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts @@ -95,7 +95,7 @@ suite('ConfigurationEditingService', () => { instantiationService = workbenchInstantiationService(undefined, disposables); environmentService = TestEnvironmentService; instantiationService.stub(IEnvironmentService, environmentService); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, logService)); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, logService)); const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null)); disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService)))); instantiationService.stub(IFileService, fileService); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index a1cd6a67918c7..25d0421b21caa 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -78,7 +78,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(environmentService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, environmentService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); await (testObject).initialize(convertToWorkspacePayload(folder)); }); @@ -118,7 +118,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(environmentService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, environmentService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); await (testObject).initialize(convertToWorkspacePayload(folder)); const actual = testObject.getWorkspaceFolder(joinPath(folder, 'a')); @@ -138,7 +138,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(environmentService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, environmentService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); await (testObject).initialize(convertToWorkspacePayload(folder)); @@ -185,7 +185,7 @@ suite('WorkspaceContextService - Workspace', () => { const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null)); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(environmentService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, environmentService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -243,7 +243,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(environmentService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, environmentService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); @@ -485,7 +485,7 @@ suite('WorkspaceService - Initialization', () => { environmentService = TestEnvironmentService; const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, logService)); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, logService)); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IFileService, fileService); @@ -736,7 +736,7 @@ suite('WorkspaceConfigurationService - Folder', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, logService)); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, logService)); workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); @@ -1408,7 +1408,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, logService)); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, logService)); const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IFileService, fileService); @@ -2070,7 +2070,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve(), needsCaching: () => false }; - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, logService)); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, logService)); testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index ae666036856a4..e2d3658fa8c8d 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -61,9 +61,6 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi @memoize get cacheHome(): URI { return joinPath(this.userRoamingDataHome, 'caches'); } - @memoize - get globalStorageHome(): URI { return joinPath(this.userRoamingDataHome, 'globalStorage'); } - @memoize get workspaceStorageHome(): URI { return joinPath(this.userRoamingDataHome, 'workspaceStorage'); } diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index 31b1091efce43..b032d80a41573 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -30,6 +30,7 @@ import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { FileAccess } from 'vs/base/common/network'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { parentOriginHash } from 'vs/workbench/browser/webview'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export interface IWebWorkerExtensionHostInitData { readonly autoStart: boolean; @@ -66,6 +67,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost @ILabelService private readonly _labelService: ILabelService, @ILogService private readonly _logService: ILogService, @IBrowserWorkbenchEnvironmentService private readonly _environmentService: IBrowserWorkbenchEnvironmentService, + @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService, @IProductService private readonly _productService: IProductService, @ILayoutService private readonly _layoutService: ILayoutService, @IStorageService private readonly _storageService: IStorageService, @@ -281,7 +283,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, - globalStorageHome: this._environmentService.globalStorageHome, + globalStorageHome: this._userDataProfilesService.currentProfile.globalStorageHome, workspaceStorageHome: this._environmentService.workspaceStorageHome, }, workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { diff --git a/src/vs/workbench/services/extensions/common/extensionStorageMigration.ts b/src/vs/workbench/services/extensions/common/extensionStorageMigration.ts index 2e5a0bb6e4a11..f3ff6531f705d 100644 --- a/src/vs/workbench/services/extensions/common/extensionStorageMigration.ts +++ b/src/vs/workbench/services/extensions/common/extensionStorageMigration.ts @@ -12,6 +12,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; /** @@ -22,6 +23,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace export async function migrateExtensionStorage(fromExtensionId: string, toExtensionId: string, global: boolean, instantionService: IInstantiationService): Promise { return instantionService.invokeFunction(async serviceAccessor => { const environmentService = serviceAccessor.get(IEnvironmentService); + const userDataProfilesService = serviceAccessor.get(IUserDataProfilesService); const extensionStorageService = serviceAccessor.get(IExtensionStorageService); const storageService = serviceAccessor.get(IStorageService); const uriIdentityService = serviceAccessor.get(IUriIdentityService); @@ -37,7 +39,7 @@ export async function migrateExtensionStorage(fromExtensionId: string, toExtensi const getExtensionStorageLocation = (extensionId: string, global: boolean): URI => { if (global) { - return uriIdentityService.extUri.joinPath(environmentService.globalStorageHome, extensionId.toLowerCase() /* Extension id is lower cased for global storage */); + return uriIdentityService.extUri.joinPath(userDataProfilesService.defaultProfile.globalStorageHome, extensionId.toLowerCase() /* Extension id is lower cased for global storage */); } return uriIdentityService.extUri.joinPath(environmentService.workspaceStorageHome, workspaceContextService.getWorkspace().id, extensionId); }; diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index 0f7f60bbc7de9..e648c62202f3e 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -44,6 +44,7 @@ import { SerializedError } from 'vs/base/common/errors'; import { removeDangerousEnvVariables } from 'vs/base/common/processes'; import { StopWatch } from 'vs/base/common/stopwatch'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; export interface ILocalProcessExtensionHostInitData { readonly autoStart: boolean; @@ -143,6 +144,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { @INativeHostService private readonly _nativeHostService: INativeHostService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, @INativeWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService, + @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @ILogService private readonly _logService: ILogService, @ILabelService private readonly _labelService: ILabelService, @@ -512,7 +514,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, - globalStorageHome: this._environmentService.globalStorageHome, + globalStorageHome: this._userDataProfilesService.currentProfile.globalStorageHome, workspaceStorageHome: this._environmentService.workspaceStorageHome, }, workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { diff --git a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts index 47167afb1a89e..39466b0209b4c 100644 --- a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts +++ b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts @@ -19,12 +19,13 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { migrateExtensionStorage } from 'vs/workbench/services/extensions/common/extensionStorageMigration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; suite('ExtensionStorageMigration', () => { const disposables = new DisposableStore(); const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' }); - const globalStorageHome = joinPath(ROOT, 'globalStorageHome'), workspaceStorageHome = joinPath(ROOT, 'workspaceStorageHome'); + const workspaceStorageHome = joinPath(ROOT, 'workspaceStorageHome'); let instantiationService: TestInstantiationService; @@ -34,7 +35,8 @@ suite('ExtensionStorageMigration', () => { const fileService = disposables.add(new FileService(new NullLogService())); fileService.registerProvider(ROOT.scheme, disposables.add(new InMemoryFileSystemProvider())); instantiationService.stub(IFileService, fileService); - instantiationService.stub(IEnvironmentService, >{ globalStorageHome, workspaceStorageHome }); + const environmentService = instantiationService.stub(IEnvironmentService, >{ userRoamingDataHome: ROOT, workspaceStorageHome }); + instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, new NullLogService())); instantiationService.stub(IExtensionStorageService, instantiationService.createInstance(ExtensionStorageService)); }); @@ -43,11 +45,11 @@ suite('ExtensionStorageMigration', () => { test('migrate extension storage', async () => { const fromExtensionId = 'pub.from', toExtensionId = 'pub.to', storageMigratedKey = `extensionStorage.migrate.${fromExtensionId}-${toExtensionId}`; - const extensionStorageService = instantiationService.get(IExtensionStorageService), fileService = instantiationService.get(IFileService), storageService = instantiationService.get(IStorageService); + const extensionStorageService = instantiationService.get(IExtensionStorageService), fileService = instantiationService.get(IFileService), storageService = instantiationService.get(IStorageService), userDataProfilesService = instantiationService.get(IUserDataProfilesService); extensionStorageService.setExtensionState(fromExtensionId, { globalKey: 'hello global state' }, true); extensionStorageService.setExtensionState(fromExtensionId, { workspaceKey: 'hello workspace state' }, false); - await fileService.writeFile(joinPath(globalStorageHome, fromExtensionId), VSBuffer.fromString('hello global storage')); + await fileService.writeFile(joinPath(userDataProfilesService.defaultProfile.globalStorageHome, fromExtensionId), VSBuffer.fromString('hello global storage')); await fileService.writeFile(joinPath(workspaceStorageHome, TestWorkspace.id, fromExtensionId), VSBuffer.fromString('hello workspace storage')); await migrateExtensionStorage(fromExtensionId, toExtensionId, true, instantiationService); @@ -55,12 +57,12 @@ suite('ExtensionStorageMigration', () => { assert.deepStrictEqual(extensionStorageService.getExtensionState(fromExtensionId, true), undefined); assert.deepStrictEqual(extensionStorageService.getExtensionState(fromExtensionId, false), undefined); - assert.deepStrictEqual((await fileService.exists(joinPath(globalStorageHome, fromExtensionId))), false); + assert.deepStrictEqual((await fileService.exists(joinPath(userDataProfilesService.defaultProfile.globalStorageHome, fromExtensionId))), false); assert.deepStrictEqual((await fileService.exists(joinPath(workspaceStorageHome, TestWorkspace.id, fromExtensionId))), false); assert.deepStrictEqual(extensionStorageService.getExtensionState(toExtensionId, true), { globalKey: 'hello global state' }); assert.deepStrictEqual(extensionStorageService.getExtensionState(toExtensionId, false), { workspaceKey: 'hello workspace state' }); - assert.deepStrictEqual((await fileService.readFile(joinPath(globalStorageHome, toExtensionId))).value.toString(), 'hello global storage'); + assert.deepStrictEqual((await fileService.readFile(joinPath(userDataProfilesService.defaultProfile.globalStorageHome, toExtensionId))).value.toString(), 'hello global storage'); assert.deepStrictEqual((await fileService.readFile(joinPath(workspaceStorageHome, TestWorkspace.id, toExtensionId))).value.toString(), 'hello workspace storage'); assert.deepStrictEqual(storageService.get(storageMigratedKey, StorageScope.GLOBAL), 'true'); @@ -70,19 +72,19 @@ suite('ExtensionStorageMigration', () => { test('migrate extension storage when does not exist', async () => { const fromExtensionId = 'pub.from', toExtensionId = 'pub.to', storageMigratedKey = `extensionStorage.migrate.${fromExtensionId}-${toExtensionId}`; - const extensionStorageService = instantiationService.get(IExtensionStorageService), fileService = instantiationService.get(IFileService), storageService = instantiationService.get(IStorageService); + const extensionStorageService = instantiationService.get(IExtensionStorageService), fileService = instantiationService.get(IFileService), storageService = instantiationService.get(IStorageService), userDataProfilesService = instantiationService.get(IUserDataProfilesService); await migrateExtensionStorage(fromExtensionId, toExtensionId, true, instantiationService); await migrateExtensionStorage(fromExtensionId, toExtensionId, false, instantiationService); assert.deepStrictEqual(extensionStorageService.getExtensionState(fromExtensionId, true), undefined); assert.deepStrictEqual(extensionStorageService.getExtensionState(fromExtensionId, false), undefined); - assert.deepStrictEqual((await fileService.exists(joinPath(globalStorageHome, fromExtensionId))), false); + assert.deepStrictEqual((await fileService.exists(joinPath(userDataProfilesService.defaultProfile.globalStorageHome, fromExtensionId))), false); assert.deepStrictEqual((await fileService.exists(joinPath(workspaceStorageHome, TestWorkspace.id, fromExtensionId))), false); assert.deepStrictEqual(extensionStorageService.getExtensionState(toExtensionId, true), undefined); assert.deepStrictEqual(extensionStorageService.getExtensionState(toExtensionId, false), undefined); - assert.deepStrictEqual((await fileService.exists(joinPath(globalStorageHome, toExtensionId))), false); + assert.deepStrictEqual((await fileService.exists(joinPath(userDataProfilesService.defaultProfile.globalStorageHome, toExtensionId))), false); assert.deepStrictEqual((await fileService.exists(joinPath(workspaceStorageHome, TestWorkspace.id, toExtensionId))), false); assert.deepStrictEqual(storageService.get(storageMigratedKey, StorageScope.GLOBAL), 'true'); diff --git a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts index 702ff90b22cb5..c0cb23b6e9c9a 100644 --- a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts @@ -64,7 +64,7 @@ suite('KeybindingsEditing', () => { const configService = new TestConfigurationService(); configService.setUserConfiguration('files', { 'eol': '\n' }); - userDataProfilesService = new UserDataProfilesService(environmentService, logService); + userDataProfilesService = new UserDataProfilesService(undefined, environmentService, logService); instantiationService = workbenchInstantiationService({ fileService: () => fileService, diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 1e70814a6bdce..5eb84a0315258 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -243,7 +243,7 @@ export function workbenchInstantiationService( const environmentService = overrides?.environmentService ? overrides.environmentService(instantiationService) : TestEnvironmentService; instantiationService.stub(IEnvironmentService, environmentService); instantiationService.stub(IWorkbenchEnvironmentService, environmentService); - instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, new NullLogService())); + instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, new NullLogService())); const contextKeyService = overrides?.contextKeyService ? overrides.contextKeyService(instantiationService) : instantiationService.createInstance(MockContextKeyService); instantiationService.stub(IContextKeyService, contextKeyService); instantiationService.stub(IProgressService, new TestProgressService()); diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 191a3829c20cb..a0988c023e99b 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -270,7 +270,7 @@ export function workbenchInstantiationService(disposables = new DisposableStore( instantiationService.stub(INativeEnvironmentService, TestEnvironmentService); instantiationService.stub(IWorkbenchEnvironmentService, TestEnvironmentService); instantiationService.stub(INativeWorkbenchEnvironmentService, TestEnvironmentService); - instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(TestEnvironmentService, new NullLogService())); + instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, TestEnvironmentService, new NullLogService())); return instantiationService; } From aacb387ef1c926af69224ea45b1c2ccce899d4db Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 19 May 2022 17:08:42 +0200 Subject: [PATCH 216/942] implement diagnostic pull --- .../server/src/cssServer.ts | 67 ++++------- .../server/src/utils/validation.ts | 109 ++++++++++++++++++ .../server/src/utils/validation.ts | 72 ++++++++++++ .../server/src/jsonServer.ts | 81 ++++--------- .../server/src/utils/validation.ts | 109 ++++++++++++++++++ 5 files changed, 335 insertions(+), 103 deletions(-) create mode 100644 extensions/css-language-features/server/src/utils/validation.ts create mode 100644 extensions/html-language-features/server/src/utils/validation.ts create mode 100644 extensions/json-language-features/server/src/utils/validation.ts diff --git a/extensions/css-language-features/server/src/cssServer.ts b/extensions/css-language-features/server/src/cssServer.ts index 3eed39628793d..b4ce51c162a81 100644 --- a/extensions/css-language-features/server/src/cssServer.ts +++ b/extensions/css-language-features/server/src/cssServer.ts @@ -9,7 +9,8 @@ import { import { URI } from 'vscode-uri'; import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, TextDocument, Position, CSSFormatConfiguration } from 'vscode-css-languageservice'; import { getLanguageModelCache } from './languageModelCache'; -import { formatError, runSafeAsync } from './utils/runner'; +import { runSafeAsync } from './utils/runner'; +import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation'; import { getDocumentContext } from './utils/documentContext'; import { fetchDataProviders } from './customData'; import { RequestService, getRequestService } from './requests'; @@ -56,6 +57,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) let dataProvidersReady: Promise = Promise.resolve(); + let diagnosticPushSupport: DiagnosticsSupport | undefined; + const languageServices: { [id: string]: LanguageService } = {}; const notReady = () => Promise.reject('Not Ready'); @@ -91,12 +94,20 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const snippetSupport = !!getClientCapability('textDocument.completion.completionItem.snippetSupport', false); scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); + formatterMaxNumberOfEdits = initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; languageServices.css = getCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); languageServices.scss = getSCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); languageServices.less = getLESSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); + const pullDiagnosticSupport = getClientCapability('textDocument.diagnostic', undefined); + if (pullDiagnosticSupport === undefined) { + diagnosticPushSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); + } else { + diagnosticPushSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); + } + const capabilities: ServerCapabilities = { textDocumentSync: TextDocumentSyncKind.Incremental, completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/', '-', ':'] } : undefined, @@ -113,6 +124,11 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) colorProvider: {}, foldingRangeProvider: true, selectionRangeProvider: true, + diagnosticProvider: { + documentSelector: null, + interFileDependencies: false, + workspaceDiagnostics: false + }, documentRangeFormattingProvider: initializationOptions?.provideFormatter === true, documentFormattingProvider: initializationOptions?.provideFormatter === true, }; @@ -157,53 +173,16 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } // reset all document settings documentSettings = {}; - // Revalidate any open text documents - documents.all().forEach(triggerValidation); - } - - const pendingValidationRequests: { [uri: string]: Disposable } = {}; - const validationDelayMs = 500; - - // The content of a text document has changed. This event is emitted - // when the text document first opened or when its content has changed. - documents.onDidChangeContent(change => { - triggerValidation(change.document); - }); - - // a document has closed: clear all diagnostics - documents.onDidClose(event => { - cleanPendingValidation(event.document); - connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); - }); - - function cleanPendingValidation(textDocument: TextDocument): void { - const request = pendingValidationRequests[textDocument.uri]; - if (request) { - request.dispose(); - delete pendingValidationRequests[textDocument.uri]; - } + diagnosticPushSupport?.requestRefresh(); } - function triggerValidation(textDocument: TextDocument): void { - cleanPendingValidation(textDocument); - pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(() => { - delete pendingValidationRequests[textDocument.uri]; - validateTextDocument(textDocument); - }, validationDelayMs); - } - - function validateTextDocument(textDocument: TextDocument): void { + async function validateTextDocument(textDocument: TextDocument): Promise { const settingsPromise = getDocumentSettings(textDocument); - Promise.all([settingsPromise, dataProvidersReady]).then(async ([settings]) => { - const stylesheet = stylesheets.get(textDocument); - const diagnostics = getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings) as Diagnostic[]; - // Send the computed diagnostics to VSCode. - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); - }, e => { - connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); - }); - } + const [settings] = await Promise.all([settingsPromise, dataProvidersReady]); + const stylesheet = stylesheets.get(textDocument); + return getLanguageService(textDocument).doValidation(textDocument, stylesheet, settings); + } function updateDataProviders(dataPaths: string[]) { dataProvidersReady = fetchDataProviders(dataPaths, requestService).then(customDataProviders => { diff --git a/extensions/css-language-features/server/src/utils/validation.ts b/extensions/css-language-features/server/src/utils/validation.ts new file mode 100644 index 0000000000000..2658f74016ac2 --- /dev/null +++ b/extensions/css-language-features/server/src/utils/validation.ts @@ -0,0 +1,109 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken, Connection, Diagnostic, Disposable, DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportKind, TextDocuments } from 'vscode-languageserver'; +import { TextDocument } from 'vscode-css-languageservice'; +import { formatError, runSafeAsync } from './runner'; +import { RuntimeEnvironment } from '../cssServer'; + +export type Validator = (textDocument: TextDocument) => Promise; +export type DiagnosticsSupport = { + dispose(): void; + requestRefresh(): void; +}; + +export function registerDiagnosticsPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { + + const pendingValidationRequests: { [uri: string]: Disposable } = {}; + const validationDelayMs = 500; + + const disposables: Disposable[] = []; + + // The content of a text document has changed. This event is emitted + // when the text document first opened or when its content has changed. + documents.onDidChangeContent(change => { + triggerValidation(change.document); + }, undefined, disposables); + + // a document has closed: clear all diagnostics + documents.onDidClose(event => { + cleanPendingValidation(event.document); + connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); + }, undefined, disposables); + + function cleanPendingValidation(textDocument: TextDocument): void { + const request = pendingValidationRequests[textDocument.uri]; + if (request) { + request.dispose(); + delete pendingValidationRequests[textDocument.uri]; + } + } + + function triggerValidation(textDocument: TextDocument): void { + cleanPendingValidation(textDocument); + const request = pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(async () => { + if (request === pendingValidationRequests[textDocument.uri]) { + try { + const diagnostics = await validate(textDocument); + if (request === pendingValidationRequests[textDocument.uri]) { + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + } + delete pendingValidationRequests[textDocument.uri]; + } catch (e) { + connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); + } + } + }, validationDelayMs); + } + + documents.all().forEach(triggerValidation); + + return { + requestRefresh: () => { + documents.all().forEach(triggerValidation); + }, + dispose: () => { + disposables.forEach(d => d.dispose()); + disposables.length = 0; + const keys = Object.keys(pendingValidationRequests); + for (const key of keys) { + pendingValidationRequests[key].dispose(); + delete pendingValidationRequests[key]; + } + } + }; +} + +export function registerDiagnosticsPullSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { + + function newDocumentDiagnosticReport(diagnostics: Diagnostic[]): DocumentDiagnosticReport { + return { + kind: DocumentDiagnosticReportKind.Full, + items: diagnostics + }; + } + + connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => { + return runSafeAsync(runtime, async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + return newDocumentDiagnosticReport(await validate(document)); + } + return newDocumentDiagnosticReport([]); + + }, newDocumentDiagnosticReport([]), `Error while computing diagnostics for ${params.textDocument.uri}`, token); + }); + + function requestRefresh(): void { + connection.languages.diagnostics.refresh(); + } + + return { + requestRefresh, + dispose: () => { + } + }; + +} diff --git a/extensions/html-language-features/server/src/utils/validation.ts b/extensions/html-language-features/server/src/utils/validation.ts new file mode 100644 index 0000000000000..5c5261cf9e5b2 --- /dev/null +++ b/extensions/html-language-features/server/src/utils/validation.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Connection, Diagnostic, Disposable, TextDocuments } from 'vscode-languageserver'; +import { TextDocument } from 'vscode-html-languageservice'; +import { formatError } from './runner'; +import { RuntimeEnvironment } from '../htmlServer'; + +export type Validator = (textDocument: TextDocument) => Promise; +export type DiagnosticPushSupport = { dispose(): void; triggerValidation(textDocument: TextDocument): void }; + +export function registerDiagnosticPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticPushSupport { + + const pendingValidationRequests: { [uri: string]: Disposable } = {}; + const validationDelayMs = 500; + + const disposables: Disposable[] = []; + + // The content of a text document has changed. This event is emitted + // when the text document first opened or when its content has changed. + documents.onDidChangeContent(change => { + triggerValidation(change.document); + }, undefined, disposables); + + // a document has closed: clear all diagnostics + documents.onDidClose(event => { + cleanPendingValidation(event.document); + connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); + }, undefined, disposables); + + function cleanPendingValidation(textDocument: TextDocument): void { + const request = pendingValidationRequests[textDocument.uri]; + if (request) { + request.dispose(); + delete pendingValidationRequests[textDocument.uri]; + } + } + + function triggerValidation(textDocument: TextDocument): void { + cleanPendingValidation(textDocument); + const request = pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(async () => { + if (request === pendingValidationRequests[textDocument.uri]) { + try { + const diagnostics = await validate(textDocument); + if (request === pendingValidationRequests[textDocument.uri]) { + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + } + delete pendingValidationRequests[textDocument.uri]; + } catch (e) { + connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); + } + } + }, validationDelayMs); + } + + documents.all().forEach(triggerValidation); + + return { + triggerValidation, + dispose: () => { + disposables.forEach(d => d.dispose()); + disposables.length = 0; + const keys = Object.keys(pendingValidationRequests); + for (const key of keys) { + pendingValidationRequests[key].dispose(); + delete pendingValidationRequests[key]; + } + } + }; +} diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index b818bcefbf280..df8bb6faa35c0 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -10,6 +10,7 @@ import { } from 'vscode-languageserver'; import { formatError, runSafe, runSafeAsync } from './utils/runner'; +import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation'; import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Range, Position } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; import { Utils, URI } from 'vscode-uri'; @@ -113,6 +114,9 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) let resultLimit = Number.MAX_VALUE; let formatterMaxNumberOfEdits = Number.MAX_VALUE; + let diagnosticSupport: DiagnosticsSupport | undefined; + + // After the server has started the client sends an initialize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities. connection.onInitialize((params: InitializeParams): InitializeResult => { @@ -147,6 +151,15 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false); formatterMaxNumberOfEdits = initializationOptions.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; + + const pullDiagnosticSupport = getClientCapability('textDocument.diagnostic', undefined); + if (pullDiagnosticSupport === undefined) { + diagnosticSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); + } else { + diagnosticSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); + } + + const capabilities: ServerCapabilities = { textDocumentSync: TextDocumentSyncKind.Incremental, completionProvider: clientSnippetSupport ? { @@ -160,7 +173,12 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) colorProvider: {}, foldingRangeProvider: true, selectionRangeProvider: true, - documentLinkProvider: {} + documentLinkProvider: {}, + diagnosticProvider: { + documentSelector: null, + interFileDependencies: false, + workspaceDiagnostics: false + } }; return { capabilities }; @@ -351,68 +369,13 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) documents.all().forEach(triggerValidation); } - // The content of a text document has changed. This event is emitted - // when the text document first opened or when its content has changed. - documents.onDidChangeContent((change) => { - limitExceededWarnings.cancel(change.document.uri); - triggerValidation(change.document); - }); - - // a document has closed: clear all diagnostics - documents.onDidClose(event => { - limitExceededWarnings.cancel(event.document.uri); - cleanPendingValidation(event.document); - connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); - }); - - const pendingValidationRequests: { [uri: string]: Disposable } = {}; - const validationDelayMs = 300; - - function cleanPendingValidation(textDocument: TextDocument): void { - const request = pendingValidationRequests[textDocument.uri]; - if (request) { - request.dispose(); - delete pendingValidationRequests[textDocument.uri]; - } - } - - function triggerValidation(textDocument: TextDocument): void { - cleanPendingValidation(textDocument); - if (validateEnabled) { - pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(() => { - delete pendingValidationRequests[textDocument.uri]; - validateTextDocument(textDocument); - }, validationDelayMs); - } else { - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics: [] }); - } - } - - function validateTextDocument(textDocument: TextDocument, callback?: (diagnostics: Diagnostic[]) => void): void { - const respond = (diagnostics: Diagnostic[]) => { - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); - if (callback) { - callback(diagnostics); - } - }; + async function validateTextDocument(textDocument: TextDocument): Promise { if (textDocument.getText().length === 0) { - respond([]); // ignore empty documents - return; + return []; // ignore empty documents } const jsonDocument = getJSONDocument(textDocument); - const version = textDocument.version; - const documentSettings: DocumentLanguageSettings = textDocument.languageId === 'jsonc' ? { comments: 'ignore', trailingCommas: 'warning' } : { comments: 'error', trailingCommas: 'error' }; - languageService.doValidation(textDocument, jsonDocument, documentSettings).then(diagnostics => { - runtime.timer.setImmediate(() => { - const currDocument = documents.get(textDocument.uri); - if (currDocument && currDocument.version === version) { - respond(diagnostics as Diagnostic[]); // Send the computed diagnostics to VSCode. - } - }); - }, error => { - connection.console.error(formatError(`Error while validating ${textDocument.uri}`, error)); - }); + return await languageService.doValidation(textDocument, jsonDocument, documentSettings); } connection.onDidChangeWatchedFiles((change) => { diff --git a/extensions/json-language-features/server/src/utils/validation.ts b/extensions/json-language-features/server/src/utils/validation.ts new file mode 100644 index 0000000000000..10130d2684f14 --- /dev/null +++ b/extensions/json-language-features/server/src/utils/validation.ts @@ -0,0 +1,109 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken, Connection, Diagnostic, Disposable, DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportKind, TextDocuments } from 'vscode-languageserver'; +import { TextDocument } from 'vscode-json-languageservice'; +import { formatError, runSafeAsync } from './runner'; +import { RuntimeEnvironment } from '../jsonServer'; + +export type Validator = (textDocument: TextDocument) => Promise; +export type DiagnosticsSupport = { + dispose(): void; + requestRefresh(): void; +}; + +export function registerDiagnosticsPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { + + const pendingValidationRequests: { [uri: string]: Disposable } = {}; + const validationDelayMs = 500; + + const disposables: Disposable[] = []; + + // The content of a text document has changed. This event is emitted + // when the text document first opened or when its content has changed. + documents.onDidChangeContent(change => { + triggerValidation(change.document); + }, undefined, disposables); + + // a document has closed: clear all diagnostics + documents.onDidClose(event => { + cleanPendingValidation(event.document); + connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); + }, undefined, disposables); + + function cleanPendingValidation(textDocument: TextDocument): void { + const request = pendingValidationRequests[textDocument.uri]; + if (request) { + request.dispose(); + delete pendingValidationRequests[textDocument.uri]; + } + } + + function triggerValidation(textDocument: TextDocument): void { + cleanPendingValidation(textDocument); + const request = pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(async () => { + if (request === pendingValidationRequests[textDocument.uri]) { + try { + const diagnostics = await validate(textDocument); + if (request === pendingValidationRequests[textDocument.uri]) { + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + } + delete pendingValidationRequests[textDocument.uri]; + } catch (e) { + connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); + } + } + }, validationDelayMs); + } + + documents.all().forEach(triggerValidation); + + return { + requestRefresh: () => { + documents.all().forEach(triggerValidation); + }, + dispose: () => { + disposables.forEach(d => d.dispose()); + disposables.length = 0; + const keys = Object.keys(pendingValidationRequests); + for (const key of keys) { + pendingValidationRequests[key].dispose(); + delete pendingValidationRequests[key]; + } + } + }; +} + +export function registerDiagnosticsPullSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { + + function newDocumentDiagnosticReport(diagnostics: Diagnostic[]): DocumentDiagnosticReport { + return { + kind: DocumentDiagnosticReportKind.Full, + items: diagnostics + }; + } + + connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => { + return runSafeAsync(runtime, async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + return newDocumentDiagnosticReport(await validate(document)); + } + return newDocumentDiagnosticReport([]); + + }, newDocumentDiagnosticReport([]), `Error while computing diagnostics for ${params.textDocument.uri}`, token); + }); + + function requestRefresh(): void { + connection.languages.diagnostics.refresh(); + } + + return { + requestRefresh, + dispose: () => { + } + }; + +} From 49944b15011247708da1a9a466fb38da5c7dda68 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 19 May 2022 22:27:28 +0200 Subject: [PATCH 217/942] DiagnosticsSupport for json and html --- .../server/src/cssServer.ts | 8 +-- .../server/src/utils/validation.ts | 5 +- .../server/src/htmlServer.ts | 58 +++++++------------ .../server/src/utils/validation.ts | 50 +++++++++++++--- .../server/src/jsonServer.ts | 36 +++++------- .../server/src/utils/validation.ts | 5 +- 6 files changed, 87 insertions(+), 75 deletions(-) diff --git a/extensions/css-language-features/server/src/cssServer.ts b/extensions/css-language-features/server/src/cssServer.ts index b4ce51c162a81..af55ec28f06ba 100644 --- a/extensions/css-language-features/server/src/cssServer.ts +++ b/extensions/css-language-features/server/src/cssServer.ts @@ -57,7 +57,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) let dataProvidersReady: Promise = Promise.resolve(); - let diagnosticPushSupport: DiagnosticsSupport | undefined; + let diagnosticsSupport: DiagnosticsSupport | undefined; const languageServices: { [id: string]: LanguageService } = {}; @@ -103,9 +103,9 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const pullDiagnosticSupport = getClientCapability('textDocument.diagnostic', undefined); if (pullDiagnosticSupport === undefined) { - diagnosticPushSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); + diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); } else { - diagnosticPushSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); + diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); } const capabilities: ServerCapabilities = { @@ -173,7 +173,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } // reset all document settings documentSettings = {}; - diagnosticPushSupport?.requestRefresh(); + diagnosticsSupport?.requestRefresh(); } async function validateTextDocument(textDocument: TextDocument): Promise { diff --git a/extensions/css-language-features/server/src/utils/validation.ts b/extensions/css-language-features/server/src/utils/validation.ts index 2658f74016ac2..edd5f5618c754 100644 --- a/extensions/css-language-features/server/src/utils/validation.ts +++ b/extensions/css-language-features/server/src/utils/validation.ts @@ -58,8 +58,6 @@ export function registerDiagnosticsPushSupport(documents: TextDocuments { documents.all().forEach(triggerValidation); @@ -85,7 +83,7 @@ export function registerDiagnosticsPullSupport(documents: TextDocuments { + const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => { return runSafeAsync(runtime, async () => { const document = documents.get(params.textDocument.uri); if (document) { @@ -103,6 +101,7 @@ export function registerDiagnosticsPullSupport(documents: TextDocuments { + registration.dispose(); } }; diff --git a/extensions/html-language-features/server/src/htmlServer.ts b/extensions/html-language-features/server/src/htmlServer.ts index 319399a038c68..3198ad16d96e8 100644 --- a/extensions/html-language-features/server/src/htmlServer.ts +++ b/extensions/html-language-features/server/src/htmlServer.ts @@ -19,6 +19,7 @@ import { pushAll } from './utils/arrays'; import { getDocumentContext } from './utils/documentContext'; import { URI } from 'vscode-uri'; import { formatError, runSafe } from './utils/runner'; +import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation'; import { getFoldingRanges } from './modes/htmlFolding'; import { fetchHTMLDataProviders } from './customData'; @@ -92,6 +93,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) let languageModes: LanguageModes; + let diagnosticsSupport: DiagnosticsSupport | undefined; + let clientSnippetSupport = false; let dynamicFormatterRegistration = false; let scopedSettingsSupport = false; @@ -180,6 +183,14 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) workspaceFoldersSupport = getClientCapability('workspace.workspaceFolders', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); formatterMaxNumberOfEdits = initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; + + const pullDiagnosticSupport = getClientCapability('textDocument.diagnostic', undefined); + if (pullDiagnosticSupport === undefined) { + diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); + } else { + diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); + } + const capabilities: ServerCapabilities = { textDocumentSync: TextDocumentSyncKind.Incremental, completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/'] } : undefined, @@ -196,7 +207,12 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) foldingRangeProvider: true, selectionRangeProvider: true, renameProvider: true, - linkedEditingRangeProvider: true + linkedEditingRangeProvider: true, + diagnosticProvider: { + documentSelector: null, + interFileDependencies: false, + workspaceDiagnostics: false + } }; return { capabilities }; }); @@ -217,7 +233,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } } workspaceFolders = updatedFolders.concat(toAdd); - documents.all().forEach(triggerValidation); + diagnosticsSupport?.requestRefresh(); }); } }); @@ -228,7 +244,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) connection.onDidChangeConfiguration((change) => { globalSettings = change.settings as Settings; documentSettings = {}; // reset all document settings - documents.all().forEach(triggerValidation); + diagnosticsSupport?.requestRefresh(); // dynamically enable & disable the formatter if (dynamicFormatterRegistration) { @@ -248,37 +264,6 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } }); - const pendingValidationRequests: { [uri: string]: Disposable } = {}; - const validationDelayMs = 500; - - // The content of a text document has changed. This event is emitted - // when the text document first opened or when its content has changed. - documents.onDidChangeContent(change => { - triggerValidation(change.document); - }); - - // a document has closed: clear all diagnostics - documents.onDidClose(event => { - cleanPendingValidation(event.document); - connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] }); - }); - - function cleanPendingValidation(textDocument: TextDocument): void { - const request = pendingValidationRequests[textDocument.uri]; - if (request) { - request.dispose(); - delete pendingValidationRequests[textDocument.uri]; - } - } - - function triggerValidation(textDocument: TextDocument): void { - cleanPendingValidation(textDocument); - pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(() => { - delete pendingValidationRequests[textDocument.uri]; - validateTextDocument(textDocument); - }, validationDelayMs); - } - function isValidationEnabled(languageId: string, settings: Settings = globalSettings) { const validationSettings = settings && settings.html && settings.html.validate; if (validationSettings) { @@ -287,7 +272,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) return true; } - async function validateTextDocument(textDocument: TextDocument) { + async function validateTextDocument(textDocument: TextDocument): Promise { try { const version = textDocument.version; const diagnostics: Diagnostic[] = []; @@ -301,12 +286,13 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) pushAll(diagnostics, await mode.doValidation(latestTextDocument, settings)); } } - connection.sendDiagnostics({ uri: latestTextDocument.uri, diagnostics }); + return diagnostics; } } } catch (e) { connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); } + return []; } connection.onCompletion(async (textDocumentPosition, token) => { diff --git a/extensions/html-language-features/server/src/utils/validation.ts b/extensions/html-language-features/server/src/utils/validation.ts index 5c5261cf9e5b2..adb13086391fa 100644 --- a/extensions/html-language-features/server/src/utils/validation.ts +++ b/extensions/html-language-features/server/src/utils/validation.ts @@ -3,15 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Connection, Diagnostic, Disposable, TextDocuments } from 'vscode-languageserver'; +import { CancellationToken, Connection, Diagnostic, Disposable, DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportKind, TextDocuments } from 'vscode-languageserver'; import { TextDocument } from 'vscode-html-languageservice'; -import { formatError } from './runner'; +import { formatError, runSafe } from './runner'; import { RuntimeEnvironment } from '../htmlServer'; export type Validator = (textDocument: TextDocument) => Promise; -export type DiagnosticPushSupport = { dispose(): void; triggerValidation(textDocument: TextDocument): void }; +export type DiagnosticsSupport = { + dispose(): void; + requestRefresh(): void; +}; -export function registerDiagnosticPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticPushSupport { +export function registerDiagnosticsPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { const pendingValidationRequests: { [uri: string]: Disposable } = {}; const validationDelayMs = 500; @@ -55,10 +58,10 @@ export function registerDiagnosticPushSupport(documents: TextDocuments { + documents.all().forEach(triggerValidation); + }, dispose: () => { disposables.forEach(d => d.dispose()); disposables.length = 0; @@ -70,3 +73,36 @@ export function registerDiagnosticPushSupport(documents: TextDocuments, connection: Connection, runtime: RuntimeEnvironment, validate: Validator): DiagnosticsSupport { + + function newDocumentDiagnosticReport(diagnostics: Diagnostic[]): DocumentDiagnosticReport { + return { + kind: DocumentDiagnosticReportKind.Full, + items: diagnostics + }; + } + + const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => { + return runSafe(runtime, async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + return newDocumentDiagnosticReport(await validate(document)); + } + return newDocumentDiagnosticReport([]); + + }, newDocumentDiagnosticReport([]), `Error while computing diagnostics for ${params.textDocument.uri}`, token); + }); + + function requestRefresh(): void { + connection.languages.diagnostics.refresh(); + } + + return { + requestRefresh, + dispose: () => { + registration.dispose(); + } + }; + +} diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index df8bb6faa35c0..5df112220d174 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -9,7 +9,7 @@ import { DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions, Diagnostic } from 'vscode-languageserver'; -import { formatError, runSafe, runSafeAsync } from './utils/runner'; +import { runSafe, runSafeAsync } from './utils/runner'; import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation'; import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Range, Position } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; @@ -114,7 +114,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) let resultLimit = Number.MAX_VALUE; let formatterMaxNumberOfEdits = Number.MAX_VALUE; - let diagnosticSupport: DiagnosticsSupport | undefined; + let diagnosticsSupport: DiagnosticsSupport | undefined; // After the server has started the client sends an initialize request. The server receives @@ -154,9 +154,9 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const pullDiagnosticSupport = getClientCapability('textDocument.diagnostic', undefined); if (pullDiagnosticSupport === undefined) { - diagnosticSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); + diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); } else { - diagnosticSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); + diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); } @@ -301,25 +301,18 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) needsRevalidation = languageService.resetSchema(uriOrUris); } if (needsRevalidation) { - for (const doc of documents.all()) { - triggerValidation(doc); - } + diagnosticsSupport?.requestRefresh(); } }); // Retry schema validation on all open documents - connection.onRequest(ForceValidateRequest.type, uri => { - return new Promise(resolve => { - const document = documents.get(uri); - if (document) { - updateConfiguration(); - validateTextDocument(document, diagnostics => { - resolve(diagnostics); - }); - } else { - resolve([]); - } - }); + connection.onRequest(ForceValidateRequest.type, async uri => { + const document = documents.get(uri); + if (document) { + updateConfiguration(); + return await validateTextDocument(document); + } + return []; }); connection.onRequest(LanguageStatusRequest.type, async uri => { @@ -365,8 +358,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } languageService.configure(languageSettings); - // Revalidate any open text documents - documents.all().forEach(triggerValidation); + diagnosticsSupport?.requestRefresh(); } async function validateTextDocument(textDocument: TextDocument): Promise { @@ -387,7 +379,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } }); if (hasChanges) { - documents.all().forEach(triggerValidation); + diagnosticsSupport?.requestRefresh(); } }); diff --git a/extensions/json-language-features/server/src/utils/validation.ts b/extensions/json-language-features/server/src/utils/validation.ts index 10130d2684f14..a4d06d90cd66e 100644 --- a/extensions/json-language-features/server/src/utils/validation.ts +++ b/extensions/json-language-features/server/src/utils/validation.ts @@ -58,8 +58,6 @@ export function registerDiagnosticsPushSupport(documents: TextDocuments { documents.all().forEach(triggerValidation); @@ -85,7 +83,7 @@ export function registerDiagnosticsPullSupport(documents: TextDocuments { + const registration = connection.languages.diagnostics.on(async (params: DocumentDiagnosticParams, token: CancellationToken) => { return runSafeAsync(runtime, async () => { const document = documents.get(params.textDocument.uri); if (document) { @@ -103,6 +101,7 @@ export function registerDiagnosticsPullSupport(documents: TextDocuments { + registration.dispose(); } }; From f1b392ed556d963195ffac589d329325d5e6dc74 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 19 May 2022 22:35:59 +0200 Subject: [PATCH 218/942] polish names --- extensions/css-language-features/server/src/cssServer.ts | 4 ++-- extensions/html-language-features/server/src/htmlServer.ts | 4 ++-- extensions/json-language-features/server/src/jsonServer.ts | 7 ++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/extensions/css-language-features/server/src/cssServer.ts b/extensions/css-language-features/server/src/cssServer.ts index af55ec28f06ba..314aa8aebc0a8 100644 --- a/extensions/css-language-features/server/src/cssServer.ts +++ b/extensions/css-language-features/server/src/cssServer.ts @@ -101,8 +101,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) languageServices.scss = getSCSSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); languageServices.less = getLESSLanguageService({ fileSystemProvider: requestService, clientCapabilities: params.capabilities }); - const pullDiagnosticSupport = getClientCapability('textDocument.diagnostic', undefined); - if (pullDiagnosticSupport === undefined) { + const supportsDiagnosticPull = getClientCapability('textDocument.diagnostic', undefined); + if (supportsDiagnosticPull === undefined) { diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); } else { diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); diff --git a/extensions/html-language-features/server/src/htmlServer.ts b/extensions/html-language-features/server/src/htmlServer.ts index 3198ad16d96e8..99e3cb75bcdcc 100644 --- a/extensions/html-language-features/server/src/htmlServer.ts +++ b/extensions/html-language-features/server/src/htmlServer.ts @@ -184,8 +184,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); formatterMaxNumberOfEdits = initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; - const pullDiagnosticSupport = getClientCapability('textDocument.diagnostic', undefined); - if (pullDiagnosticSupport === undefined) { + const supportsDiagnosticPull = getClientCapability('textDocument.diagnostic', undefined); + if (supportsDiagnosticPull === undefined) { diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); } else { diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index 5df112220d174..9594d242f51a9 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -144,22 +144,19 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) return c; } - - clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false); dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof initializationOptions.provideFormatter !== 'boolean'); foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false); formatterMaxNumberOfEdits = initializationOptions.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; - const pullDiagnosticSupport = getClientCapability('textDocument.diagnostic', undefined); - if (pullDiagnosticSupport === undefined) { + const supportsDiagnosticPull = getClientCapability('textDocument.diagnostic', undefined); + if (supportsDiagnosticPull === undefined) { diagnosticsSupport = registerDiagnosticsPushSupport(documents, connection, runtime, validateTextDocument); } else { diagnosticsSupport = registerDiagnosticsPullSupport(documents, connection, runtime, validateTextDocument); } - const capabilities: ServerCapabilities = { textDocumentSync: TextDocumentSyncKind.Incremental, completionProvider: clientSnippetSupport ? { From c0769274fa136b45799edeccc0d0a2f645b75caf Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Sun, 22 May 2022 20:04:19 +0200 Subject: [PATCH 219/942] update json schema (#150001) --- src/vs/base/common/jsonSchema.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/jsonSchema.ts b/src/vs/base/common/jsonSchema.ts index da614983689e2..81262c2f46aab 100644 --- a/src/vs/base/common/jsonSchema.ts +++ b/src/vs/base/common/jsonSchema.ts @@ -46,6 +46,7 @@ export interface IJSONSchema { const?: any; contains?: IJSONSchema; propertyNames?: IJSONSchema; + examples?: any[]; // schema draft 07 $comment?: string; @@ -53,7 +54,27 @@ export interface IJSONSchema { then?: IJSONSchema; else?: IJSONSchema; - // VS Code extensions + // schema 2019-09 + unevaluatedProperties?: boolean | IJSONSchema; + unevaluatedItems?: boolean | IJSONSchema; + minContains?: number; + maxContains?: number; + deprecated?: boolean; + dependentRequired?: { [prop: string]: string[] }; + dependentSchemas?: IJSONSchemaMap; + $defs?: { [name: string]: IJSONSchema }; + $anchor?: string; + $recursiveRef?: string; + $recursiveAnchor?: string; + $vocabulary?: any; + + // schema 2020-12 + prefixItems?: IJSONSchema[]; + $dynamicRef?: string; + $dynamicAnchor?: string; + + // VSCode extensions + defaultSnippets?: IJSONSchemaSnippet[]; errorMessage?: string; patternErrorMessage?: string; From 62ed0040bc4cbb248f19272d268a18eb78f63278 Mon Sep 17 00:00:00 2001 From: Song Xie Date: Sun, 22 May 2022 14:21:34 -0700 Subject: [PATCH 220/942] Format date strings with the right locale --- .../workbench/contrib/extensions/browser/extensionEditor.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 4cfe2517ef5e1..da2bc141afc45 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/extensionEditor'; import { localize } from 'vs/nls'; import * as arrays from 'vs/base/common/arrays'; -import { OS } from 'vs/base/common/platform'; +import { OS, locale } from 'vs/base/common/platform'; import { Event, Emitter } from 'vs/base/common/event'; import { Cache, CacheResult } from 'vs/base/common/cache'; import { Action, IAction } from 'vs/base/common/actions'; @@ -997,11 +997,11 @@ export class ExtensionEditor extends EditorPane { append(moreInfo, $('.more-info-entry', undefined, $('div', undefined, localize('release date', "Released on")), - $('div', undefined, new Date(gallery.releaseDate).toLocaleString(undefined, { hourCycle: 'h23' })) + $('div', undefined, new Date(gallery.releaseDate).toLocaleString(locale, { hourCycle: 'h23' })) ), $('.more-info-entry', undefined, $('div', undefined, localize('last updated', "Last updated")), - $('div', undefined, new Date(gallery.lastUpdated).toLocaleString(undefined, { hourCycle: 'h23' })) + $('div', undefined, new Date(gallery.lastUpdated).toLocaleString(locale, { hourCycle: 'h23' })) ) ); } From 86d085dbcaf02287a025923d5933cdde7a55f127 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 23 May 2022 00:57:58 +0200 Subject: [PATCH 221/942] - Save settings profile - Switch settings profile - Remove settings profile - Status bar entry --- build/lib/i18n.resources.json | 4 +- .../sharedProcess/sharedProcessMain.ts | 3 +- src/vs/code/electron-main/app.ts | 5 + src/vs/code/electron-main/main.ts | 39 +++-- src/vs/code/node/cliProcessMain.ts | 2 +- .../test/common/configurationService.test.ts | 4 +- src/vs/platform/environment/common/argv.ts | 3 - src/vs/platform/environment/node/argv.ts | 1 - .../abstractExtensionManagementService.ts | 6 +- .../common/extensionManagement.ts | 1 + .../common/extensionManagementIpc.ts | 7 +- .../common/extensionsProfileScannerService.ts | 8 +- .../common/extensionsScannerService.ts | 10 +- .../node/extensionManagementService.ts | 5 + .../electron-main/sharedProcess.ts | 3 +- .../sharedProcess/node/sharedProcess.ts | 4 +- .../electron-main/storageMainService.test.ts | 3 +- .../userData/common/fileUserDataProvider.ts | 14 +- .../test/browser/fileUserDataProvider.test.ts | 2 +- .../userDataProfile/common/userDataProfile.ts | 71 ++++++-- .../electron-main/userDataProfile.ts | 46 +++++ .../electron-sandbox/userDataProfile.ts | 29 ++++ .../test/common/userDataSyncClient.ts | 4 +- src/vs/platform/window/common/window.ts | 5 +- .../electron-main/windowsMainService.ts | 4 +- .../node/remoteExtensionHostAgentCli.ts | 2 +- src/vs/server/node/serverServices.ts | 2 +- .../parts/activitybar/activitybarPart.ts | 2 +- .../browser/parts/panel/panelPart.ts | 2 +- .../browser/parts/statusbar/statusbarModel.ts | 2 +- src/vs/workbench/browser/web.main.ts | 8 +- .../profiles/common/profiles.contribution.ts | 6 - .../browser/userDataProfile.contribution.ts | 38 +++++ .../common/userDataProfileActions.ts} | 127 +++++++++++--- .../electron-sandbox/desktop.main.ts | 10 +- .../configurationEditingService.test.ts | 2 +- .../test/browser/configurationService.test.ts | 18 +- .../browser/webExtensionsScannerService.ts | 7 +- .../common/extensionManagement.ts | 3 +- .../common/extensionManagementService.ts | 10 +- .../common/webExtensionManagementService.ts | 4 + .../browser/extensionStorageMigration.test.ts | 2 +- .../test/browser/keybindingEditing.test.ts | 2 +- .../browser/userDataProfileManagement.ts | 157 ++++++++++++++++++ .../common/extensionsProfile.ts | 2 +- .../common/globalStateProfile.ts | 4 +- .../common/settingsProfile.ts | 2 +- .../common/userDataProfile.ts} | 35 +++- .../common/userDataProfileStorageRegistry.ts} | 0 .../userDataProfileWorkbenchService.ts} | 16 +- .../views/browser/viewDescriptorService.ts | 2 +- .../views/common/viewContainerModel.ts | 2 +- .../test/browser/workbenchTestServices.ts | 2 +- .../electron-browser/workbenchTestServices.ts | 22 ++- src/vs/workbench/workbench.common.main.ts | 7 +- 55 files changed, 639 insertions(+), 142 deletions(-) create mode 100644 src/vs/platform/userDataProfile/electron-main/userDataProfile.ts create mode 100644 src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts delete mode 100644 src/vs/workbench/contrib/profiles/common/profiles.contribution.ts create mode 100644 src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts rename src/vs/workbench/contrib/{profiles/common/profilesActions.ts => userDataProfile/common/userDataProfileActions.ts} (52%) create mode 100644 src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts rename src/vs/workbench/services/{profiles => userDataProfile}/common/extensionsProfile.ts (98%) rename src/vs/workbench/services/{profiles => userDataProfile}/common/globalStateProfile.ts (93%) rename src/vs/workbench/services/{profiles => userDataProfile}/common/settingsProfile.ts (98%) rename src/vs/workbench/services/{profiles/common/profile.ts => userDataProfile/common/userDataProfile.ts} (55%) rename src/vs/workbench/services/{profiles/common/profileStorageRegistry.ts => userDataProfile/common/userDataProfileStorageRegistry.ts} (100%) rename src/vs/workbench/services/{profiles/common/profileService.ts => userDataProfile/common/userDataProfileWorkbenchService.ts} (76%) diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 06bfbb2b506b9..0eee6e028e470 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -451,11 +451,11 @@ "project": "vscode-workbench" }, { - "name": "vs/workbench/contrib/profiles", + "name": "vs/workbench/contrib/userDataProfile", "project": "vscode-profiles" }, { - "name": "vs/workbench/services/profiles", + "name": "vs/workbench/services/userDataProfile", "project": "vscode-profiles" } ] diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 3abecf7e57cdf..759146f5d368a 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -102,6 +102,7 @@ import { IExtensionsScannerService } from 'vs/platform/extensionManagement/commo import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService'; import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; +import { revive } from 'vs/base/common/marshalling'; class SharedProcessMain extends Disposable { @@ -224,7 +225,7 @@ class SharedProcessMain extends Disposable { fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider); // User Data Profiles - const userDataProfilesService = this._register(new UserDataProfilesService(this.configuration.profile, environmentService, logService)); + const userDataProfilesService = this._register(new UserDataProfilesService(revive(this.configuration.defaultProfile), revive(this.configuration.currentProfile), environmentService, fileService, logService)); services.set(IUserDataProfilesService, userDataProfilesService); // Configuration diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 651c9ea09e2ec..e94e8418e1bd0 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -99,6 +99,7 @@ import { IWorkspacesHistoryMainService, WorkspacesHistoryMainService } from 'vs/ import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { IWorkspacesManagementMainService, WorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { CredentialsNativeMainService } from 'vs/platform/credentials/electron-main/credentialsMainService'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; /** * The main VS Code application. There will only ever be one instance, @@ -702,6 +703,10 @@ export class CodeApplication extends Disposable { mainProcessElectronServer.registerChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME, fileSystemProviderChannel); sharedProcessClient.then(client => client.registerChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME, fileSystemProviderChannel)); + // Profiles + const userDataProfilesService = ProxyChannel.fromService(accessor.get(IUserDataProfilesService)); + mainProcessElectronServer.registerChannel('userDataProfiles', userDataProfilesService); + // Update const updateChannel = new UpdateChannel(accessor.get(IUpdateService)); mainProcessElectronServer.registerChannel('update', updateChannel); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 943a5943535b3..194211f095fe5 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -62,7 +62,8 @@ import { IStateMainService } from 'vs/platform/state/electron-main/state'; import { StateMainService } from 'vs/platform/state/electron-main/stateMainService'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IThemeMainService, ThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; /** * The main VS Code entry point. @@ -90,13 +91,13 @@ class CodeMain { setUnexpectedErrorHandler(err => console.error(err)); // Create services - const [instantiationService, instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService, userDataProfilesService] = this.createServices(); + const [instantiationService, instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService, userDataProfilesMainService] = this.createServices(); try { // Init services try { - await this.initServices(environmentMainService, userDataProfilesService, configurationService, stateMainService); + await this.initServices(environmentMainService, userDataProfilesMainService, configurationService, stateMainService); } catch (error) { // Show a dialog for errors that can be resolved by the user @@ -138,7 +139,7 @@ class CodeMain { } } - private createServices(): [IInstantiationService, IProcessEnvironment, IEnvironmentMainService, ConfigurationService, StateMainService, BufferLogService, IProductService, IUserDataProfilesService] { + private createServices(): [IInstantiationService, IProcessEnvironment, IEnvironmentMainService, ConfigurationService, StateMainService, BufferLogService, IProductService, UserDataProfilesMainService] { const services = new ServiceCollection(); // Product @@ -146,8 +147,7 @@ class CodeMain { services.set(IProductService, productService); // Environment - const args = this.resolveArgs(); - const environmentMainService = new EnvironmentMainService(args, productService); + const environmentMainService = new EnvironmentMainService(this.resolveArgs(), productService); const instanceEnvironment = this.patchEnvironment(environmentMainService); // Patch `process.env` with the instance's environment services.set(IEnvironmentMainService, environmentMainService); @@ -169,18 +169,18 @@ class CodeMain { services.set(ILoggerService, new LoggerService(logService, fileService)); // User Data Profiles - const userDataProfilesService = new UserDataProfilesService(args['__profile'], environmentMainService, logService); - services.set(IUserDataProfilesService, userDataProfilesService); + const userDataProfilesMainService = new UserDataProfilesMainService(environmentMainService, fileService, logService); + services.set(IUserDataProfilesService, userDataProfilesMainService); // Configuration - const configurationService = new ConfigurationService(userDataProfilesService, fileService); + const configurationService = new ConfigurationService(userDataProfilesMainService, fileService); services.set(IConfigurationService, configurationService); // Lifecycle services.set(ILifecycleMainService, new SyncDescriptor(LifecycleMainService)); // State - const stateMainService = new StateMainService(environmentMainService, userDataProfilesService, logService, fileService); + const stateMainService = new StateMainService(environmentMainService, userDataProfilesMainService, logService, fileService); services.set(IStateMainService, stateMainService); // Request @@ -198,7 +198,7 @@ class CodeMain { // Protocol services.set(IProtocolMainService, new SyncDescriptor(ProtocolMainService)); - return [new InstantiationService(services, true), instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService, userDataProfilesService]; + return [new InstantiationService(services, true), instanceEnvironment, environmentMainService, configurationService, stateMainService, bufferLogService, productService, userDataProfilesMainService]; } private patchEnvironment(environmentMainService: IEnvironmentMainService): IProcessEnvironment { @@ -218,7 +218,10 @@ class CodeMain { return instanceEnvironment; } - private initServices(environmentMainService: IEnvironmentMainService, userDataProfilesService: IUserDataProfilesService, configurationService: ConfigurationService, stateMainService: StateMainService): Promise { + private async initServices(environmentMainService: IEnvironmentMainService, userDataProfilesMainService: UserDataProfilesMainService, configurationService: ConfigurationService, stateMainService: StateMainService): Promise { + // State service + await stateMainService.init(); + return Promises.settled([ // Environment service (paths) @@ -226,18 +229,18 @@ class CodeMain { environmentMainService.extensionsPath, environmentMainService.codeCachePath, environmentMainService.logsPath, - userDataProfilesService.defaultProfile.globalStorageHome.fsPath, - userDataProfilesService.currentProfile.globalStorageHome.fsPath, + userDataProfilesMainService.defaultProfile.globalStorageHome.fsPath, environmentMainService.workspaceStorageHome.fsPath, environmentMainService.localHistoryHome.fsPath, environmentMainService.backupHome ].map(path => path ? FSPromises.mkdir(path, { recursive: true }) : undefined)), - // Configuration service - configurationService.initialize(), + // User Data Profiles Service + userDataProfilesMainService.init(stateMainService) + .then(() => userDataProfilesMainService.currentProfile.globalStorageHome.fsPath !== userDataProfilesMainService.defaultProfile.globalStorageHome.fsPath ? FSPromises.mkdir(userDataProfilesMainService.currentProfile.globalStorageHome.fsPath, { recursive: true }) : undefined), - // State service - stateMainService.init() + // Configuration service + configurationService.initialize() ]); } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 8a76844d1fb6b..9973c3cd78b60 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -131,7 +131,7 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, diskFileSystemProvider); // User Data Profiles - const userDataProfilesService = new UserDataProfilesService(undefined, environmentService, logService); + const userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService); services.set(IUserDataProfilesService, userDataProfilesService); // Configuration diff --git a/src/vs/platform/configuration/test/common/configurationService.test.ts b/src/vs/platform/configuration/test/common/configurationService.test.ts index bc560f07ab3df..adbfc34c029f0 100644 --- a/src/vs/platform/configuration/test/common/configurationService.test.ts +++ b/src/vs/platform/configuration/test/common/configurationService.test.ts @@ -31,7 +31,7 @@ suite('ConfigurationService', () => { fileService = disposables.add(new FileService(new NullLogService())); const diskFileSystemProvider = disposables.add(new InMemoryFileSystemProvider()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - userDataProfileService = new UserDataProfilesService(undefined, { userRoamingDataHome: URI.file('User') }, new NullLogService()); + userDataProfileService = new UserDataProfilesService(undefined, undefined, { userRoamingDataHome: URI.file('User') }, fileService, new NullLogService()); }); teardown(() => disposables.clear()); @@ -157,7 +157,7 @@ suite('ConfigurationService', () => { } }); - let testObject = disposables.add(new ConfigurationService(new UserDataProfilesService(undefined, { userRoamingDataHome: URI.file('User1') }, new NullLogService()), fileService)); + let testObject = disposables.add(new ConfigurationService(new UserDataProfilesService(undefined, undefined, { userRoamingDataHome: URI.file('User1') }, fileService, new NullLogService()), fileService)); await testObject.initialize(); let setting = testObject.getValue(); diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index b37686cadb192..b2b4500a61468 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -110,7 +110,4 @@ export interface NativeParsedArgs { // MS Build command line arg 'ms-enable-electron-run-as-node'?: boolean; - - // Internal - '__profile'?: string; } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 0748875195adc..062ce48a5808c 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -149,7 +149,6 @@ export const OPTIONS: OptionDescriptions> = { 'vmodule': { type: 'string' }, '_urls': { type: 'string[]' }, 'disable-dev-shm-usage': { type: 'boolean' }, - '__profile': { type: 'string' }, _: { type: 'string[]' } // main arguments }; diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 7f456cfe2c2fd..1013592e6f925 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -594,6 +594,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl abstract install(vsix: URI, options?: ServerInstallVSIXOptions): Promise; abstract getInstalled(type?: ExtensionType, profileLocation?: URI): Promise; + abstract getMetadata(extension: ILocalExtension): Promise; abstract updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise; abstract updateExtensionScope(local: ILocalExtension, isMachineScoped: boolean): Promise; @@ -705,7 +706,7 @@ export abstract class AbstractInstallExtensionTask extends AbstractExtensionTask protected async doRun(token: CancellationToken): Promise { const { local, metadata } = await this.install(token); if (this.options.profileLocation) { - await this.extensionsProfileScannerService.addExtensionToProfile(local, metadata, this.options.profileLocation); + await this.extensionsProfileScannerService.addExtensionsToProfile([[local, metadata]], this.options.profileLocation); } return local; } @@ -725,9 +726,10 @@ export abstract class AbstractUninstallExtensionTask extends AbstractExtensionTa } protected async doRun(token: CancellationToken): Promise { - await this.uninstall(token); if (this.options.profileLocation) { await this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension.identifier, this.options.profileLocation); + } else { + await this.uninstall(token); } } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index ee103be4247b8..e7c9497ee379c 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -412,6 +412,7 @@ export interface IExtensionManagementService { getInstalled(type?: ExtensionType): Promise; getExtensionsControlManifest(): Promise; + getMetadata(extension: ILocalExtension): Promise; updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise; updateExtensionScope(local: ILocalExtension, isMachineScoped: boolean): Promise; diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index 5f51a32473e90..c55491edf17b4 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -10,7 +10,7 @@ import { cloneAndChange } from 'vs/base/common/objects'; import { URI, UriComponents } from 'vs/base/common/uri'; import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, IExtensionTipsService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOptions, InstallVSIXOptions, IExtensionsControlManifest, isTargetPlatformCompatible, UninstallOptions, IServerExtensionManagementService, ServerInstallOptions, ServerInstallVSIXOptions, ServerUninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, IExtensionTipsService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOptions, InstallVSIXOptions, IExtensionsControlManifest, isTargetPlatformCompatible, UninstallOptions, IServerExtensionManagementService, ServerInstallOptions, ServerInstallVSIXOptions, ServerUninstallOptions, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; @@ -72,6 +72,7 @@ export class ExtensionManagementChannel implements IServerChannel { case 'uninstall': return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), revive(args[1])); case 'reinstallFromGallery': return this.service.reinstallFromGallery(transformIncomingExtension(args[0], uriTransformer)); case 'getInstalled': return this.service.getInstalled(args[0], URI.revive(args[1])).then(extensions => extensions.map(e => transformOutgoingExtension(e, uriTransformer))); + case 'getMetadata': return this.service.getMetadata(transformIncomingExtension(args[0], uriTransformer)); case 'updateMetadata': return this.service.updateMetadata(transformIncomingExtension(args[0], uriTransformer), args[1]).then(e => transformOutgoingExtension(e, uriTransformer)); case 'updateExtensionScope': return this.service.updateExtensionScope(transformIncomingExtension(args[0], uriTransformer), args[1]).then(e => transformOutgoingExtension(e, uriTransformer)); case 'getExtensionsControlManifest': return this.service.getExtensionsControlManifest(); @@ -165,6 +166,10 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt .then(extensions => extensions.map(extension => transformIncomingExtension(extension, null))); } + getMetadata(local: ILocalExtension): Promise { + return Promise.resolve(this.channel.call('getMetadata', [local])); + } + updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { return Promise.resolve(this.channel.call('updateMetadata', [local, metadata])) .then(extension => transformIncomingExtension(extension, null)); diff --git a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts index b7a0693fde7dd..2f77d97b6ff75 100644 --- a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts @@ -32,7 +32,7 @@ export interface IExtensionsProfileScannerService { readonly _serviceBrand: undefined; scanProfileExtensions(profileLocation: URI): Promise; - addExtensionToProfile(extension: ILocalExtension, metadata: Metadata, profileLocation: URI): Promise; + addExtensionsToProfile(extensions: [ILocalExtension, Metadata | undefined][], profileLocation: URI): Promise; removeExtensionFromProfile(identifier: IExtensionIdentifier, profileLocation: URI): Promise; } @@ -52,11 +52,11 @@ export class ExtensionsProfileScannerService extends Disposable implements IExte return this.withProfileExtensions(profileLocation); } - addExtensionToProfile(extension: ILocalExtension, metadata: Metadata, profileLocation: URI): Promise { + addExtensionsToProfile(extensions: [ILocalExtension, Metadata][], profileLocation: URI): Promise { return this.withProfileExtensions(profileLocation, profileExtensions => { // Remove the existing extension to avoid duplicates - profileExtensions = profileExtensions.filter(e => !areSameExtensions(e.identifier, extension.identifier)); - profileExtensions.push({ identifier: extension.identifier, location: extension.location, metadata }); + profileExtensions = profileExtensions.filter(e => extensions.some(([extension]) => !areSameExtensions(e.identifier, extension.identifier))); + profileExtensions.push(...extensions.map(([extension, metadata]) => ({ identifier: extension.identifier, location: extension.location, metadata }))); return profileExtensions; }); } diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index 419327d391062..143ff8c916a09 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -124,6 +124,7 @@ export interface IExtensionsScannerService { scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise; scanOneOrMultipleExtensions(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise; + scanMetadata(extensionLocation: URI): Promise; updateMetadata(extensionLocation: URI, metadata: Partial): Promise; } @@ -230,6 +231,13 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem return this.applyScanOptions(extensions, scanOptions, true); } + async scanMetadata(extensionLocation: URI): Promise { + const manifestLocation = joinPath(extensionLocation, 'package.json'); + const content = (await this.fileService.readFile(manifestLocation)).value.toString(); + const manifest: IScannedExtensionManifest = JSON.parse(content); + return manifest.__metadata; + } + async updateMetadata(extensionLocation: URI, metaData: Partial): Promise { const manifestLocation = joinPath(extensionLocation, 'package.json'); const content = (await this.fileService.readFile(manifestLocation)).value.toString(); @@ -542,7 +550,7 @@ class ExtensionsScanner extends Disposable { return extension; } - private async scanExtensionManifest(extensionLocation: URI): Promise { + async scanExtensionManifest(extensionLocation: URI): Promise { const manifestLocation = joinPath(extensionLocation, 'package.json'); let content; try { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index ed0f6f97b2aca..445cf64b5b315 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -61,6 +61,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi @ITelemetryService telemetryService: ITelemetryService, @ILogService logService: ILogService, @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, + @IExtensionsScannerService private readonly extensionsScannerService: IExtensionsScannerService, @IExtensionsProfileScannerService protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService, @IDownloadService private downloadService: IDownloadService, @IInstantiationService instantiationService: IInstantiationService, @@ -126,6 +127,10 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi return this.installExtension(manifest, downloadLocation, options); } + getMetadata(extension: ILocalExtension): Promise { + return this.extensionsScannerService.scanMetadata(extension.location); + } + async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { this.logService.trace('ExtensionManagementService#updateMetadata', local.identifier.id); const localMetadata: Metadata = { ...metadata }; diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index 78508f934977f..5f6ea5596b492 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -241,7 +241,8 @@ export class SharedProcess extends Disposable implements ISharedProcess { appRoot: this.environmentMainService.appRoot, codeCachePath: this.environmentMainService.codeCachePath, backupWorkspacesPath: this.environmentMainService.backupWorkspacesPath, - profile: this.userDataProfilesService.currentProfile.name, + defaultProfile: this.userDataProfilesService.defaultProfile, + currentProfile: this.userDataProfilesService.currentProfile, userEnv: this.userEnv, args: this.environmentMainService.args, logLevel: this.logService.getLevel(), diff --git a/src/vs/platform/sharedProcess/node/sharedProcess.ts b/src/vs/platform/sharedProcess/node/sharedProcess.ts index 7b9b45970fcbf..597e13cf65390 100644 --- a/src/vs/platform/sharedProcess/node/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/node/sharedProcess.ts @@ -6,6 +6,7 @@ import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { LogLevel } from 'vs/platform/log/common/log'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; export interface ISharedProcess { @@ -25,5 +26,6 @@ export interface ISharedProcessConfiguration extends ISandboxConfiguration { readonly backupWorkspacesPath: string; - readonly profile?: string; + readonly defaultProfile: IUserDataProfile; + readonly currentProfile: IUserDataProfile; } diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 1ff463569a6c5..62a7f9744c551 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -128,7 +128,8 @@ suite('StorageMainService', function () { function createStorageService(lifecycleMainService: ILifecycleMainService = new StorageTestLifecycleMainService()): TestStorageMainService { const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService); - return new TestStorageMainService(new NullLogService(), environmentService, new UserDataProfilesService(undefined, environmentService, new NullLogService()), lifecycleMainService, new FileService(new NullLogService()), NullTelemetryService); + const fileService = new FileService(new NullLogService()); + return new TestStorageMainService(new NullLogService(), environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, new NullLogService()), lifecycleMainService, fileService, NullTelemetryService); } test('basics (global)', function () { diff --git a/src/vs/platform/userData/common/fileUserDataProvider.ts b/src/vs/platform/userData/common/fileUserDataProvider.ts index 138aa219e8e4c..b0f5e864a11cb 100644 --- a/src/vs/platform/userData/common/fileUserDataProvider.ts +++ b/src/vs/platform/userData/common/fileUserDataProvider.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, IFileOverwriteOptions, FileType, IFileWriteOptions, IFileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithFileReadStreamCapability, IFileReadStreamOptions, IFileSystemProviderWithFileAtomicReadCapability } from 'vs/platform/files/common/files'; +import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, IFileOverwriteOptions, FileType, IFileWriteOptions, IFileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithFileReadStreamCapability, IFileReadStreamOptions, IFileSystemProviderWithFileAtomicReadCapability, IFileSystemProviderWithFileFolderCopyCapability } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; import { newWriteableStream, ReadableStreamEvents } from 'vs/base/common/stream'; @@ -20,7 +20,8 @@ import { VSBuffer } from 'vs/base/common/buffer'; export class FileUserDataProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithFileReadStreamCapability, - IFileSystemProviderWithFileAtomicReadCapability { + IFileSystemProviderWithFileAtomicReadCapability, + IFileSystemProviderWithFileFolderCopyCapability { get capabilities() { return this.fileSystemProvider.capabilities & ~FileSystemProviderCapabilities.FileOpenReadWriteClose; } readonly onDidChangeCapabilities: Event = this.fileSystemProvider.onDidChangeCapabilities; @@ -32,7 +33,7 @@ export class FileUserDataProvider extends Disposable implements constructor( private readonly fileSystemScheme: string, - private readonly fileSystemProvider: IFileSystemProviderWithFileReadWriteCapability & (IFileSystemProviderWithFileReadStreamCapability | IFileSystemProviderWithFileAtomicReadCapability), + private readonly fileSystemProvider: IFileSystemProviderWithFileReadWriteCapability & (IFileSystemProviderWithFileReadStreamCapability | IFileSystemProviderWithFileAtomicReadCapability | IFileSystemProviderWithFileFolderCopyCapability), private readonly userDataScheme: string, private readonly logService: ILogService, ) { @@ -91,6 +92,13 @@ export class FileUserDataProvider extends Disposable implements return this.fileSystemProvider.delete(this.toFileSystemResource(resource), opts); } + copy(from: URI, to: URI, opts: IFileOverwriteOptions): Promise { + if (this.fileSystemProvider.copy) { + return this.fileSystemProvider.copy(this.toFileSystemResource(from), this.toFileSystemResource(to), opts); + } + throw new Error('copy not supported'); + } + private handleFileChanges(changes: readonly IFileChange[]): void { const userDataChanges: IFileChange[] = []; for (const change of changes) { diff --git a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts index f76714a1fca79..7df2c12094a2c 100644 --- a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts +++ b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts @@ -52,7 +52,7 @@ suite('FileUserDataProvider', () => { await testObject.createFolder(backupWorkspaceHomeOnDisk); environmentService = new TestEnvironmentService(userDataHomeOnDisk); - userDataProfilesService = new UserDataProfilesService(undefined, environmentService, logService); + userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, testObject, logService); fileUserDataProvider = new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService); disposables.add(fileUserDataProvider); diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index 18634094d25fa..3ccc42c74538e 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -3,16 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { coalesce } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; export interface IUserDataProfile { - readonly name: string | undefined; + readonly name: string; readonly location: URI; readonly globalStorageHome: URI; readonly settingsResource: URI; @@ -26,45 +28,92 @@ export const IUserDataProfilesService = createDecorator; readonly currentProfile: IUserDataProfile; + + createProfile(name: string): IUserDataProfile; + setProfile(name: string): Promise; + getAllProfiles(): Promise; + +} + +function reviveProfile(profile: IUserDataProfile, scheme: string): IUserDataProfile { + return { + name: profile.name, + location: URI.revive(profile.location).with({ scheme }), + globalStorageHome: URI.revive(profile.globalStorageHome).with({ scheme }), + settingsResource: URI.revive(profile.settingsResource).with({ scheme }), + keybindingsResource: URI.revive(profile.keybindingsResource).with({ scheme }), + tasksResource: URI.revive(profile.tasksResource).with({ scheme }), + snippetsHome: URI.revive(profile.snippetsHome).with({ scheme }), + extensionsResource: URI.revive(profile.extensionsResource)?.with({ scheme }) + }; } export class UserDataProfilesService extends Disposable implements IUserDataProfilesService { readonly _serviceBrand: undefined; - private _currentProfile: IUserDataProfile; + protected static DEFAULT_PROFILE_NAME = 'default'; + + protected _currentProfile: IUserDataProfile; get currentProfile(): IUserDataProfile { return this._currentProfile; } - readonly defaultProfile: IUserDataProfile; + readonly profilesHome: URI; + protected _defaultProfile: IUserDataProfile; + get defaultProfile(): IUserDataProfile { return this._defaultProfile; } private readonly _onDidChangeCurrentProfile = this._register(new Emitter()); readonly onDidChangeCurrentProfile = this._onDidChangeCurrentProfile.event; constructor( - profile: string | undefined, + defaultProfile: IUserDataProfile | undefined, + currentProfile: IUserDataProfile | undefined, @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ILogService logService: ILogService + @IFileService protected readonly fileService: IFileService, + @ILogService protected readonly logService: ILogService ) { super(); - this.defaultProfile = this.createProfile(undefined); - this._currentProfile = profile ? this.createProfile(profile) : this.defaultProfile; + this.profilesHome = joinPath(this.environmentService.userRoamingDataHome, 'profiles'); + this._defaultProfile = defaultProfile ? reviveProfile(defaultProfile, this.profilesHome.scheme) : this.createProfile(undefined); + this._currentProfile = currentProfile ? reviveProfile(currentProfile, this.profilesHome.scheme) : this._defaultProfile; } - private createProfile(name: string | undefined): IUserDataProfile { - const location = name ? joinPath(this.environmentService.userRoamingDataHome, 'profiles', name) : this.environmentService.userRoamingDataHome; + createProfile(name: string | undefined): IUserDataProfile { + const location = name && name !== UserDataProfilesService.DEFAULT_PROFILE_NAME ? joinPath(this.profilesHome, name) : this.environmentService.userRoamingDataHome; return { - name, + name: name ?? UserDataProfilesService.DEFAULT_PROFILE_NAME, location: location, globalStorageHome: joinPath(location, 'globalStorage'), settingsResource: joinPath(location, 'settings.json'), keybindingsResource: joinPath(location, 'keybindings.json'), tasksResource: joinPath(location, 'tasks.json'), snippetsHome: joinPath(location, 'snippets'), - extensionsResource: name ? joinPath(location, 'extensions') : undefined + extensionsResource: name ? joinPath(location, 'extensions.json') : undefined }; } + async getAllProfiles(): Promise { + try { + const stat = await this.fileService.resolve(this.profilesHome); + const profiles = coalesce(stat.children?.map(stat => stat.isDirectory ? this.createProfile(stat.name) : undefined) ?? []); + if (profiles.length) { + profiles.unshift(this._defaultProfile); + } + return profiles; + } catch (error) { + if ((error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) { + this.logService.error('Error while getting all profiles', error); + } + } + return []; + } + + protected createCurrentProfile(profile: string | undefined): IUserDataProfile { + return profile === UserDataProfilesService.DEFAULT_PROFILE_NAME ? this._defaultProfile : this.createProfile(profile); + } + + setProfile(name: string): Promise { throw new Error('Not implemented'); } } diff --git a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts new file mode 100644 index 0000000000000..38a07a7bf4991 --- /dev/null +++ b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IStateMainService } from 'vs/platform/state/electron-main/state'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; + +export class UserDataProfilesMainService extends UserDataProfilesService implements IUserDataProfilesService { + + private static CURRENT_PROFILE_KEY = 'currentUserDataProfile'; + + private stateMainService: IStateMainService | undefined; + + constructor( + @IEnvironmentService environmentService: IEnvironmentService, + @IFileService fileService: IFileService, + @ILogService logService: ILogService, + ) { + super(undefined, undefined, environmentService, fileService, logService); + } + + async init(stateMainService: IStateMainService): Promise { + this.stateMainService = stateMainService; + const profileName = this.stateMainService.getItem(UserDataProfilesMainService.CURRENT_PROFILE_KEY); + if (profileName) { + const profiles = await this.getAllProfiles(); + const profile = profiles.find(p => p.name === profileName); + if (profile || (profileName === UserDataProfilesMainService.DEFAULT_PROFILE_NAME && profiles.length > 1)) { + this._defaultProfile = this.createProfile(UserDataProfilesService.DEFAULT_PROFILE_NAME); + this._currentProfile = profileName === UserDataProfilesMainService.DEFAULT_PROFILE_NAME ? this._defaultProfile : profile ?? this._defaultProfile; + } else { + this.stateMainService?.removeItem(UserDataProfilesMainService.CURRENT_PROFILE_KEY); + } + } + } + + override async setProfile(name: string): Promise { + this.stateMainService?.setItem(UserDataProfilesMainService.CURRENT_PROFILE_KEY, name); + } + +} + diff --git a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts new file mode 100644 index 0000000000000..c28b57fe965a0 --- /dev/null +++ b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IUserDataProfile, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; + +export class UserDataProfilesNativeService extends UserDataProfilesService implements IUserDataProfilesService { + + constructor( + defaultProfile: IUserDataProfile, + currentProfile: IUserDataProfile, + private readonly channel: IChannel, + @IEnvironmentService environmentService: IEnvironmentService, + @IFileService fileService: IFileService, + @ILogService logService: ILogService, + ) { + super(defaultProfile, currentProfile, environmentService, fileService, logService); + } + + override setProfile(name: string): Promise { + return this.channel.call('setProfile', [name]); + } +} + diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index f61a75daf83cc..fcfaebfc810eb 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -66,8 +66,6 @@ export class UserDataSyncClient extends Disposable { sync: 'on', }); - const userDataProfilesService = this.instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, logService)); - this.instantiationService.stub(IProductService, { _serviceBrand: undefined, ...product, ...{ 'configurationSync.store': { @@ -84,6 +82,8 @@ export class UserDataSyncClient extends Disposable { fileService.registerProvider(Schemas.inMemory, new InMemoryFileSystemProvider()); this.instantiationService.stub(IFileService, fileService); + const userDataProfilesService = this.instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); + this.instantiationService.stub(IStorageService, this._register(new InMemoryStorageService())); const configurationService = this._register(new ConfigurationService(userDataProfilesService, fileService)); diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index ca963cadff92a..573228bcccf0e 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -13,6 +13,7 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { FileType } from 'vs/platform/files/common/files'; import { LogLevel } from 'vs/platform/log/common/log'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export const WindowMinimumSize = { @@ -266,7 +267,9 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native execPath: string; backupPath?: string; - profile?: string; + defaultProfile: IUserDataProfile; + currentProfile: IUserDataProfile; + homeDir: string; tmpDir: string; userDataDir: string; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 00d9a41019101..b1f3cde44375b 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1298,7 +1298,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // loading the window. backupPath: options.emptyWindowBackupInfo ? join(this.environmentMainService.backupHome, options.emptyWindowBackupInfo.backupFolder) : undefined, - profile: this.userDataProfilesService.currentProfile.name, + defaultProfile: this.userDataProfilesService.defaultProfile, + currentProfile: this.userDataProfilesService.currentProfile, + homeDir: this.environmentMainService.userHome.fsPath, tmpDir: this.environmentMainService.tmpDir.fsPath, userDataDir: this.environmentMainService.userDataPath, diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index 79e341bb9bfd0..4a0c84131d189 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -93,7 +93,7 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, this._register(new DiskFileSystemProvider(logService))); // User Data Profiles - const userDataProfilesService = this._register(new UserDataProfilesService(undefined, environmentService, logService)); + const userDataProfilesService = this._register(new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); services.set(IUserDataProfilesService, userDataProfilesService); // Configuration diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 406e51d6cdbcc..8a1a3cd1725ac 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -110,7 +110,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken fileService.registerProvider(Schemas.file, disposables.add(new DiskFileSystemProvider(logService))); // Configuration - const userDataProfilesService = new UserDataProfilesService(undefined, environmentService, logService); + const userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService); const configurationService = new ConfigurationService(userDataProfilesService, fileService); services.set(IConfigurationService, configurationService); configurationService.initialize(environmentService.machineSettingsResource); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index c94c38b2e4736..7ad65021c028a 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -43,7 +43,7 @@ import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; import { GestureEvent } from 'vs/base/browser/touch'; import { IPaneCompositePart, IPaneCompositeSelectorPart } from 'vs/workbench/browser/parts/paneCompositePart'; import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/profiles/common/profileStorageRegistry'; +import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/userDataProfile/common/userDataProfileStorageRegistry'; interface IPlaceholderViewContainer { readonly id: string; diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 453dd8c5f1162..08fa0f71883e4 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -45,7 +45,7 @@ import { IPaneCompositePart, IPaneCompositeSelectorPart } from 'vs/workbench/bro import { IPartOptions } from 'vs/workbench/browser/part'; import { StringSHA1 } from 'vs/base/common/hash'; import { URI } from 'vs/base/common/uri'; -import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/profiles/common/profileStorageRegistry'; +import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/userDataProfile/common/userDataProfileStorageRegistry'; interface ICachedPanel { id: string; diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts index f3ea7a8e5f69f..4d6c56e5b3220 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts @@ -9,7 +9,7 @@ import { hide, show, isAncestor } from 'vs/base/browser/dom'; import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; import { Emitter } from 'vs/base/common/event'; import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/profiles/common/profileStorageRegistry'; +import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/userDataProfile/common/userDataProfileStorageRegistry'; import { localize } from 'vs/nls'; export interface IStatusbarEntryPriority { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index b7fd242063f49..d2d8fe332d3b2 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -203,10 +203,6 @@ export class BrowserMain extends Disposable { const logService = new BufferLogService(getLogLevel(environmentService)); serviceCollection.set(ILogService, logService); - // User Data Profiles - const userDataProfilesService = new UserDataProfilesService(undefined, environmentService, logService); - serviceCollection.set(IUserDataProfilesService, userDataProfilesService); - // Remote const connectionToken = environmentService.options.connectionToken || getCookieValue(connectionTokenCookieName); const remoteAuthorityResolverService = new RemoteAuthorityResolverService(productService, connectionToken, this.configuration.resourceUriProvider); @@ -236,6 +232,10 @@ export class BrowserMain extends Disposable { serviceCollection.set(IWorkbenchFileService, fileService); await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath); + // User Data Profiles + const userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService); + serviceCollection.set(IUserDataProfilesService, userDataProfilesService); + // URI Identity const uriIdentityService = new UriIdentityService(fileService); serviceCollection.set(IUriIdentityService, uriIdentityService); diff --git a/src/vs/workbench/contrib/profiles/common/profiles.contribution.ts b/src/vs/workbench/contrib/profiles/common/profiles.contribution.ts deleted file mode 100644 index facfa51c4a3ba..0000000000000 --- a/src/vs/workbench/contrib/profiles/common/profiles.contribution.ts +++ /dev/null @@ -1,6 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import './profilesActions'; diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts new file mode 100644 index 0000000000000..afde476fee85a --- /dev/null +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; +import { PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import '../common/userDataProfileActions'; + +class UserDataProfileStatusBarEntryContribution implements IWorkbenchContribution { + + constructor( + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IStatusbarService private readonly statusBarService: IStatusbarService + ) { + this.updateStatus(); + } + + private async updateStatus(): Promise { + const profiles = await this.userDataProfilesService.getAllProfiles(); + if (profiles.length) { + this.statusBarService.addEntry({ + name: this.userDataProfilesService.currentProfile.name!, + command: 'workbench.profiles.actions.switchProfile', + ariaLabel: localize('currentProfile', "Current Settings Profile is {0}", this.userDataProfilesService.currentProfile.name), + text: `${PROFILES_CATEGORY}: ${this.userDataProfilesService.currentProfile.name!}`, + }, 'status.userDataProfile', StatusbarAlignment.LEFT, 1); + } + } +} + +const workbenchRegistry = Registry.as(Extensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(UserDataProfileStatusBarEntryContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/profiles/common/profilesActions.ts b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts similarity index 52% rename from src/vs/workbench/contrib/profiles/common/profilesActions.ts rename to src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts index bae901d966d89..ea2911b945761 100644 --- a/src/vs/workbench/contrib/profiles/common/profilesActions.ts +++ b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts @@ -8,22 +8,109 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { localize } from 'vs/nls'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; -import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IFileService } from 'vs/platform/files/common/files'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { asJson, asText, IRequestService } from 'vs/platform/request/common/request'; -import { IProfile, isProfile, IWorkbenchProfileService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER } from 'vs/workbench/services/profiles/common/profile'; +import { IUserDataProfileTemplate, isProfile, IUserDataProfileManagementService, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; + +registerAction2(class SaveProfileAsAction extends Action2 { + constructor() { + super({ + id: 'workbench.profiles.actions.saveProfileAs', + title: { + value: localize('save profile as', "Save Settings Profile As..."), + original: 'Save Settings Profile As...' + }, + category: PROFILES_CATEGORY, + f1: true + }); + } + + async run(accessor: ServicesAccessor) { + const quickInputService = accessor.get(IQuickInputService); + const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); + const name = await quickInputService.input({ + placeHolder: localize('name', "Profile name"), + title: localize('save profile as', "Save Settings Profile As..."), + }); + if (name) { + await userDataProfileManagementService.createAndEnterProfile(name); + } + } +}); + +registerAction2(class SwitchProfileAction extends Action2 { + constructor() { + super({ + id: 'workbench.profiles.actions.switchProfile', + title: { + value: localize('switch profile', "Switch Settings Profile"), + original: 'Switch Settings Profile' + }, + category: PROFILES_CATEGORY, + f1: true + }); + } + + async run(accessor: ServicesAccessor) { + const quickInputService = accessor.get(IQuickInputService); + const userDataProfilesService = accessor.get(IUserDataProfilesService); + const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); + + const profiles = await userDataProfilesService.getAllProfiles(); + if (profiles.length) { + const picks: IQuickPickItem[] = profiles.map(p => ({ + label: p.name!, + description: p.name === userDataProfilesService.currentProfile.name ? localize('current', "Current") : undefined, + })); + const pick = await quickInputService.pick(picks, { placeHolder: localize('pick profile', "Select Settings Profile") }); + if (pick) { + await userDataProfileManagementService.switchProfile(pick.label); + } + } + } +}); + +registerAction2(class RemoveProfileAction extends Action2 { + constructor() { + super({ + id: 'workbench.profiles.actions.removeProfile', + title: { + value: localize('remove profile', "Remove Settings Profile"), + original: 'Remove Settings Profile' + }, + category: PROFILES_CATEGORY, + f1: true + }); + } + + async run(accessor: ServicesAccessor) { + const quickInputService = accessor.get(IQuickInputService); + const userDataProfilesService = accessor.get(IUserDataProfilesService); + const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); + + const profiles = (await userDataProfilesService.getAllProfiles()).filter(p => p.name !== userDataProfilesService.currentProfile.name && p.name !== userDataProfilesService.defaultProfile.name); + if (profiles.length) { + const pick = await quickInputService.pick(profiles.map(p => ({ label: p.name! })), { placeHolder: localize('pick profile', "Select Settings Profile") }); + if (pick) { + await userDataProfileManagementService.removeProfile(pick.label); + } + } + } +}); registerAction2(class ExportProfileAction extends Action2 { constructor() { super({ id: 'workbench.profiles.actions.exportProfile', title: { - value: localize('export profile', "Export Settings as a Profile..."), - original: 'Export Settings as a Profile...' + value: localize('export profile', "Export Settings Profile as a Template..."), + original: 'Export Settings as a Profile as a Template...' }, category: PROFILES_CATEGORY, f1: true @@ -33,7 +120,7 @@ registerAction2(class ExportProfileAction extends Action2 { async run(accessor: ServicesAccessor) { const textFileService = accessor.get(ITextFileService); const fileDialogService = accessor.get(IFileDialogService); - const profileService = accessor.get(IWorkbenchProfileService); + const profileService = accessor.get(IUserDataProfileWorkbenchService); const notificationService = accessor.get(INotificationService); const profileLocation = await fileDialogService.showSaveDialog({ @@ -53,13 +140,13 @@ registerAction2(class ExportProfileAction extends Action2 { } }); -registerAction2(class ImportProfileAction extends Action2 { +registerAction2(class CreateProfileFromTemplateAction extends Action2 { constructor() { super({ - id: 'workbench.profiles.actions.importProfile', + id: 'workbench.profiles.actions.createProfileFromTemplate', title: { - value: localize('import profile', "Import Settings from a Profile..."), - original: 'Import Settings from a Profile...' + value: localize('create profile from template', "Create Settings Profile from Template..."), + original: 'Create Settings Profile from Template...' }, category: PROFILES_CATEGORY, f1: true @@ -71,15 +158,7 @@ registerAction2(class ImportProfileAction extends Action2 { const quickInputService = accessor.get(IQuickInputService); const fileService = accessor.get(IFileService); const requestService = accessor.get(IRequestService); - const profileService = accessor.get(IWorkbenchProfileService); - const dialogService = accessor.get(IDialogService); - - if (!(await dialogService.confirm({ - title: localize('import profile title', "Import Settings from a Profile"), - message: localize('confiirmation message', "This will replace your current settings. Are you sure you want to continue?"), - })).confirmed) { - return; - } + const userDataProfileMangementService = accessor.get(IUserDataProfileManagementService); const disposables = new DisposableStore(); const quickPick = disposables.add(quickInputService.createQuickPick()); @@ -98,14 +177,20 @@ registerAction2(class ImportProfileAction extends Action2 { quickPick.hide(); const profile = quickPick.selectedItems[0].description ? await this.getProfileFromURL(quickPick.value, requestService) : await this.getProfileFromFileSystem(fileDialogService, fileService); if (profile) { - await profileService.setProfile(profile); + const name = await quickInputService.input({ + placeHolder: localize('name', "Profile name"), + title: localize('save profile as', "Save Settings Profile As..."), + }); + if (name) { + await userDataProfileMangementService.createAndEnterProfileFromTemplate(name, profile); + } } })); disposables.add(quickPick.onDidHide(() => disposables.dispose())); quickPick.show(); } - private async getProfileFromFileSystem(fileDialogService: IFileDialogService, fileService: IFileService): Promise { + private async getProfileFromFileSystem(fileDialogService: IFileDialogService, fileService: IFileService): Promise { const profileLocation = await fileDialogService.showOpenDialog({ canSelectFolders: false, canSelectFiles: true, @@ -121,7 +206,7 @@ registerAction2(class ImportProfileAction extends Action2 { return isProfile(parsed) ? parsed : null; } - private async getProfileFromURL(url: string, requestService: IRequestService): Promise { + private async getProfileFromURL(url: string, requestService: IRequestService): Promise { const options = { type: 'GET', url }; const context = await requestService.request(options, CancellationToken.None); if (context.res.statusCode === 200) { diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 78ae758c31b84..855f26bfec06a 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -50,7 +50,8 @@ import { isCI, isMacintosh } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/electron-sandbox/diskFileSystemProvider'; import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfilesNativeService } from 'vs/platform/userDataProfile/electron-sandbox/userDataProfile'; export class DesktopMain extends Disposable { @@ -179,10 +180,6 @@ export class DesktopMain extends Disposable { logService.trace('workbench#open(): with configuration', safeStringify(this.configuration)); } - // User Data Profiles - const userDataProfilesService = new UserDataProfilesService(this.configuration.profile, environmentService, logService); - serviceCollection.set(IUserDataProfilesService, userDataProfilesService); - // Shared Process const sharedProcessService = new SharedProcessService(this.configuration.windowId, logService); serviceCollection.set(ISharedProcessService, sharedProcessService); @@ -235,6 +232,9 @@ export class DesktopMain extends Disposable { const uriIdentityService = new UriIdentityService(fileService); serviceCollection.set(IUriIdentityService, uriIdentityService); + // User Data Profiles + const userDataProfilesService = new UserDataProfilesNativeService(this.configuration.defaultProfile, this.configuration.currentProfile, mainProcessService.getChannel('userDataProfiles'), environmentService, fileService, logService); + serviceCollection.set(IUserDataProfilesService, userDataProfilesService); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts index b326d3902e2a4..7fa7d03109381 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts @@ -95,7 +95,7 @@ suite('ConfigurationEditingService', () => { instantiationService = workbenchInstantiationService(undefined, disposables); environmentService = TestEnvironmentService; instantiationService.stub(IEnvironmentService, environmentService); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, logService)); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null)); disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService)))); instantiationService.stub(IFileService, fileService); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 25d0421b21caa..fa0c9135c31b8 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -78,7 +78,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, environmentService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); await (testObject).initialize(convertToWorkspacePayload(folder)); }); @@ -118,7 +118,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, environmentService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); await (testObject).initialize(convertToWorkspacePayload(folder)); const actual = testObject.getWorkspaceFolder(joinPath(folder, 'a')); @@ -138,7 +138,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, environmentService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService())); await (testObject).initialize(convertToWorkspacePayload(folder)); @@ -185,7 +185,7 @@ suite('WorkspaceContextService - Workspace', () => { const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null)); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, environmentService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -243,7 +243,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, environmentService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); @@ -485,7 +485,7 @@ suite('WorkspaceService - Initialization', () => { environmentService = TestEnvironmentService; const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, logService)); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IFileService, fileService); @@ -736,7 +736,7 @@ suite('WorkspaceConfigurationService - Folder', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, logService)); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); @@ -1408,7 +1408,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, logService)); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IFileService, fileService); @@ -2070,7 +2070,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve(), needsCaching: () => false }; - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, logService)); + userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); diff --git a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts index 1124b843b01a4..78ea6e57b3152 100644 --- a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts @@ -400,7 +400,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten return result; } - async scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType): Promise { + async scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType): Promise { if (extensionType === ExtensionType.System) { const systemExtensions = await this.scanSystemExtensions(); return systemExtensions.find(e => e.location.toString() === extensionLocation.toString()) || null; @@ -409,6 +409,11 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten return userExtensions.find(e => e.location.toString() === extensionLocation.toString()) || null; } + async scanMetadata(extensionLocation: URI): Promise { + const extension = await this.scanExistingExtension(extensionLocation, ExtensionType.User); + return extension?.metadata; + } + async scanExtensionManifest(extensionLocation: URI): Promise { const packageJSONUri = joinPath(extensionLocation, 'package.json'); try { diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index 5c374e3e103b4..7e8d96d0bb6d8 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -152,11 +152,12 @@ export interface IWebExtensionsScannerService { scanSystemExtensions(): Promise; scanUserExtensions(options?: ScanOptions): Promise; scanExtensionsUnderDevelopment(): Promise; - scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType): Promise; + scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType): Promise; addExtension(location: URI, metadata?: Metadata): Promise; addExtensionFromGallery(galleryExtension: IGalleryExtension, metadata?: Metadata): Promise; removeExtension(identifier: IExtensionIdentifier, version?: string): Promise; + scanMetadata(extensionLocation: URI): Promise; scanExtensionManifest(extensionLocation: URI): Promise; } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index 46c26ac179502..e50218a6c6a3d 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -5,7 +5,7 @@ import { Event, EventMultiplexer } from 'vs/base/common/event'; import { - ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionsControlManifest, IGalleryMetadata, IExtensionGalleryService, InstallOptions, UninstallOptions, InstallVSIXOptions, InstallExtensionResult, ExtensionManagementError, ExtensionManagementErrorCode + ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionsControlManifest, IGalleryMetadata, IExtensionGalleryService, InstallOptions, UninstallOptions, InstallVSIXOptions, InstallExtensionResult, ExtensionManagementError, ExtensionManagementErrorCode, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; import { DidUninstallExtensionOnServerEvent, IExtensionManagementServer, IExtensionManagementServerService, InstallExtensionOnServerEvent, IWorkbenchExtensionManagementService, UninstallExtensionOnServerEvent } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionType, isLanguagePackExtension, IExtensionManifest, getWorkspaceSupportTypeMessage, TargetPlatform } from 'vs/platform/extensions/common/extensions'; @@ -485,5 +485,13 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return this._targetPlatformPromise; } + async getMetadata(extension: ILocalExtension): Promise { + const server = this.getServer(extension); + if (!server) { + return undefined; + } + return server.extensionManagementService.getMetadata(extension); + } + registerParticipant() { throw new Error('Not Supported'); } } diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts index 54558b2cdcca3..04f014323962a 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts @@ -69,6 +69,10 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe return this.installExtension(manifest, location, options); } + getMetadata(extension: ILocalExtension): Promise { + return this.webExtensionsScannerService.scanMetadata(extension.location); + } + protected override async getCompatibleVersion(extension: IGalleryExtension, fetchCompatibleVersion: boolean, includePreRelease: boolean): Promise { const compatibleExtension = await super.getCompatibleVersion(extension, fetchCompatibleVersion, includePreRelease); if (compatibleExtension) { diff --git a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts index 39466b0209b4c..7dc22a8528d35 100644 --- a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts +++ b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts @@ -36,7 +36,7 @@ suite('ExtensionStorageMigration', () => { fileService.registerProvider(ROOT.scheme, disposables.add(new InMemoryFileSystemProvider())); instantiationService.stub(IFileService, fileService); const environmentService = instantiationService.stub(IEnvironmentService, >{ userRoamingDataHome: ROOT, workspaceStorageHome }); - instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, new NullLogService())); + instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, new NullLogService())); instantiationService.stub(IExtensionStorageService, instantiationService.createInstance(ExtensionStorageService)); }); diff --git a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts index c0cb23b6e9c9a..75f565718ae67 100644 --- a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts @@ -64,7 +64,7 @@ suite('KeybindingsEditing', () => { const configService = new TestConfigurationService(); configService.setUserConfiguration('files', { 'eol': '\n' }); - userDataProfilesService = new UserDataProfilesService(undefined, environmentService, logService); + userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService); instantiationService = workbenchInstantiationService({ fileService: () => fileService, diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts new file mode 100644 index 0000000000000..27336f47e71b8 --- /dev/null +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts @@ -0,0 +1,157 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from 'vs/base/common/buffer'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { basename, joinPath } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { ILocalExtension, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { IFileService } from 'vs/platform/files/common/files'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IExtensionManagementServerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { CreationOptions, IUserDataProfileManagementService, IUserDataProfileTemplate, PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; + +const DefaultOptions: CreationOptions = { + settings: true, + keybindings: true, + tasks: true, + snippets: true, + extensions: true, + uiState: true +}; + +export class UserDataProfileManagementService extends Disposable implements IUserDataProfileManagementService { + readonly _serviceBrand: undefined; + + constructor( + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IFileService private readonly fileService: IFileService, + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IWorkbenchExtensionManagementService private readonly extensionManagementService: IWorkbenchExtensionManagementService, + @IExtensionsProfileScannerService private readonly extensionsProfileScannerService: IExtensionsProfileScannerService, + @IHostService private readonly hostService: IHostService, + @IDialogService private readonly dialogService: IDialogService, + @IProgressService private readonly progressService: IProgressService, + @ILogService logService: ILogService + ) { + super(); + } + + async createAndEnterProfile(name: string, options: CreationOptions = DefaultOptions): Promise { + const promises: Promise[] = []; + const newProfile = this.userDataProfilesService.createProfile(name); + await this.fileService.createFolder(newProfile.location); + if (options?.uiState) { + promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.globalStorageHome, newProfile.globalStorageHome)); + } + if (options?.settings) { + promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.settingsResource, newProfile.settingsResource)); + } + if (options?.extensions && newProfile.extensionsResource) { + promises.push((async () => { + const extensionsProfileResource = this.userDataProfilesService.currentProfile.extensionsResource ?? await this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, basename(newProfile.extensionsResource!))); + this.fileService.copy(extensionsProfileResource, newProfile.extensionsResource!); + })()); + } + if (options?.keybindings) { + promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.keybindingsResource, newProfile.keybindingsResource)); + } + if (options?.tasks) { + promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.tasksResource, newProfile.tasksResource)); + } + if (options?.snippets) { + promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.snippetsHome, newProfile.snippetsHome)); + } + await Promise.allSettled(promises); + await this.doSwitchProfile(name); + } + + async removeProfile(name: string): Promise { + if (name === this.userDataProfilesService.defaultProfile.name) { + throw new Error(localize('cannotDeleteDefaultProfile', "Cannot delete the default profile")); + } + if (name === this.userDataProfilesService.currentProfile.name) { + throw new Error(localize('cannotDeleteCurrentProfile', "Cannot delete the current profile")); + } + const profiles = await this.userDataProfilesService.getAllProfiles(); + const profile = profiles.find(p => p.name === name); + if (!profile) { + throw new Error(`Profile ${name} does not exist`); + } + if (profiles.length === 2) { + await this.fileService.del(this.userDataProfilesService.profilesHome, { recursive: true }); + } else { + await this.fileService.del(profile.location, { recursive: true }); + } + } + + async switchProfile(name: string): Promise { + const profiles = await this.userDataProfilesService.getAllProfiles(); + const profile = profiles.find(p => p.name === name); + if (!profile) { + throw new Error(`Profile ${name} does not exist`); + } + await this.doSwitchProfile(name); + } + + async createAndEnterProfileFromTemplate(name: string, template: IUserDataProfileTemplate, options: CreationOptions = DefaultOptions): Promise { + await this.progressService.withProgress({ + location: ProgressLocation.Notification, + title: localize('profiles.creating', "{0}: Creating...", PROFILES_CATEGORY), + }, async progress => { + const promises: Promise[] = []; + const newProfile = this.userDataProfilesService.createProfile(name); + await this.fileService.createFolder(newProfile.location); + if (template.globalState) { + // todo: create global state + } + if (template.settings) { + promises.push(this.fileService.writeFile(newProfile.settingsResource, VSBuffer.fromString(template.settings))); + } + if (template.extensions && newProfile.extensionsResource) { + promises.push(this.fileService.writeFile(newProfile.extensionsResource, VSBuffer.fromString(template.extensions))); + } + await Promise.allSettled(promises); + }); + await this.doSwitchProfile(name); + } + + async reset(): Promise { + if (this.userDataProfilesService.currentProfile.name !== this.userDataProfilesService.defaultProfile.name) { + throw new Error('Please switch to default profile to reset'); + } + await this.fileService.del(this.userDataProfilesService.profilesHome); + } + + private async doSwitchProfile(name: string): Promise { + await this.userDataProfilesService.setProfile(name); + const result = await this.dialogService.confirm({ + type: 'info', + message: localize('restart message', "Switching a profile requires restarting VS Code."), + primaryButton: localize('restart button', "&&Restart"), + }); + if (result.confirmed) { + await this.hostService.restart(); + } + } + + private async createDefaultExtensionsProfile(extensionsProfileResource: URI): Promise { + const extensionManagementService = this.extensionManagementServerService.localExtensionManagementServer?.extensionManagementService ?? this.extensionManagementService; + const userExtensions = await extensionManagementService.getInstalled(ExtensionType.User); + const extensions: [ILocalExtension, Metadata | undefined][] = await Promise.all(userExtensions.map(async e => ([e, await this.extensionManagementService.getMetadata(e)]))); + await this.extensionsProfileScannerService.addExtensionsToProfile(extensions, extensionsProfileResource); + return extensionsProfileResource; + } +} + +registerSingleton(IUserDataProfileManagementService, UserDataProfileManagementService); diff --git a/src/vs/workbench/services/profiles/common/extensionsProfile.ts b/src/vs/workbench/services/userDataProfile/common/extensionsProfile.ts similarity index 98% rename from src/vs/workbench/services/profiles/common/extensionsProfile.ts rename to src/vs/workbench/services/userDataProfile/common/extensionsProfile.ts index 3e10dd0f9f58c..254e8d6bbdd8d 100644 --- a/src/vs/workbench/services/profiles/common/extensionsProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/extensionsProfile.ts @@ -9,7 +9,7 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { EnablementState, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { IResourceProfile } from 'vs/workbench/services/profiles/common/profile'; +import { IResourceProfile } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; interface IProfileExtension { identifier: IExtensionIdentifier; diff --git a/src/vs/workbench/services/profiles/common/globalStateProfile.ts b/src/vs/workbench/services/userDataProfile/common/globalStateProfile.ts similarity index 93% rename from src/vs/workbench/services/profiles/common/globalStateProfile.ts rename to src/vs/workbench/services/userDataProfile/common/globalStateProfile.ts index 7016a4c6136c7..c1e38038baf18 100644 --- a/src/vs/workbench/services/profiles/common/globalStateProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/globalStateProfile.ts @@ -7,8 +7,8 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { ILogService } from 'vs/platform/log/common/log'; import { Registry } from 'vs/platform/registry/common/platform'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IResourceProfile } from 'vs/workbench/services/profiles/common/profile'; -import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/profiles/common/profileStorageRegistry'; +import { IResourceProfile } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/userDataProfile/common/userDataProfileStorageRegistry'; interface IGlobalState { storage: IStringDictionary; diff --git a/src/vs/workbench/services/profiles/common/settingsProfile.ts b/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts similarity index 98% rename from src/vs/workbench/services/profiles/common/settingsProfile.ts rename to src/vs/workbench/services/userDataProfile/common/settingsProfile.ts index 8bfd681b11496..8ec179b556fe0 100644 --- a/src/vs/workbench/services/profiles/common/settingsProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts @@ -11,7 +11,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { removeComments, updateIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; -import { IResourceProfile, ProfileCreationOptions } from 'vs/workbench/services/profiles/common/profile'; +import { IResourceProfile, ProfileCreationOptions } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; interface ISettingsContent { settings: string; diff --git a/src/vs/workbench/services/profiles/common/profile.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts similarity index 55% rename from src/vs/workbench/services/profiles/common/profile.ts rename to src/vs/workbench/services/userDataProfile/common/userDataProfile.ts index a57331a9c56d2..3d43ca85d1fb8 100644 --- a/src/vs/workbench/services/profiles/common/profile.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts @@ -7,15 +7,36 @@ import { isUndefined } from 'vs/base/common/types'; import { localize } from 'vs/nls'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -export interface IProfile { +export type CreationOptions = { + settings?: boolean; + keybindings?: boolean; + tasks?: boolean; + snippets?: boolean; + extensions?: boolean; + uiState?: boolean; +}; + +export const IUserDataProfileManagementService = createDecorator('IUserDataProfileManagementService'); +export interface IUserDataProfileManagementService { + readonly _serviceBrand: undefined; + + createAndEnterProfile(name: string, options?: CreationOptions): Promise; + createAndEnterProfileFromTemplate(name: string, template: IUserDataProfileTemplate, options?: CreationOptions): Promise; + removeProfile(name: string): Promise; + switchProfile(name: string): Promise; + + reset(): Promise; +} + +export interface IUserDataProfileTemplate { readonly name?: string; readonly settings?: string; readonly globalState?: string; readonly extensions?: string; } -export function isProfile(thing: any): thing is IProfile { - const candidate = thing as IProfile | undefined; +export function isProfile(thing: any): thing is IUserDataProfileTemplate { + const candidate = thing as IUserDataProfileTemplate | undefined; return !!(candidate && typeof candidate === 'object' && (isUndefined(candidate.name) || typeof candidate.name === 'string') @@ -26,12 +47,12 @@ export function isProfile(thing: any): thing is IProfile { export type ProfileCreationOptions = { readonly skipComments: boolean }; -export const IWorkbenchProfileService = createDecorator('IWorkbenchProfileService'); -export interface IWorkbenchProfileService { +export const IUserDataProfileWorkbenchService = createDecorator('IUserDataProfileWorkbenchService'); +export interface IUserDataProfileWorkbenchService { readonly _serviceBrand: undefined; - createProfile(options?: ProfileCreationOptions): Promise; - setProfile(profile: IProfile): Promise; + createProfile(options?: ProfileCreationOptions): Promise; + setProfile(profile: IUserDataProfileTemplate): Promise; } export interface IResourceProfile { diff --git a/src/vs/workbench/services/profiles/common/profileStorageRegistry.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfileStorageRegistry.ts similarity index 100% rename from src/vs/workbench/services/profiles/common/profileStorageRegistry.ts rename to src/vs/workbench/services/userDataProfile/common/userDataProfileStorageRegistry.ts diff --git a/src/vs/workbench/services/profiles/common/profileService.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfileWorkbenchService.ts similarity index 76% rename from src/vs/workbench/services/profiles/common/profileService.ts rename to src/vs/workbench/services/userDataProfile/common/userDataProfileWorkbenchService.ts index 053b9e59bcf52..e4aec98fb8416 100644 --- a/src/vs/workbench/services/profiles/common/profileService.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfileWorkbenchService.ts @@ -8,12 +8,12 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { ExtensionsProfile } from 'vs/workbench/services/profiles/common/extensionsProfile'; -import { GlobalStateProfile } from 'vs/workbench/services/profiles/common/globalStateProfile'; -import { IProfile, IWorkbenchProfileService, PROFILES_CATEGORY } from 'vs/workbench/services/profiles/common/profile'; -import { SettingsProfile } from 'vs/workbench/services/profiles/common/settingsProfile'; +import { ExtensionsProfile } from 'vs/workbench/services/userDataProfile/common/extensionsProfile'; +import { GlobalStateProfile } from 'vs/workbench/services/userDataProfile/common/globalStateProfile'; +import { IUserDataProfileTemplate, IUserDataProfileWorkbenchService, PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { SettingsProfile } from 'vs/workbench/services/userDataProfile/common/settingsProfile'; -export class WorkbenchProfileService implements IWorkbenchProfileService { +export class UserDataProfileWorkbenchService implements IUserDataProfileWorkbenchService { readonly _serviceBrand: undefined; @@ -31,7 +31,7 @@ export class WorkbenchProfileService implements IWorkbenchProfileService { this.extensionsProfile = instantiationService.createInstance(ExtensionsProfile); } - async createProfile(options?: { skipComments: boolean }): Promise { + async createProfile(options?: { skipComments: boolean }): Promise { const settings = await this.settingsProfile.getProfileContent(options); const globalState = await this.globalStateProfile.getProfileContent(); const extensions = await this.extensionsProfile.getProfileContent(); @@ -42,7 +42,7 @@ export class WorkbenchProfileService implements IWorkbenchProfileService { }; } - async setProfile(profile: IProfile): Promise { + async setProfile(profile: IUserDataProfileTemplate): Promise { await this.progressService.withProgress({ location: ProgressLocation.Notification, title: localize('profiles.applying', "{0}: Applying...", PROFILES_CATEGORY), @@ -62,4 +62,4 @@ export class WorkbenchProfileService implements IWorkbenchProfileService { } -registerSingleton(IWorkbenchProfileService, WorkbenchProfileService); +registerSingleton(IUserDataProfileWorkbenchService, UserDataProfileWorkbenchService); diff --git a/src/vs/workbench/services/views/browser/viewDescriptorService.ts b/src/vs/workbench/services/views/browser/viewDescriptorService.ts index 4e84c1e579613..5ef98a4e93215 100644 --- a/src/vs/workbench/services/views/browser/viewDescriptorService.ts +++ b/src/vs/workbench/services/views/browser/viewDescriptorService.ts @@ -19,7 +19,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { getViewsStateStorageId, ViewContainerModel } from 'vs/workbench/services/views/common/viewContainerModel'; import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions'; import { localize } from 'vs/nls'; -import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/profiles/common/profileStorageRegistry'; +import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/userDataProfile/common/userDataProfileStorageRegistry'; interface ICachedViewContainerInfo { containerId: string; diff --git a/src/vs/workbench/services/views/common/viewContainerModel.ts b/src/vs/workbench/services/views/common/viewContainerModel.ts index 5cb12926dc91a..8d1456d8b8f43 100644 --- a/src/vs/workbench/services/views/common/viewContainerModel.ts +++ b/src/vs/workbench/services/views/common/viewContainerModel.ts @@ -16,7 +16,7 @@ import { isUndefined, isUndefinedOrNull } from 'vs/base/common/types'; import { isEqual } from 'vs/base/common/resources'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IStringDictionary } from 'vs/base/common/collections'; -import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/profiles/common/profileStorageRegistry'; +import { Extensions, IProfileStorageRegistry } from 'vs/workbench/services/userDataProfile/common/userDataProfileStorageRegistry'; import { localize } from 'vs/nls'; export function getViewsStateStorageId(viewContainerStorageId: string): string { return `${viewContainerStorageId}.hidden`; } diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 5eb84a0315258..ec337f4428efe 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -243,7 +243,6 @@ export function workbenchInstantiationService( const environmentService = overrides?.environmentService ? overrides.environmentService(instantiationService) : TestEnvironmentService; instantiationService.stub(IEnvironmentService, environmentService); instantiationService.stub(IWorkbenchEnvironmentService, environmentService); - instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, new NullLogService())); const contextKeyService = overrides?.contextKeyService ? overrides.contextKeyService(instantiationService) : instantiationService.createInstance(MockContextKeyService); instantiationService.stub(IContextKeyService, contextKeyService); instantiationService.stub(IProgressService, new TestProgressService()); @@ -282,6 +281,7 @@ export function workbenchInstantiationService( instantiationService.stub(IModelService, disposables.add(instantiationService.createInstance(ModelService))); const fileService = overrides?.fileService ? overrides.fileService(instantiationService) : new TestFileService(); instantiationService.stub(IFileService, fileService); + instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, new NullLogService())); instantiationService.stub(IUriIdentityService, new UriIdentityService(fileService)); instantiationService.stub(IWorkingCopyBackupService, new TestWorkingCopyBackupService()); instantiationService.stub(ITelemetryService, NullTelemetryService); diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index a0988c023e99b..a98d8000e8c6c 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -48,10 +48,24 @@ import { IElevatedFileService } from 'vs/workbench/services/files/common/elevate import { IDecorationsService } from 'vs/workbench/services/decorations/common/decorations'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { joinPath } from 'vs/base/common/resources'; const args = parseArgs(process.argv, OPTIONS); +const homeDir = homedir(); +const NULL_PROFILE: IUserDataProfile = { + name: '', + location: URI.file(homeDir), + settingsResource: joinPath(URI.file(homeDir), 'settings.json'), + globalStorageHome: joinPath(URI.file(homeDir), 'globalStorage'), + keybindingsResource: joinPath(URI.file(homeDir), 'keybindings.json'), + tasksResource: joinPath(URI.file(homeDir), 'tasks.json'), + snippetsHome: joinPath(URI.file(homeDir), 'snippets'), + extensionsResource: undefined +}; + export const TestNativeWindowConfiguration: INativeWindowConfiguration = { windowId: 0, machineId: 'testMachineId', @@ -64,9 +78,11 @@ export const TestNativeWindowConfiguration: INativeWindowConfiguration = { colorScheme: { dark: true, highContrast: false }, os: { release: release(), hostname: hostname() }, product, - homeDir: homedir(), + homeDir: homeDir, tmpDir: tmpdir(), userDataDir: getUserDataPath(args), + defaultProfile: NULL_PROFILE, + currentProfile: NULL_PROFILE, ...args }; @@ -270,7 +286,7 @@ export function workbenchInstantiationService(disposables = new DisposableStore( instantiationService.stub(INativeEnvironmentService, TestEnvironmentService); instantiationService.stub(IWorkbenchEnvironmentService, TestEnvironmentService); instantiationService.stub(INativeWorkbenchEnvironmentService, TestEnvironmentService); - instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, TestEnvironmentService, new NullLogService())); + instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, TestEnvironmentService, new FileService(new NullLogService()), new NullLogService())); return instantiationService; } diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 933cf07be3a0d..811481f397aba 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -83,7 +83,8 @@ import 'vs/workbench/services/extensionRecommendations/common/extensionIgnoredRe import 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; import 'vs/workbench/services/notification/common/notificationService'; import 'vs/workbench/services/userDataSync/common/userDataSyncUtil'; -import 'vs/workbench/services/profiles/common/profileService'; +import 'vs/workbench/services/userDataProfile/common/userDataProfileWorkbenchService'; +import 'vs/workbench/services/userDataProfile/browser/userDataProfileManagement'; import 'vs/workbench/services/remote/common/remoteExplorerService'; import 'vs/workbench/services/workingCopy/common/workingCopyService'; import 'vs/workbench/services/workingCopy/common/workingCopyFileService'; @@ -321,8 +322,8 @@ import 'vs/workbench/contrib/feedback/browser/feedback.contribution'; // User Data Sync import 'vs/workbench/contrib/userDataSync/browser/userDataSync.contribution'; -// Profiles -import 'vs/workbench/contrib/profiles/common/profiles.contribution'; +// User Data Profiles +import 'vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution'; // Code Actions import 'vs/workbench/contrib/codeActions/browser/codeActions.contribution'; From 17f17b429b44b55e158913159fc06279921a23e4 Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 22 May 2022 18:58:42 -0700 Subject: [PATCH 222/942] remove remaining kernel state. --- .../browser/viewParts/notebookKernelActionViewItem.ts | 10 ---------- .../services/actions/common/menusExtensionPoint.ts | 2 +- .../extensions/common/extensionsApiProposals.ts | 2 +- ....d.ts => vscode.proposed.notebookKernelSource.d.ts} | 0 4 files changed, 2 insertions(+), 12 deletions(-) rename src/vscode-dts/{vscode.proposed.notebookProxyController.d.ts => vscode.proposed.notebookKernelSource.d.ts} (100%) diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts index 8b8f92277e2e5..1e7b6980a23ee 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts @@ -6,7 +6,6 @@ import 'vs/css!./notebookKernelActionViewItem'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { Action, IAction } from 'vs/base/common/actions'; -import { DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { executingStateIcon, selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; @@ -18,7 +17,6 @@ import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookB export class NotebooKernelActionViewItem extends ActionViewItem { private _kernelLabel?: HTMLAnchorElement; - private _kernelDisposable: DisposableStore; constructor( actualAction: IAction, @@ -34,7 +32,6 @@ export class NotebooKernelActionViewItem extends ActionViewItem { this._register(_notebookKernelService.onDidChangeNotebookAffinity(this._update, this)); this._register(_notebookKernelService.onDidChangeSelectedNotebooks(this._update, this)); this._register(_notebookKernelService.onDidChangeSourceActions(this._update, this)); - this._kernelDisposable = this._register(new DisposableStore()); } override render(container: HTMLElement): void { @@ -96,7 +93,6 @@ export class NotebooKernelActionViewItem extends ActionViewItem { } private _updateActionFromKernelInfo(info: INotebookKernelMatchResult): void { - this._kernelDisposable.clear(); this._action.enabled = true; this._action.class = ThemeIcon.asClassName(selectKernelIcon); const selectedOrSuggested = info.selected ?? (info.suggestions.length === 1 ? info.suggestions[0] : undefined); @@ -107,12 +103,6 @@ export class NotebooKernelActionViewItem extends ActionViewItem { if (!info.selected) { // special UI for selected kernel? } - - this._kernelDisposable.add(selectedOrSuggested.onDidChange(e => { - if (e.state) { - this._action.label = this._generateKenrelLabel(selectedOrSuggested); - } - })); } else { // many kernels or no kernels this._action.label = localize('select', "Select Kernel"); diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index bceef1e438f93..adf3947667164 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -175,7 +175,7 @@ const apiMenus: IAPIMenu[] = [ key: 'notebook/kernelSource', id: MenuId.NotebookKernelSource, description: localize('notebook.kernelSource', "The contributed notebook kernel sources menu"), - proposed: 'notebookProxyController' + proposed: 'notebookKernelSource' }, { key: 'notebook/cell/title', diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index c562da5671fc5..d5ded005d7276 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -38,10 +38,10 @@ export const allApiProposals = Object.freeze({ notebookEditor: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditor.d.ts', notebookEditorDecorationType: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditorDecorationType.d.ts', notebookEditorEdit: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts', + notebookKernelSource: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookKernelSource.d.ts', notebookLiveShare: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts', notebookMessaging: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts', notebookMime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMime.d.ts', - notebookProxyController: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts', notebookWorkspaceEdit: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts', portsAttributes: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.portsAttributes.d.ts', quickPickSortByLabel: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts', diff --git a/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts b/src/vscode-dts/vscode.proposed.notebookKernelSource.d.ts similarity index 100% rename from src/vscode-dts/vscode.proposed.notebookProxyController.d.ts rename to src/vscode-dts/vscode.proposed.notebookKernelSource.d.ts From cb30029e0208451c0f7cf0801b4ada76521449d8 Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 22 May 2022 19:36:01 -0700 Subject: [PATCH 223/942] show install from marketplace only when there is no kernels and commands at all. --- .../contrib/editorStatusBar/editorStatusBar.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index bb1bd41aeb181..fab2077edd905 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -212,14 +212,6 @@ registerAction2(class extends Action2 { }); } - if (!all.length) { - // there is no kernel, show the install from marketplace - quickPickItems.push({ - id: 'install', - label: nls.localize('installKernels', "Install kernels from the marketplace"), - }); - } - const sourceActions = notebookKernelService.getSourceActions(); if (sourceActions.length) { quickPickItems.push({ @@ -238,6 +230,14 @@ registerAction2(class extends Action2 { }); } + if (!all.length && !sourceActions.length) { + // there is no kernel, show the install from marketplace + quickPickItems.push({ + id: 'install', + label: nls.localize('installKernels', "Install kernels from the marketplace"), + }); + } + const pick = await quickInputService.pick(quickPickItems, { placeHolder: selected ? nls.localize('prompt.placeholder.change', "Change kernel for '{0}'", labelService.getUriLabel(notebook.uri, { relative: true })) From 01e4dbd95c7133e5a955d24ab47c5e60b0e252b1 Mon Sep 17 00:00:00 2001 From: rebornix Date: Sun, 22 May 2022 20:01:02 -0700 Subject: [PATCH 224/942] hide explicit IAction. --- .../editorStatusBar/editorStatusBar.ts | 13 ++-- .../browser/notebookExecutionServiceImpl.ts | 2 +- .../browser/notebookKernelServiceImpl.ts | 65 +++++++++++++------ .../viewParts/notebookKernelActionViewItem.ts | 13 ++-- .../notebook/common/notebookKernelService.ts | 18 +++-- 5 files changed, 71 insertions(+), 40 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index fab2077edd905..20c8c594f12fe 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction } from 'vs/base/common/actions'; import { groupBy } from 'vs/base/common/arrays'; import { Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; @@ -29,7 +28,7 @@ import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/note import { configureKernelIcon, selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService, ISourceAction } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; @@ -164,7 +163,7 @@ registerAction2(class extends Action2 { } type KernelPick = IQuickPickItem & { kernel: INotebookKernel }; - type SourcePick = IQuickPickItem & { action: IAction }; + type SourcePick = IQuickPickItem & { action: ISourceAction }; const configButton: IQuickInputButton = { iconClass: ThemeIcon.asClassName(configureKernelIcon), @@ -219,11 +218,11 @@ registerAction2(class extends Action2 { // label: nls.localize('sourceActions', "") }); - sourceActions.forEach(action => { + sourceActions.forEach(sourceAction => { const res = { - action, + action: sourceAction, picked: false, - label: action.label, + label: sourceAction.action.label, }; quickPickItems.push(res); @@ -262,7 +261,7 @@ registerAction2(class extends Action2 { await this._showKernelExtension(paneCompositeService, notebook.viewType); } else if ('action' in pick) { // selected explicilty, it should trigger the execution? - notebookKernelService.runSourceAction(pick.action); + pick.action.runAction(); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts index 75b6b1a55dd8f..0be1459d36a07 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts @@ -85,7 +85,7 @@ export class NotebookExecutionService implements INotebookExecutionService, IDis // no kernel at all const sourceActions = this._notebookKernelService.getSourceActions(); if (sourceActions.length === 1) { - await this._notebookKernelService.runSourceAction(sourceActions[0]); + await sourceActions[0].runAction(); kernel = this._notebookKernelService.getSelectedOrSuggestedKernel(notebook); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts index 6e0151f222c2b..8466386bbdb8a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts @@ -6,7 +6,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { INotebookTextModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernel, ISelectedNotebooksChangeEvent, INotebookKernelMatchResult, INotebookKernelService, INotebookTextModelLike } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, ISelectedNotebooksChangeEvent, INotebookKernelMatchResult, INotebookKernelService, INotebookTextModelLike, ISourceAction } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { LRUCache, ResourceMap } from 'vs/base/common/map'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { URI } from 'vs/base/common/uri'; @@ -46,6 +46,34 @@ class NotebookTextModelLikeId { } } +class SourceAction extends Disposable implements ISourceAction { + execution: Promise | undefined; + private readonly _onDidChangeState = this._register(new Emitter()); + readonly onDidChangeState = this._onDidChangeState.event; + + constructor( + readonly action: IAction, + ) { + super(); + } + + async runAction() { + if (this.execution) { + return this.execution; + } + + this.execution = this._runAction(); + this._onDidChangeState.fire(); + await this.execution; + this.execution = undefined; + this._onDidChangeState.fire(); + } + + private async _runAction(): Promise { + await this.action.run(); + } +} + export class NotebookKernelService extends Disposable implements INotebookKernelService { declare _serviceBrand: undefined; @@ -61,7 +89,7 @@ export class NotebookKernelService extends Disposable implements INotebookKernel private readonly _onDidChangeNotebookAffinity = this._register(new Emitter()); private readonly _onDidChangeSourceActions = this._register(new Emitter()); private readonly _sourceMenu: IMenu; - private _sourceActions: IAction[]; + private _sourceActions: [ISourceAction, IDisposable][]; readonly onDidChangeSelectedNotebooks: Event = this._onDidChangeNotebookKernelBinding.event; readonly onDidAddKernel: Event = this._onDidAddKernel.event; @@ -111,25 +139,32 @@ export class NotebookKernelService extends Disposable implements INotebookKernel } private _initSourceActions() { - const loadActions = (menu: IMenu) => { + const loadActionsFromMenu = (menu: IMenu) => { const groups = menu.getActions({ shouldForwardArgs: true }); const actions: IAction[] = []; groups.forEach(group => { actions.push(...group[1]); }); - this._sourceActions = actions; + this._sourceActions = actions.map(action => { + const sourceAction = new SourceAction(action); + const stateChangeListener = sourceAction.onDidChangeState(() => { + this._onDidChangeSourceActions.fire(); + }); + return [sourceAction, stateChangeListener]; + }); this._onDidChangeSourceActions.fire(); }; this._register(this._sourceMenu.onDidChange(() => { - loadActions(this._sourceMenu); + loadActionsFromMenu(this._sourceMenu); })); - loadActions(this._sourceMenu); + loadActionsFromMenu(this._sourceMenu); } override dispose() { this._kernels.clear(); + this._sourceActions.forEach(a => a[1].dispose()); super.dispose(); } @@ -288,21 +323,11 @@ export class NotebookKernelService extends Disposable implements INotebookKernel this._onDidChangeNotebookAffinity.fire(); } - private _runningAction: IAction | undefined = undefined; - - getRunningSourceAction() { - return this._runningAction; - } - - getSourceActions(): IAction[] { - return this._sourceActions; + getRunningSourceActions() { + return this._sourceActions.filter(action => action[0].execution).map(action => action[0]); } - async runSourceAction(action: IAction): Promise { - this._runningAction = action; - this._onDidChangeSourceActions.fire(); - await action.run(); - this._runningAction = undefined; - this._onDidChangeSourceActions.fire(); + getSourceActions(): ISourceAction[] { + return this._sourceActions.map(a => a[0]); } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts index 1e7b6980a23ee..958a59df428ac 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts @@ -9,7 +9,7 @@ import { Action, IAction } from 'vs/base/common/actions'; import { localize } from 'vs/nls'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { executingStateIcon, selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; -import { INotebookKernel, INotebookKernelMatchResult, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelMatchResult, INotebookKernelService, ISourceAction } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { Event } from 'vs/base/common/event'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -59,9 +59,9 @@ export class NotebooKernelActionViewItem extends ActionViewItem { return; } - const runningAction = this._notebookKernelService.getRunningSourceAction(); - if (runningAction) { - return this._updateActionFromSourceAction(runningAction, true); + const runningActions = this._notebookKernelService.getRunningSourceActions(); + if (runningActions.length) { + return this._updateActionFromSourceAction(runningActions[0] /** TODO handle multiple actions state */, true); } const info = this._notebookKernelService.getMatchingKernel(notebook); @@ -72,10 +72,11 @@ export class NotebooKernelActionViewItem extends ActionViewItem { this._updateActionFromKernelInfo(info); } - private _updateActionFromSourceAction(sourceAction: IAction, running: boolean) { + private _updateActionFromSourceAction(sourceAction: ISourceAction, running: boolean) { + const action = sourceAction.action; this.action.class = running ? ThemeIcon.asClassName(ThemeIcon.modify(executingStateIcon, 'spin')) : ThemeIcon.asClassName(selectKernelIcon); this.updateClass(); - this._action.label = sourceAction.label; + this._action.label = action.label; this._action.enabled = true; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index 0068bd968e1c5..2ab657faf5b34 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -64,6 +64,13 @@ export interface INotebookProxyKernelChangeEvent extends INotebookKernelChangeEv connectionState?: true; } +export interface ISourceAction { + readonly action: IAction; + readonly onDidChangeState: Event; + execution: Promise | undefined; + runAction: () => Promise; +} + export interface INotebookTextModelLike { uri: URI; viewType: string } export const INotebookKernelService = createDecorator('INotebookKernelService'); @@ -75,8 +82,6 @@ export interface INotebookKernelService { readonly onDidRemoveKernel: Event; readonly onDidChangeSelectedNotebooks: Event; readonly onDidChangeNotebookAffinity: Event; - readonly onDidChangeSourceActions: Event; - registerKernel(kernel: INotebookKernel): IDisposable; getMatchingKernel(notebook: INotebookTextModelLike): INotebookKernelMatchResult; @@ -109,8 +114,9 @@ export interface INotebookKernelService { */ updateKernelNotebookAffinity(kernel: INotebookKernel, notebook: URI, preference: number | undefined): void; - getSourceActions(): IAction[]; - getRunningSourceAction(): IAction | undefined; - - runSourceAction(action: IAction): Promise; + //#region Kernel source actions + readonly onDidChangeSourceActions: Event; + getSourceActions(): ISourceAction[]; + getRunningSourceActions(): ISourceAction[]; + //#endregion } From d3af646dd02d1c73ecf15b4193781b771cce1e6a Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Mon, 23 May 2022 09:21:10 +0200 Subject: [PATCH 225/942] switch bot assignment fro debug back to weinand --- .github/classifier.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/classifier.json b/.github/classifier.json index 3b23ece3ce875..5b5625f6e93d6 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -23,7 +23,7 @@ "context-keys": {"assign": []}, "css-less-scss": {"assign": ["aeschli"]}, "custom-editors": {"assign": ["mjbvz"]}, - "debug": {"assign": ["roblourens"]}, + "debug": {"assign": ["weinand"]}, "dialogs": {"assign": ["sbatten"]}, "diff-editor": {"assign": ["alexdima"]}, "dropdown": {"assign": []}, From 801eaecfbce0b4bccaa46de85fb7513cf1f8cd7f Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 23 May 2022 09:28:08 +0200 Subject: [PATCH 226/942] change label to `Show Search Modes...` --- src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts b/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts index 77e1ebb32eb04..63dd190ea8298 100644 --- a/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts @@ -119,7 +119,7 @@ export class TitleMenuControl { const container = document.createElement('span'); container.classList.add('all-options'); parent.appendChild(container); - const action = new Action('all', localize('all', "Show Quick Pick Options..."), Codicon.chevronDown.classNames, true, () => { + const action = new Action('all', localize('all', "Show Search Modes..."), Codicon.chevronDown.classNames, true, () => { quickInputService.quickAccess.show('?'); }); const dropdown = new ActionViewItem(undefined, action, { icon: true, label: false, hoverDelegate }); From 8a27b48cff9d19c85b1d253e7046dd017d7df46a Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 23 May 2022 11:52:26 +0200 Subject: [PATCH 227/942] when temporarily showing/hiding inlay hints use the same "redraw range" that was used to compute inlays We would get all inlay hints but update the visible ranges. This duplicates all inlays that are outside the ranges, fixes https://github.com/microsoft/vscode/issues/149572 --- .../contrib/inlayHints/browser/inlayHintsController.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts b/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts index 9044a21c70d82..0d234f8a4e971 100644 --- a/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts +++ b/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts @@ -253,9 +253,9 @@ export class InlayHintsController implements IEditorContribution { const newRenderMode = e.altKey && e.ctrlKey ? altMode : defaultMode; if (newRenderMode !== this._activeRenderMode) { this._activeRenderMode = newRenderMode; - const ranges = this._getHintsRanges(); - const copies = this._copyInlayHintsWithCurrentAnchor(this._editor.getModel()); - this._updateHintsDecorators(ranges, copies); + const model = this._editor.getModel(); + const copies = this._copyInlayHintsWithCurrentAnchor(model); + this._updateHintsDecorators([model.getFullModelRange()], copies); scheduler.schedule(0); } })); @@ -393,7 +393,7 @@ export class InlayHintsController implements IEditorContribution { } // return inlay hints but with an anchor that reflects "updates" - // that happens after receiving them, e.g adding new lines before a hint + // that happened after receiving them, e.g adding new lines before a hint private _copyInlayHintsWithCurrentAnchor(model: ITextModel): InlayHintItem[] { const items = new Map(); for (const [id, obj] of this._decorationsMetadata) { From c3bddb2a4379ba203907f23c911504b639aeda17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 23 May 2022 16:01:40 +0200 Subject: [PATCH 228/942] adopt policy from configuration service --- src/vs/code/electron-main/app.ts | 2 +- .../electron-main/abstractUpdateService.ts | 6 ++-- .../electron-main/updateService.darwin.ts | 4 +-- .../electron-main/updateService.win32.ts | 28 ------------------- 4 files changed, 6 insertions(+), 34 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index a162ae7db70a2..2d7e71bef347b 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -1148,7 +1148,7 @@ export class CodeApplication extends Disposable { // Initialize update service const updateService = accessor.get(IUpdateService); if (updateService instanceof Win32UpdateService || updateService instanceof LinuxUpdateService || updateService instanceof DarwinUpdateService) { - await updateService.initialize(); + updateService.initialize(); } // Start to fetch shell environment (if needed) after window has opened diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index 86737bd165703..42b9e8cee7a43 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -57,7 +57,7 @@ export abstract class AbstractUpdateService implements IUpdateService { * optimization, to avoid using extra CPU cycles before first window open. * https://github.com/microsoft/vscode/issues/89784 */ - async initialize(): Promise { + initialize(): void { if (!this.environmentMainService.isBuilt) { return; // updates are never enabled when running out of sources } @@ -72,7 +72,7 @@ export abstract class AbstractUpdateService implements IUpdateService { return; } - const updateMode = await this.getUpdateMode(); + const updateMode = this.getUpdateMode(); const quality = this.getProductQuality(updateMode); if (!quality) { @@ -104,7 +104,7 @@ export abstract class AbstractUpdateService implements IUpdateService { } } - protected async getUpdateMode(): Promise<'none' | 'manual' | 'start' | 'default'> { + protected getUpdateMode(): 'none' | 'manual' | 'start' | 'default' { return getMigratedSettingValue<'none' | 'manual' | 'start' | 'default'>(this.configurationService, 'update.mode', 'update.channel'); } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index 688ea356ef175..0c008ea850493 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -38,8 +38,8 @@ export class DarwinUpdateService extends AbstractUpdateService { super(lifecycleMainService, configurationService, environmentMainService, requestService, logService, productService); } - override async initialize(): Promise { - await super.initialize(); + override initialize(): void { + super.initialize(); this.onRawError(this.onError, this, this.disposables); this.onRawUpdateAvailable(this.onUpdateAvailable, this, this.disposables); this.onRawUpdateDownloaded(this.onUpdateDownloaded, this, this.disposables); diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index e3661ce4bbc11..fa2490cdfca71 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -47,14 +47,6 @@ function getUpdateType(): UpdateType { return _updateType; } -function validateUpdateModeValue(value: string | undefined): 'none' | 'manual' | 'start' | 'default' | undefined { - if (value === 'none' || value === 'manual' || value === 'start' || value === 'default') { - return value; - } else { - return undefined; - } -} - export class Win32UpdateService extends AbstractUpdateService { private availableUpdate: IAvailableUpdate | undefined; @@ -79,26 +71,6 @@ export class Win32UpdateService extends AbstractUpdateService { super(lifecycleMainService, configurationService, environmentMainService, requestService, logService, productService); } - protected override async getUpdateMode(): Promise<'none' | 'manual' | 'start' | 'default'> { - if (this.productService.win32RegValueName) { - const policyKey = `Software\\Policies\\Microsoft\\${this.productService.win32RegValueName}`; - const [hklm, hkcu] = await Promise.all([ - this.nativeHostMainService.windowsGetStringRegKey(undefined, 'HKEY_LOCAL_MACHINE', policyKey, 'UpdateMode').then(validateUpdateModeValue), - this.nativeHostMainService.windowsGetStringRegKey(undefined, 'HKEY_CURRENT_USER', policyKey, 'UpdateMode').then(validateUpdateModeValue) - ]); - - if (hklm) { - this.logService.info(`update#getUpdateMode: 'UpdateMode' policy defined in 'HKLM\\${policyKey}':`, hklm); - return hklm; - } else if (hkcu) { - this.logService.info(`update#getUpdateMode: 'UpdateMode' policy defined in 'HKCU\\${policyKey}':`, hkcu); - return hkcu; - } - } - - return await super.getUpdateMode(); - } - protected buildUpdateFeedUrl(quality: string): string | undefined { let platform = 'win32'; From 553fc81c7ecea8af02f6580a6d1a3d35941dad3a Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 23 May 2022 16:06:50 +0200 Subject: [PATCH 229/942] Problems view - add toggle to view problems in a tree/table (#149943) --- src/vs/base/browser/ui/table/tableWidget.ts | 4 + .../contrib/markers/browser/constants.ts | 2 + .../markers/browser/markers.contribution.ts | 52 +- .../contrib/markers/browser/markers.ts | 2 + .../contrib/markers/browser/markersModel.ts | 14 + .../contrib/markers/browser/markersTable.ts | 527 +++++++++++++++ .../markers/browser/markersTreeViewer.ts | 38 +- .../contrib/markers/browser/markersView.ts | 605 +++++++++++------- .../contrib/markers/browser/media/markers.css | 84 +++ .../contrib/markers/browser/messages.ts | 1 + 10 files changed, 1094 insertions(+), 235 deletions(-) create mode 100644 src/vs/workbench/contrib/markers/browser/markersTable.ts diff --git a/src/vs/base/browser/ui/table/tableWidget.ts b/src/vs/base/browser/ui/table/tableWidget.ts index eb298a582c734..03f6f284785cb 100644 --- a/src/vs/base/browser/ui/table/tableWidget.ts +++ b/src/vs/base/browser/ui/table/tableWidget.ts @@ -341,6 +341,10 @@ export class Table implements ISpliceable, IThemable, IDisposable { return this.list.getFocusedElements(); } + getRelativeTop(index: number): number | null { + return this.list.getRelativeTop(index); + } + reveal(index: number, relativeTop?: number): void { this.list.reveal(index, relativeTop); } diff --git a/src/vs/workbench/contrib/markers/browser/constants.ts b/src/vs/workbench/contrib/markers/browser/constants.ts index 82f1acb1779c8..a883b3bf14891 100644 --- a/src/vs/workbench/contrib/markers/browser/constants.ts +++ b/src/vs/workbench/contrib/markers/browser/constants.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { MarkersViewMode } from 'vs/workbench/contrib/markers/browser/markersView'; export default { MARKERS_CONTAINER_ID: 'workbench.panel.markers', @@ -23,6 +24,7 @@ export default { MARKER_SHOW_QUICK_FIX: 'problems.action.showQuickFixes', TOGGLE_MARKERS_VIEW_ACTION_ID: 'workbench.actions.view.toggleProblems', + MarkersViewModeContextKey: new RawContextKey('problemsViewMode', MarkersViewMode.Tree), MarkersViewSmallLayoutContextKey: new RawContextKey(`problemsView.smallLayout`, false), MarkersTreeVisibilityContextKey: new RawContextKey('problemsVisibility', false), MarkerFocusContextKey: new RawContextKey('problemFocus', false), diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index b17ef050f9423..ffd153246576a 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -11,7 +11,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; import { Marker, RelatedInformation, ResourceMarkers } from 'vs/workbench/contrib/markers/browser/markersModel'; -import { MarkersView } from 'vs/workbench/contrib/markers/browser/markersView'; +import { MarkersView, MarkersViewMode } from 'vs/workbench/contrib/markers/browser/markersView'; import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; @@ -97,6 +97,12 @@ Registry.as(Extensions.Configuration).registerConfigurat 'type': 'boolean', 'default': true }, + 'problems.defaultViewMode': { + 'description': Messages.PROBLEMS_PANEL_CONFIGURATION_VIEW_MODE, + 'type': 'string', + 'default': 'tree', + 'enum': ['table', 'tree'], + }, 'problems.showCurrentInStatus': { 'description': Messages.PROBLEMS_PANEL_CONFIGURATION_SHOW_CURRENT_STATUS, 'type': 'boolean', @@ -148,6 +154,48 @@ const workbenchRegistry = Registry.as(Workbench workbenchRegistry.registerWorkbenchContribution(ActivityUpdater, LifecyclePhase.Restored); // actions +registerAction2(class extends ViewAction { + constructor() { + super({ + id: `workbench.actions.table.${Constants.MARKERS_VIEW_ID}.viewAsTree`, + title: localize('viewAsTree', "View as Tree"), + menu: { + id: MenuId.ViewTitle, + when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Constants.MARKERS_VIEW_ID), Constants.MarkersViewModeContextKey.isEqualTo(MarkersViewMode.Table)), + group: 'navigation', + order: 3 + }, + icon: Codicon.listTree, + viewId: Constants.MARKERS_VIEW_ID + }); + } + + async runInView(serviceAccessor: ServicesAccessor, view: IMarkersView): Promise { + view.setViewMode(MarkersViewMode.Tree); + } +}); + +registerAction2(class extends ViewAction { + constructor() { + super({ + id: `workbench.actions.table.${Constants.MARKERS_VIEW_ID}.viewAsTable`, + title: localize('viewAsTable', "View as Table"), + menu: { + id: MenuId.ViewTitle, + when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Constants.MARKERS_VIEW_ID), Constants.MarkersViewModeContextKey.isEqualTo(MarkersViewMode.Tree)), + group: 'navigation', + order: 3 + }, + icon: Codicon.listFlat, + viewId: Constants.MARKERS_VIEW_ID + }); + } + + async runInView(serviceAccessor: ServicesAccessor, view: IMarkersView): Promise { + view.setViewMode(MarkersViewMode.Table); + } +}); + registerAction2(class extends Action2 { constructor() { super({ @@ -345,7 +393,7 @@ registerAction2(class extends ViewAction { title: localize('collapseAll', "Collapse All"), menu: { id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', Constants.MARKERS_VIEW_ID), + when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Constants.MARKERS_VIEW_ID), Constants.MarkersViewModeContextKey.isEqualTo(MarkersViewMode.Tree)), group: 'navigation', order: 2, }, diff --git a/src/vs/workbench/contrib/markers/browser/markers.ts b/src/vs/workbench/contrib/markers/browser/markers.ts index a62b4a553a44a..c65df45418ac7 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.ts @@ -13,6 +13,7 @@ import { MarkersFilters } from 'vs/workbench/contrib/markers/browser/markersView import { Event } from 'vs/base/common/event'; import { IView } from 'vs/workbench/common/views'; import { MarkerElement, ResourceMarkers } from 'vs/workbench/contrib/markers/browser/markersModel'; +import { MarkersViewMode } from 'vs/workbench/contrib/markers/browser/markersView'; export interface IMarkersView extends IView { @@ -30,6 +31,7 @@ export interface IMarkersView extends IView { collapseAll(): void; setMultiline(multiline: boolean): void; + setViewMode(viewMode: MarkersViewMode): void; } export class ActivityUpdater extends Disposable implements IWorkbenchContribution { diff --git a/src/vs/workbench/contrib/markers/browser/markersModel.ts b/src/vs/workbench/contrib/markers/browser/markersModel.ts index c1155aa239113..d5c34bd8982d1 100644 --- a/src/vs/workbench/contrib/markers/browser/markersModel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersModel.ts @@ -13,6 +13,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Hasher } from 'vs/base/common/hash'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { splitLines } from 'vs/base/common/strings'; +import { IMatch } from 'vs/base/common/filters'; export type MarkerElement = ResourceMarkers | Marker | RelatedInformation; @@ -117,6 +118,19 @@ export class Marker { } } +export class MarkerTableItem extends Marker { + constructor( + marker: Marker, + readonly sourceMatches?: IMatch[], + readonly codeMatches?: IMatch[], + readonly messageMatches?: IMatch[], + readonly fileMatches?: IMatch[], + readonly ownerMatches?: IMatch[], + ) { + super(marker.id, marker.marker, marker.relatedInformation); + } +} + export class RelatedInformation { constructor( diff --git a/src/vs/workbench/contrib/markers/browser/markersTable.ts b/src/vs/workbench/contrib/markers/browser/markersTable.ts new file mode 100644 index 0000000000000..0b807b99d6d77 --- /dev/null +++ b/src/vs/workbench/contrib/markers/browser/markersTable.ts @@ -0,0 +1,527 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import * as DOM from 'vs/base/browser/dom'; +import * as network from 'vs/base/common/network'; +import { Event } from 'vs/base/common/event'; +import { ITableContextMenuEvent, ITableEvent, ITableRenderer, ITableVirtualDelegate } from 'vs/base/browser/ui/table/table'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IOpenEvent, IWorkbenchTableOptions, WorkbenchTable } from 'vs/platform/list/browser/listService'; +import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { Marker, MarkerTableItem, ResourceMarkers } from 'vs/workbench/contrib/markers/browser/markersModel'; +import { MarkerSeverity } from 'vs/platform/markers/common/markers'; +import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { FilterOptions } from 'vs/workbench/contrib/markers/browser/markersFilterOptions'; +import { Link } from 'vs/platform/opener/browser/link'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { MarkersViewModel } from 'vs/workbench/contrib/markers/browser/markersTreeViewer'; +import { IAction } from 'vs/base/common/actions'; +import { QuickFixAction, QuickFixActionViewItem } from 'vs/workbench/contrib/markers/browser/markersViewActions'; +import { DomEmitter } from 'vs/base/browser/event'; +import Messages from 'vs/workbench/contrib/markers/browser/messages'; +import { isUndefinedOrNull } from 'vs/base/common/types'; +import { IProblemsWidget } from 'vs/workbench/contrib/markers/browser/markersView'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; + +const $ = DOM.$; + +interface IMarkerIconColumnTemplateData { + readonly icon: HTMLElement; + readonly actionBar: ActionBar; +} + +interface IMarkerCodeColumnTemplateData { + readonly codeColumn: HTMLElement; + readonly sourceLabel: HighlightedLabel; + readonly codeLabel: HighlightedLabel; + readonly codeLink: Link; +} + +interface IMarkerFileColumnTemplateData { + readonly fileLabel: HighlightedLabel; + readonly positionLabel: HighlightedLabel; +} + + +interface IMarkerHighlightedLabelColumnTemplateData { + readonly highlightedLabel: HighlightedLabel; +} + +class MarkerSeverityColumnRenderer implements ITableRenderer{ + + static readonly TEMPLATE_ID = 'severity'; + + readonly templateId: string = MarkerSeverityColumnRenderer.TEMPLATE_ID; + + constructor( + private readonly markersViewModel: MarkersViewModel, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { } + + renderTemplate(container: HTMLElement): IMarkerIconColumnTemplateData { + const severityColumn = DOM.append(container, $('.severity')); + const icon = DOM.append(severityColumn, $('')); + + const actionBarColumn = DOM.append(container, $('.actions')); + const actionBar = new ActionBar(actionBarColumn, { + actionViewItemProvider: (action: IAction) => action.id === QuickFixAction.ID ? this.instantiationService.createInstance(QuickFixActionViewItem, action) : undefined, + animated: false + }); + + return { actionBar, icon }; + } + + renderElement(element: MarkerTableItem, index: number, templateData: IMarkerIconColumnTemplateData, height: number | undefined): void { + const toggleQuickFix = (enabled?: boolean) => { + if (!isUndefinedOrNull(enabled)) { + const container = DOM.findParentWithClass(templateData.icon, 'monaco-table-td')!; + container.classList.toggle('quickFix', enabled); + } + }; + + templateData.icon.className = `marker-icon codicon ${SeverityIcon.className(MarkerSeverity.toSeverity(element.marker.severity))}`; + + templateData.actionBar.clear(); + const viewModel = this.markersViewModel.getViewModel(element); + if (viewModel) { + const quickFixAction = viewModel.quickFixAction; + templateData.actionBar.push([quickFixAction], { icon: true, label: false }); + toggleQuickFix(viewModel.quickFixAction.enabled); + + quickFixAction.onDidChange(({ enabled }) => toggleQuickFix(enabled)); + quickFixAction.onShowQuickFixes(() => { + const quickFixActionViewItem = templateData.actionBar.viewItems[0]; + if (quickFixActionViewItem) { + quickFixActionViewItem.showQuickFixes(); + } + }); + } + } + + disposeTemplate(templateData: IMarkerIconColumnTemplateData): void { } +} + +class MarkerCodeColumnRenderer implements ITableRenderer { + static readonly TEMPLATE_ID = 'code'; + + readonly templateId: string = MarkerCodeColumnRenderer.TEMPLATE_ID; + + constructor( + @IOpenerService private readonly openerService: IOpenerService + ) { } + + renderTemplate(container: HTMLElement): IMarkerCodeColumnTemplateData { + const codeColumn = DOM.append(container, $('.code')); + + const sourceLabel = new HighlightedLabel(codeColumn); + sourceLabel.element.classList.add('source-label'); + + const codeLabel = new HighlightedLabel(codeColumn); + codeLabel.element.classList.add('code-label'); + + const codeLink = new Link(codeColumn, { href: '', label: '' }, {}, this.openerService); + + return { codeColumn, sourceLabel, codeLabel, codeLink }; + } + + renderElement(element: MarkerTableItem, index: number, templateData: IMarkerCodeColumnTemplateData, height: number | undefined): void { + if (element.marker.source && element.marker.code) { + templateData.codeColumn.classList.toggle('code-link', typeof element.marker.code !== 'string'); + + if (typeof element.marker.code === 'string') { + templateData.sourceLabel.set(element.marker.source, element.sourceMatches); + templateData.codeLabel.set(element.marker.code, element.codeMatches); + } else { + templateData.sourceLabel.set(element.marker.source, element.sourceMatches); + + const codeLinkLabel = new HighlightedLabel($('.code-link-label')); + codeLinkLabel.set(element.marker.code.value, element.codeMatches); + + templateData.codeLink.link = { + href: element.marker.code.target.toString(), + title: element.marker.code.target.toString(), + label: codeLinkLabel.element, + }; + } + } + } + + disposeTemplate(templateData: IMarkerCodeColumnTemplateData): void { } +} + +class MarkerMessageColumnRenderer implements ITableRenderer{ + + static readonly TEMPLATE_ID = 'message'; + + readonly templateId: string = MarkerMessageColumnRenderer.TEMPLATE_ID; + + renderTemplate(container: HTMLElement): IMarkerHighlightedLabelColumnTemplateData { + const fileColumn = DOM.append(container, $('.message')); + const highlightedLabel = new HighlightedLabel(fileColumn); + + return { highlightedLabel }; + } + + renderElement(element: MarkerTableItem, index: number, templateData: IMarkerHighlightedLabelColumnTemplateData, height: number | undefined): void { + templateData.highlightedLabel.set(element.marker.message, element.messageMatches); + } + + disposeTemplate(templateData: IMarkerHighlightedLabelColumnTemplateData): void { } +} + +class MarkerFileColumnRenderer implements ITableRenderer{ + + static readonly TEMPLATE_ID = 'file'; + + readonly templateId: string = MarkerFileColumnRenderer.TEMPLATE_ID; + + constructor( + @ILabelService private readonly labelService: ILabelService + ) { } + + renderTemplate(container: HTMLElement): IMarkerFileColumnTemplateData { + const fileColumn = DOM.append(container, $('.file')); + const fileLabel = new HighlightedLabel(fileColumn); + fileLabel.element.classList.add('file-label'); + const positionLabel = new HighlightedLabel(fileColumn); + positionLabel.element.classList.add('file-position'); + + return { fileLabel, positionLabel }; + } + + renderElement(element: MarkerTableItem, index: number, templateData: IMarkerFileColumnTemplateData, height: number | undefined): void { + templateData.fileLabel.set(this.labelService.getUriLabel(element.marker.resource, { relative: true }), element.fileMatches); + templateData.positionLabel.set(Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(element.marker.startLineNumber, element.marker.startColumn), undefined); + } + + disposeTemplate(templateData: IMarkerFileColumnTemplateData): void { } +} + +class MarkerOwnerColumnRenderer implements ITableRenderer{ + + static readonly TEMPLATE_ID = 'owner'; + + readonly templateId: string = MarkerOwnerColumnRenderer.TEMPLATE_ID; + + renderTemplate(container: HTMLElement): IMarkerHighlightedLabelColumnTemplateData { + const fileColumn = DOM.append(container, $('.owner')); + const highlightedLabel = new HighlightedLabel(fileColumn); + return { highlightedLabel }; + } + + renderElement(element: MarkerTableItem, index: number, templateData: IMarkerHighlightedLabelColumnTemplateData, height: number | undefined): void { + templateData.highlightedLabel.set(element.marker.owner, element.ownerMatches); + } + + disposeTemplate(templateData: IMarkerHighlightedLabelColumnTemplateData): void { } +} + +class MarkersTableVirtualDelegate implements ITableVirtualDelegate { + static readonly HEADER_ROW_HEIGHT = 24; + static readonly ROW_HEIGHT = 24; + readonly headerRowHeight = MarkersTableVirtualDelegate.HEADER_ROW_HEIGHT; + + getHeight(item: any) { + return MarkersTableVirtualDelegate.ROW_HEIGHT; + } +} + +export class MarkersTable extends Disposable implements IProblemsWidget { + + private _itemCount: number = 0; + private readonly table: WorkbenchTable; + + constructor( + private readonly container: HTMLElement, + private readonly markersViewModel: MarkersViewModel, + private resourceMarkers: ResourceMarkers[], + private filterOptions: FilterOptions, + options: IWorkbenchTableOptions, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ILabelService private readonly labelService: ILabelService, + ) { + super(); + + this.table = this.instantiationService.createInstance(WorkbenchTable, + 'Markers', + this.container, + new MarkersTableVirtualDelegate(), + [ + { + label: '', + tooltip: '', + weight: 0, + minimumWidth: 36, + maximumWidth: 36, + templateId: MarkerSeverityColumnRenderer.TEMPLATE_ID, + project(row: Marker): Marker { return row; } + }, + { + label: localize('codeColumnLabel', "Code"), + tooltip: '', + weight: 1, + minimumWidth: 100, + maximumWidth: 200, + templateId: MarkerCodeColumnRenderer.TEMPLATE_ID, + project(row: Marker): Marker { return row; } + }, + { + label: localize('messageColumnLabel', "Message"), + tooltip: '', + weight: 4, + templateId: MarkerMessageColumnRenderer.TEMPLATE_ID, + project(row: Marker): Marker { return row; } + }, + { + label: localize('fileColumnLabel', "File"), + tooltip: '', + weight: 2, + templateId: MarkerFileColumnRenderer.TEMPLATE_ID, + project(row: Marker): Marker { return row; } + }, + { + label: localize('sourceColumnLabel', "Source"), + tooltip: '', + weight: 1, + minimumWidth: 100, + maximumWidth: 200, + templateId: MarkerOwnerColumnRenderer.TEMPLATE_ID, + project(row: Marker): Marker { return row; } + } + ], + [ + this.instantiationService.createInstance(MarkerSeverityColumnRenderer, this.markersViewModel), + this.instantiationService.createInstance(MarkerCodeColumnRenderer), + this.instantiationService.createInstance(MarkerMessageColumnRenderer), + this.instantiationService.createInstance(MarkerFileColumnRenderer), + this.instantiationService.createInstance(MarkerOwnerColumnRenderer), + ], + options + ) as WorkbenchTable; + + const list = this.table.domNode.querySelector('.monaco-list-rows')! as HTMLElement; + + // mouseover/mouseleave event handlers + const onRowHover = Event.chain(this._register(new DomEmitter(list, 'mouseover')).event) + .map(e => DOM.findParentWithClass(e.target as HTMLElement, 'monaco-list-row', 'monaco-list-rows')) + .filter(((e: HTMLElement | null) => !!e) as any) + .map(e => parseInt(e.getAttribute('data-index')!)) + .event; + + const onListLeave = Event.map(this._register(new DomEmitter(list, 'mouseleave')).event, () => -1); + + const onRowHoverOrLeave = Event.latch(Event.any(onRowHover, onListLeave)); + const onRowPermanentHover = Event.debounce(onRowHoverOrLeave, (_, e) => e, 500); + + this._register(onRowPermanentHover(e => { + if (e !== -1 && this.table.row(e)) { + this.markersViewModel.onMarkerMouseHover(this.table.row(e)); + } + })); + } + + get contextKeyService(): IContextKeyService { + return this.table.contextKeyService; + } + + get onContextMenu(): Event> { + return this.table.onContextMenu; + } + + get onDidOpen(): Event> { + return this.table.onDidOpen; + } + + get onDidChangeFocus(): Event> { + return this.table.onDidChangeFocus; + } + + get onDidChangeSelection(): Event> { + return this.table.onDidChangeSelection; + } + + collapseMarkers(): void { } + + domFocus(): void { + this.table.domFocus(); + } + + filterMarkers(resourceMarkers: ResourceMarkers[], filterOptions: FilterOptions): void { + this.filterOptions = filterOptions; + this.reset(resourceMarkers); + } + + getFocus(): (MarkerTableItem | null)[] { + const focus = this.table.getFocus(); + return focus.length > 0 ? [this.table.row(focus[0])] : []; + } + + getHTMLElement(): HTMLElement { + return this.table.getHTMLElement(); + } + + getRelativeTop(marker: MarkerTableItem | null): number | null { + return marker ? this.table.getRelativeTop(this.table.indexOf(marker)) : null; + } + + getSelection(): (MarkerTableItem | null)[] { + const selection = this.table.getSelection(); + return selection.length > 0 ? [this.table.row(selection[0])] : []; + } + + getVisibleItemCount(): number { + return this._itemCount; + } + + isVisible(): boolean { + return !this.container.classList.contains('hidden'); + } + + layout(height: number, width: number): void { + this.table.layout(height, width); + } + + reset(resourceMarkers: ResourceMarkers[]): void { + this.resourceMarkers = resourceMarkers; + + const items: MarkerTableItem[] = []; + for (const resourceMarker of this.resourceMarkers) { + for (const marker of resourceMarker.markers) { + if (marker.resource.scheme === network.Schemas.walkThrough || marker.resource.scheme === network.Schemas.walkThroughSnippet) { + continue; + } + + // Exclude pattern + if (this.filterOptions.excludesMatcher.matches(marker.resource)) { + continue; + } + + // Include pattern + if (this.filterOptions.includesMatcher.matches(marker.resource)) { + items.push(new MarkerTableItem(marker)); + continue; + } + + // Severity filter + const matchesSeverity = this.filterOptions.showErrors && MarkerSeverity.Error === marker.marker.severity || + this.filterOptions.showWarnings && MarkerSeverity.Warning === marker.marker.severity || + this.filterOptions.showInfos && MarkerSeverity.Info === marker.marker.severity; + + if (!matchesSeverity) { + continue; + } + + // Text filter + if (this.filterOptions.textFilter.text) { + const sourceMatches = marker.marker.source ? FilterOptions._filter(this.filterOptions.textFilter.text, marker.marker.source) ?? undefined : undefined; + const codeMatches = marker.marker.code ? FilterOptions._filter(this.filterOptions.textFilter.text, typeof marker.marker.code === 'string' ? marker.marker.code : marker.marker.code.value) ?? undefined : undefined; + const messageMatches = FilterOptions._messageFilter(this.filterOptions.textFilter.text, marker.marker.message) ?? undefined; + const fileMatches = FilterOptions._messageFilter(this.filterOptions.textFilter.text, this.labelService.getUriLabel(marker.resource, { relative: true })) ?? undefined; + const ownerMatches = FilterOptions._messageFilter(this.filterOptions.textFilter.text, marker.marker.owner) ?? undefined; + + const matched = sourceMatches || codeMatches || messageMatches || fileMatches || ownerMatches; + if ((matched && !this.filterOptions.textFilter.negate) || (!matched && this.filterOptions.textFilter.negate)) { + items.push(new MarkerTableItem(marker, sourceMatches, codeMatches, messageMatches, fileMatches, ownerMatches)); + } + + continue; + } + + items.push(new MarkerTableItem(marker)); + } + } + this._itemCount = items.length; + this.table.splice(0, Number.POSITIVE_INFINITY, items.sort((a, b) => MarkerSeverity.compare(a.marker.severity, b.marker.severity))); + } + + revealMarkers(activeResource: ResourceMarkers | null, focus: boolean, lastSelectedRelativeTop: number): void { + if (activeResource) { + const activeResourceIndex = this.resourceMarkers.indexOf(activeResource); + + if (activeResourceIndex !== -1) { + if (this.hasSelectedMarkerFor(activeResource)) { + const tableSelection = this.table.getSelection(); + this.table.reveal(tableSelection[0], lastSelectedRelativeTop); + + if (focus) { + this.table.setFocus(tableSelection); + } + } else { + this.table.reveal(activeResourceIndex, 0); + + if (focus) { + this.table.setFocus([activeResourceIndex]); + this.table.setSelection([activeResourceIndex]); + } + } + } + } else if (focus) { + this.table.setSelection([]); + this.table.focusFirst(); + } + } + + setAriaLabel(label: string): void { + this.table.domNode.ariaLabel = label; + } + + setMarkerSelection(marker?: Marker): void { + if (this.isVisible()) { + if (marker) { + const index = this.findMarkerIndex(marker); + if (index !== -1) { + this.table.setFocus([index]); + this.table.setSelection([index]); + } + } else if (this.getSelection().length === 0 && this.getVisibleItemCount() > 0) { + this.table.setFocus([0]); + this.table.setSelection([0]); + } + } + } + + toggleVisibility(hide: boolean): void { + this.container.classList.toggle('hidden', hide); + } + + update(resourceMarkers: ResourceMarkers[]): void { + for (const resourceMarker of resourceMarkers) { + const index = this.resourceMarkers.indexOf(resourceMarker); + this.resourceMarkers.splice(index, 1, resourceMarker); + } + this.reset(this.resourceMarkers); + } + + updateMarker(marker: Marker): void { + this.table.rerender(); + } + + private findMarkerIndex(marker: Marker): number { + for (let index = 0; index < this.table.length; index++) { + if (this.table.row(index).marker === marker.marker) { + return index; + } + } + + return -1; + } + + private hasSelectedMarkerFor(resource: ResourceMarkers): boolean { + let selectedElement = this.getSelection(); + if (selectedElement && selectedElement.length > 0) { + if (selectedElement[0] instanceof Marker) { + if (resource.has((selectedElement[0]).marker.resource)) { + return true; + } + } + } + + return false; + } +} diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 5b9b0c78abfb4..f6e04799b34cd 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -10,7 +10,7 @@ import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; -import { ResourceMarkers, Marker, RelatedInformation, MarkerElement } from 'vs/workbench/contrib/markers/browser/markersModel'; +import { ResourceMarkers, Marker, RelatedInformation, MarkerElement, MarkerTableItem } from 'vs/workbench/contrib/markers/browser/markersModel'; import Messages from 'vs/workbench/contrib/markers/browser/messages'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; @@ -48,6 +48,9 @@ import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { Link } from 'vs/platform/opener/browser/link'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { MarkersViewMode } from 'vs/workbench/contrib/markers/browser/markersView'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import Constants from 'vs/workbench/contrib/markers/browser/constants'; interface IResourceMarkersTemplateData { resourceLabel: IResourceLabel; @@ -65,7 +68,7 @@ interface IRelatedInformationTemplateData { description: HighlightedLabel; } -export class MarkersTreeAccessibilityProvider implements IListAccessibilityProvider { +export class MarkersWidgetAccessibilityProvider implements IListAccessibilityProvider { constructor(@ILabelService private readonly labelService: ILabelService) { } @@ -73,12 +76,12 @@ export class MarkersTreeAccessibilityProvider implements IListAccessibilityProvi return localize('problemsView', "Problems View"); } - public getAriaLabel(element: MarkerElement): string | null { + public getAriaLabel(element: MarkerElement | MarkerTableItem): string | null { if (element instanceof ResourceMarkers) { const path = this.labelService.getUriLabel(element.resource, { relative: true }) || element.resource.fsPath; return Messages.MARKERS_TREE_ARIA_LABEL_RESOURCE(element.markers.length, element.name, paths.dirname(path)); } - if (element instanceof Marker) { + if (element instanceof Marker || element instanceof MarkerTableItem) { return Messages.MARKERS_TREE_ARIA_LABEL_MARKER(element); } if (element instanceof RelatedInformation) { @@ -693,6 +696,9 @@ export class MarkersViewModel extends Disposable { private readonly _onDidChange: Emitter = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; + private readonly _onDidChangeViewMode: Emitter = this._register(new Emitter()); + readonly onDidChangeViewMode: Event = this._onDidChangeViewMode.event; + private readonly markersViewStates: Map = new Map(); private readonly markersPerResource: Map = new Map(); @@ -700,13 +706,20 @@ export class MarkersViewModel extends Disposable { private hoveredMarker: Marker | null = null; private hoverDelayer: Delayer = new Delayer(300); + private viewModeContextKey: IContextKey; constructor( multiline: boolean = true, - @IInstantiationService private instantiationService: IInstantiationService + viewMode: MarkersViewMode = MarkersViewMode.Tree, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); this._multiline = multiline; + this._viewMode = viewMode; + + this.viewModeContextKey = Constants.MarkersViewModeContextKey.bindTo(this.contextKeyService); + this.viewModeContextKey.set(viewMode); } add(marker: Marker): void { @@ -789,6 +802,21 @@ export class MarkersViewModel extends Disposable { } } + private _viewMode: MarkersViewMode = MarkersViewMode.Tree; + get viewMode(): MarkersViewMode { + return this._viewMode; + } + + set viewMode(value: MarkersViewMode) { + if (this._viewMode === value) { + return; + } + + this._viewMode = value; + this._onDidChangeViewMode.fire(value); + this.viewModeContextKey.set(value); + } + override dispose(): void { this.markersViewStates.forEach(({ disposables }) => dispose(disposables)); this.markersViewStates.clear(); diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 17e438b53ce46..b20df6c492a34 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -11,7 +11,7 @@ import { IAction, Action, Separator } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import Constants from 'vs/workbench/contrib/markers/browser/constants'; -import { Marker, ResourceMarkers, RelatedInformation, MarkerChangesEvent, MarkersModel, compareMarkersByUri, MarkerElement } from 'vs/workbench/contrib/markers/browser/markersModel'; +import { Marker, ResourceMarkers, RelatedInformation, MarkerChangesEvent, MarkersModel, compareMarkersByUri, MarkerElement, MarkerTableItem } from 'vs/workbench/contrib/markers/browser/markersModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MarkersFilterActionViewItem, MarkersFilters, IMarkersFiltersChangeEvent } from 'vs/workbench/contrib/markers/browser/markersViewActions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -23,14 +23,14 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Iterable } from 'vs/base/common/iterator'; -import { ITreeElement, ITreeNode, ITreeContextMenuEvent, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { ITreeElement, ITreeNode, ITreeContextMenuEvent, ITreeRenderer, ITreeEvent } from 'vs/base/browser/ui/tree/tree'; import { Relay, Event, Emitter } from 'vs/base/common/event'; -import { WorkbenchObjectTree, IListService, IWorkbenchObjectTreeOptions } from 'vs/platform/list/browser/listService'; +import { WorkbenchObjectTree, IListService, IWorkbenchObjectTreeOptions, IOpenEvent } from 'vs/platform/list/browser/listService'; import { FilterOptions } from 'vs/workbench/contrib/markers/browser/markersFilterOptions'; import { IExpression } from 'vs/base/common/glob'; import { deepClone } from 'vs/base/common/objects'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, MarkersTreeAccessibilityProvider, MarkersViewModel } from 'vs/workbench/contrib/markers/browser/markersTreeViewer'; +import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, MarkersWidgetAccessibilityProvider, MarkersViewModel } from 'vs/workbench/contrib/markers/browser/markersTreeViewer'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ActionBar, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; @@ -40,7 +40,7 @@ import { ResourceLabels } from 'vs/workbench/browser/labels'; import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { MementoObject, Memento } from 'vs/workbench/common/memento'; -import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { KeyCode } from 'vs/base/common/keyCodes'; import { editorLightBulbForeground, editorLightBulbAutoFixForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -57,6 +57,8 @@ import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/ed import { IMarkersView } from 'vs/workbench/contrib/markers/browser/markers'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { ResourceListDnDHandler } from 'vs/workbench/browser/dnd'; +import { ITableContextMenuEvent, ITableEvent } from 'vs/base/browser/ui/table/table'; +import { MarkersTable } from 'vs/workbench/contrib/markers/browser/markersTable'; function createResourceMarkersIterator(resourceMarkers: ResourceMarkers): Iterable> { return Iterable.map(resourceMarkers.markers, m => { @@ -67,6 +69,38 @@ function createResourceMarkersIterator(resourceMarkers: ResourceMarkers): Iterab }); } +export const enum MarkersViewMode { + Table = 'table', + Tree = 'tree' +} + +export interface IProblemsWidget { + get contextKeyService(): IContextKeyService; + + get onContextMenu(): Event> | Event>; + get onDidChangeFocus(): Event> | Event>; + get onDidChangeSelection(): Event> | Event>; + get onDidOpen(): Event>; + + collapseMarkers(): void; + dispose(): void; + domFocus(): void; + filterMarkers(resourceMarkers: ResourceMarkers[], filterOptions: FilterOptions): void; + getFocus(): (MarkerElement | MarkerTableItem | null)[]; + getHTMLElement(): HTMLElement; + getRelativeTop(location: MarkerElement | MarkerTableItem | null): number | null; + getSelection(): (MarkerElement | MarkerTableItem | null)[]; + getVisibleItemCount(): number; + layout(height: number, width: number): void; + reset(resourceMarkers: ResourceMarkers[]): void; + revealMarkers(activeResource: ResourceMarkers | null, focus: boolean, lastSelectedRelativeTop: number): void; + setAriaLabel(label: string): void; + setMarkerSelection(marker?: Marker): void; + toggleVisibility(hide: boolean): void; + update(resourceMarkers: ResourceMarkers[]): void; + updateMarker(marker: Marker): void; +} + export class MarkersView extends ViewPane implements IMarkersView { private lastSelectedRelativeTop: number = 0; @@ -77,12 +111,18 @@ export class MarkersView extends ViewPane implements IMarkersView { private readonly filter: Filter; private readonly onVisibleDisposables = this._register(new DisposableStore()); - private tree: MarkersTree | undefined; + private widget!: IProblemsWidget; + private widgetDisposables = this._register(new DisposableStore()); + private widgetContainer!: HTMLElement; + private widgetIdentityProvider: IIdentityProvider; + private widgetAccessibilityProvider: MarkersWidgetAccessibilityProvider; private filterActionBar: ActionBar | undefined; private messageBoxContainer: HTMLElement | undefined; private ariaLabelElement: HTMLElement | undefined; readonly filters: MarkersFilters; + private currentHeight = 0; + private currentWidth = 0; private readonly panelState: MementoObject; private _onDidChangeFilterStats = this._register(new Emitter<{ total: number; filtered: number }>()); @@ -126,8 +166,12 @@ export class MarkersView extends ViewPane implements IMarkersView { this.panelState = new Memento(Constants.MARKERS_VIEW_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE, StorageTarget.USER); this.markersModel = this._register(instantiationService.createInstance(MarkersModel)); - this.markersViewModel = this._register(instantiationService.createInstance(MarkersViewModel, this.panelState['multiline'])); + this.markersViewModel = this._register(instantiationService.createInstance(MarkersViewModel, this.panelState['multiline'], this.panelState['viewMode'] ?? this.getDefaultViewMode())); this._register(this.onDidChangeVisibility(visible => this.onDidChangeMarkersViewVisibility(visible))); + this._register(this.markersViewModel.onDidChangeViewMode(_ => this.onDidChangeViewMode())); + + this.widgetAccessibilityProvider = instantiationService.createInstance(MarkersWidgetAccessibilityProvider); + this.widgetIdentityProvider = { getId(element: MarkerElement | MarkerTableItem) { return element.id; } }; this.setCurrentActiveEditor(); @@ -144,23 +188,38 @@ export class MarkersView extends ViewPane implements IMarkersView { activeFile: !!this.panelState['activeFile'], layout: new dom.Dimension(0, 0) })); + + // Update filter, whenever the "files.exclude" setting is changed + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (this.filters.excludedFiles && e.affectsConfiguration('files.exclude')) { + this.updateFilter(); + } + })); } public override renderBody(parent: HTMLElement): void { super.renderBody(parent); parent.classList.add('markers-panel'); + this._register(dom.addDisposableListener(parent, 'keydown', e => { + if (this.keybindingService.mightProducePrintableCharacter(new StandardKeyboardEvent(e))) { + this.focusFilter(); + } + })); - const container = dom.append(parent, dom.$('.markers-panel-container')); - - this.createFilterActionBar(container); - this.createArialLabelElement(container); - this.createMessageBox(container); - this.createTree(container); + const panelContainer = dom.append(parent, dom.$('.markers-panel-container')); - this.updateFilter(); + this.createArialLabelElement(panelContainer); + this.createFilterActionBar(panelContainer); this.filterActionBar!.push(new Action(`workbench.actions.treeView.${this.id}.filter`)); + + this.createMessageBox(panelContainer); + + this.widgetContainer = dom.append(panelContainer, dom.$('.widget-container')); + this.createWidget(this.widgetContainer); + + this.updateFilter(); this.renderContent(); } @@ -168,7 +227,7 @@ export class MarkersView extends ViewPane implements IMarkersView { return Messages.MARKERS_PANEL_TITLE_PROBLEMS; } - public override layoutBody(height: number, width: number): void { + public override layoutBody(height: number = this.currentHeight, width: number = this.currentWidth): void { super.layoutBody(height, width); const wasSmallLayout = this.smallLayout; this.smallLayout = width < 600 && height > 100; @@ -178,25 +237,26 @@ export class MarkersView extends ViewPane implements IMarkersView { } } const contentHeight = this.smallLayout ? height - 44 : height; - if (this.tree) { - this.tree.layout(contentHeight, width); - } if (this.messageBoxContainer) { this.messageBoxContainer.style.height = `${contentHeight}px`; } + this.widget.layout(contentHeight, width); this.filters.layout = new dom.Dimension(this.smallLayout ? width : width - 200, height); + + this.currentHeight = height; + this.currentWidth = width; } public override focus(): void { - if (this.tree && this.tree.getHTMLElement() === document.activeElement) { + if (this.widget.getHTMLElement() === document.activeElement) { return; } - if (this.hasNoProblems() && this.messageBoxContainer) { - this.messageBoxContainer.focus(); - } else if (this.tree) { - this.tree.domFocus(); - this.setTreeSelection(); + if (this.hasNoProblems()) { + this.messageBoxContainer!.focus(); + } else { + this.widget.domFocus(); + this.widget.setMarkerSelection(); } } @@ -217,7 +277,9 @@ export class MarkersView extends ViewPane implements IMarkersView { public openFileAtElement(element: any, preserveFocus: boolean, sideByside: boolean, pinned: boolean): boolean { const { resource, selection } = element instanceof Marker ? { resource: element.resource, selection: element.range } : - element instanceof RelatedInformation ? { resource: element.raw.resource, selection: element.raw } : { resource: null, selection: null }; + element instanceof RelatedInformation ? { resource: element.raw.resource, selection: element.raw } : + 'marker' in element ? { resource: element.marker.resource, selection: element.marker.range } : + { resource: null, selection: null }; if (resource && selection) { this.editorService.openEditor({ resource, @@ -242,57 +304,36 @@ export class MarkersView extends ViewPane implements IMarkersView { } private refreshPanel(markerOrChange?: Marker | MarkerChangesEvent): void { - if (this.isVisible() && this.tree) { - const hasSelection = this.tree.getSelection().length > 0; - this.cachedFilterStats = undefined; + if (this.isVisible()) { + const hasSelection = this.widget.getSelection().length > 0; if (markerOrChange) { if (markerOrChange instanceof Marker) { - this.tree.rerender(markerOrChange); + this.widget.updateMarker(markerOrChange); } else { if (markerOrChange.added.size || markerOrChange.removed.size) { - // Reset complete tree - this.resetTree(); + // Reset complete widget + this.resetWidget(); } else { // Update resource - for (const updated of markerOrChange.updated) { - this.tree.setChildren(updated, createResourceMarkersIterator(updated), { - /* Pass Identitiy Provider only when updating while the tree is visible */ - diffIdentityProvider: { - getId(element: MarkerElement): string { return element.id; } - } - }); - this.tree.rerender(updated); - } + this.widget.update([...markerOrChange.updated]); } } } else { - // Reset complete tree - this.resetTree(); + // Reset complete widget + this.resetWidget(); } - const { total, filtered } = this.getFilterStats(); - this.tree.toggleVisibility(total === 0 || filtered === 0); - this.renderMessage(); - this._onDidChangeFilterStats.fire(this.getFilterStats()); - if (hasSelection) { - this.setTreeSelection(); + this.widget.setMarkerSelection(); } - } - } - private setTreeSelection(): void { - if (this.tree && this.tree.isVisible() && this.tree.getSelection().length === 0) { - const firstVisibleElement = this.tree.firstVisibleElement; - const marker = firstVisibleElement ? - firstVisibleElement instanceof ResourceMarkers ? firstVisibleElement.markers[0] : - firstVisibleElement instanceof Marker ? firstVisibleElement : undefined - : undefined; - if (marker) { - this.tree.setFocus([marker]); - this.tree.setSelection([marker]); - } + this.cachedFilterStats = undefined; + const { total, filtered } = this.getFilterStats(); + this.toggleVisibility(total === 0 || filtered === 0); + this.renderMessage(); + + this._onDidChangeFilterStats.fire(this.getFilterStats()); } } @@ -300,37 +341,31 @@ export class MarkersView extends ViewPane implements IMarkersView { this.refreshPanel(marker); } - private resetTree(): void { - if (!this.tree) { - return; - } - let resourceMarkers: ResourceMarkers[] = []; - if (this.filters.activeFile) { - if (this.currentActiveResource) { - const activeResourceMarkers = this.markersModel.getResourceMarkers(this.currentActiveResource); - if (activeResourceMarkers) { - resourceMarkers = [activeResourceMarkers]; - } - } - } else { - resourceMarkers = this.markersModel.resourceMarkers; - } - this.tree.setChildren(null, Iterable.map(resourceMarkers, m => ({ element: m, children: createResourceMarkersIterator(m) }))); + private resetWidget(): void { + this.widget.reset(this.getResourceMarkers()); } private updateFilter() { - this.cachedFilterStats = undefined; this.filter.options = new FilterOptions(this.filters.filterText, this.getFilesExcludeExpressions(), this.filters.showWarnings, this.filters.showErrors, this.filters.showInfos, this.uriIdentityService); - if (this.tree) { - this.tree.refilter(); - } - this._onDidChangeFilterStats.fire(this.getFilterStats()); + this.widget.filterMarkers(this.getResourceMarkers(), this.filter.options); + this.cachedFilterStats = undefined; const { total, filtered } = this.getFilterStats(); - if (this.tree) { - this.tree.toggleVisibility(total === 0 || filtered === 0); - } + this.toggleVisibility(total === 0 || filtered === 0); this.renderMessage(); + + this._onDidChangeFilterStats.fire(this.getFilterStats()); + } + + private getDefaultViewMode(): MarkersViewMode { + switch (this.configurationService.getValue('problems.defaultViewMode')) { + case 'table': + return MarkersViewMode.Table; + case 'tree': + return MarkersViewMode.Tree; + default: + return MarkersViewMode.Tree; + } } private getFilesExcludeExpressions(): { root: URI; expression: IExpression }[] | IExpression { @@ -348,6 +383,22 @@ export class MarkersView extends ViewPane implements IMarkersView { return deepClone(this.configurationService.getValue('files.exclude', { resource })) || {}; } + private getResourceMarkers(): ResourceMarkers[] { + if (!this.filters.activeFile) { + return this.markersModel.resourceMarkers; + } + + let resourceMarkers: ResourceMarkers[] = []; + if (this.currentActiveResource) { + const activeResourceMarkers = this.markersModel.getResourceMarkers(this.currentActiveResource); + if (activeResourceMarkers) { + resourceMarkers = [activeResourceMarkers]; + } + } + + return resourceMarkers; + } + private createFilterActionBar(parent: HTMLElement): void { this.filterActionBar = this._register(new ActionBar(parent, { actionViewItemProvider: action => this.getActionViewItem(action) })); this.filterActionBar.getContainer().classList.add('markers-panel-filter-container'); @@ -364,10 +415,59 @@ export class MarkersView extends ViewPane implements IMarkersView { this.ariaLabelElement.setAttribute('id', 'markers-panel-arialabel'); } - private createTree(parent: HTMLElement): void { + private createWidget(parent: HTMLElement): void { + this.widget = this.markersViewModel.viewMode === MarkersViewMode.Table ? this.createTable(parent) : this.createTree(parent); + this.widgetDisposables.add(this.widget); + + const markerFocusContextKey = Constants.MarkerFocusContextKey.bindTo(this.widget.contextKeyService); + const relatedInformationFocusContextKey = Constants.RelatedInformationFocusContextKey.bindTo(this.widget.contextKeyService); + this.widgetDisposables.add(this.widget.onDidChangeFocus(focus => { + markerFocusContextKey.set(focus.elements.some(e => e instanceof Marker)); + relatedInformationFocusContextKey.set(focus.elements.some(e => e instanceof RelatedInformation)); + })); + + this.widgetDisposables.add(Event.debounce(this.widget.onDidOpen, (last, event) => event, 75, true)(options => { + this.openFileAtElement(options.element, !!options.editorOptions.preserveFocus, options.sideBySide, !!options.editorOptions.pinned); + })); + + this.widgetDisposables.add(Event.any(this.widget.onDidChangeSelection, this.widget.onDidChangeFocus)(() => { + const elements = [...this.widget.getSelection(), ...this.widget.getFocus()]; + for (const element of elements) { + if (element instanceof Marker) { + const viewModel = this.markersViewModel.getViewModel(element); + if (viewModel) { + viewModel.showLightBulb(); + } + } + } + })); + + this.widgetDisposables.add(this.widget.onContextMenu(this.onContextMenu, this)); + this.widgetDisposables.add(this.widget.onDidChangeSelection(this.onSelected, this)); + } + + private createTable(parent: HTMLElement): IProblemsWidget { + const table = this.instantiationService.createInstance(MarkersTable, + dom.append(parent, dom.$('.markers-table-container')), + this.markersViewModel, + this.getResourceMarkers(), + this.filter.options, + { + accessibilityProvider: this.widgetAccessibilityProvider, + horizontalScrolling: false, + identityProvider: this.widgetIdentityProvider, + multipleSelectionSupport: false, + selectionNavigation: true + }, + ); + + return table; + } + + private createTree(parent: HTMLElement): IProblemsWidget { const onDidChangeRenderNodeCount = new Relay>(); - const treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); + const treeLabels = this.instantiationService.createInstance(ResourceLabels, this); const virtualDelegate = new VirtualDelegate(this.markersViewModel); const renderers = [ @@ -375,23 +475,16 @@ export class MarkersView extends ViewPane implements IMarkersView { this.instantiationService.createInstance(MarkerRenderer, this.markersViewModel), this.instantiationService.createInstance(RelatedInformationRenderer) ]; - const accessibilityProvider = this.instantiationService.createInstance(MarkersTreeAccessibilityProvider); - - const identityProvider = { - getId(element: MarkerElement) { - return element.id; - } - }; - this.tree = this._register(this.instantiationService.createInstance(MarkersTree, + const tree = this.instantiationService.createInstance(MarkersTree, 'MarkersView', dom.append(parent, dom.$('.tree-container.show-file-icons')), virtualDelegate, renderers, { filter: this.filter, - accessibilityProvider, - identityProvider, + accessibilityProvider: this.widgetAccessibilityProvider, + identityProvider: this.widgetIdentityProvider, dnd: this.instantiationService.createInstance(ResourceListDnDHandler, (element) => { if (element instanceof ResourceMarkers) { return element.resource; @@ -411,65 +504,25 @@ export class MarkersView extends ViewPane implements IMarkersView { selectionNavigation: true, multipleSelectionSupport: true, }, - )); - - onDidChangeRenderNodeCount.input = this.tree.onDidChangeRenderNodeCount; - - const markerFocusContextKey = Constants.MarkerFocusContextKey.bindTo(this.tree.contextKeyService); - const relatedInformationFocusContextKey = Constants.RelatedInformationFocusContextKey.bindTo(this.tree.contextKeyService); - this._register(this.tree.onDidChangeFocus(focus => { - markerFocusContextKey.set(focus.elements.some(e => e instanceof Marker)); - relatedInformationFocusContextKey.set(focus.elements.some(e => e instanceof RelatedInformation)); - })); - - this._register(Event.debounce(this.tree.onDidOpen, (last, event) => event, 75, true)(options => { - this.openFileAtElement(options.element, !!options.editorOptions.preserveFocus, options.sideBySide, !!options.editorOptions.pinned); - })); - - this._register(this.tree.onContextMenu(this.onContextMenu, this)); - - this._register(this.configurationService.onDidChangeConfiguration(e => { - if (this.filters.excludedFiles && e.affectsConfiguration('files.exclude')) { - this.updateFilter(); - } - })); + ); - // move focus to input, whenever a key is pressed in the panel container - this._register(dom.addDisposableListener(parent, 'keydown', e => { - if (this.keybindingService.mightProducePrintableCharacter(new StandardKeyboardEvent(e))) { - this.focusFilter(); - } - })); + onDidChangeRenderNodeCount.input = tree.onDidChangeRenderNodeCount; - this._register(Event.any(this.tree.onDidChangeSelection, this.tree.onDidChangeFocus)(() => { - const elements = [...this.tree!.getSelection(), ...this.tree!.getFocus()]; - for (const element of elements) { - if (element instanceof Marker) { - const viewModel = this.markersViewModel.getViewModel(element); - if (viewModel) { - viewModel.showLightBulb(); - } - } - } - })); - - this._register(this.tree.onDidChangeSelection(() => this.onSelected())); + return tree; } collapseAll(): void { - if (this.tree) { - this.tree.collapseAll(); - this.tree.setSelection([]); - this.tree.setFocus([]); - this.tree.getHTMLElement().focus(); - this.tree.focusFirst(); - } + this.widget.collapseMarkers(); } setMultiline(multiline: boolean): void { this.markersViewModel.multiline = multiline; } + setViewMode(viewMode: MarkersViewMode): void { + this.markersViewModel.viewMode = viewMode; + } + private onDidChangeMarkersViewVisibility(visible: boolean): void { this.onVisibleDisposables.clear(); if (visible) { @@ -477,8 +530,6 @@ export class MarkersView extends ViewPane implements IMarkersView { this.onVisibleDisposables.add(disposable); } this.refreshPanel(); - } else if (this.tree) { - this.tree.toggleVisibility(true); } } @@ -546,6 +597,25 @@ export class MarkersView extends ViewPane implements IMarkersView { } } + private onDidChangeViewMode(): void { + if (this.widgetContainer && this.widget) { + this.widgetContainer.textContent = ''; + this.widgetDisposables.clear(); + } + + // Save selection + const selection = this.widget?.getSelection(); + + // Create new widget + this.createWidget(this.widgetContainer); + this.refreshPanel(); + + // Restore selection + if (selection && selection.length > 0 && (selection[0] instanceof Marker || selection[0] instanceof MarkerTableItem)) { + this.widget.setMarkerSelection(selection[0]); + } + } + private isCurrentResourceGotAddedToMarkersData(changedResources: URI[]) { const currentlyActiveResource = this.currentActiveResource; if (!currentlyActiveResource) { @@ -572,11 +642,9 @@ export class MarkersView extends ViewPane implements IMarkersView { } private onSelected(): void { - if (this.tree) { - let selection = this.tree.getSelection(); - if (selection && selection.length > 0) { - this.lastSelectedRelativeTop = this.tree!.getRelativeTop(selection[0]) || 0; - } + const selection = this.widget.getSelection(); + if (selection && selection.length > 0) { + this.lastSelectedRelativeTop = this.widget.getRelativeTop(selection[0]) || 0; } } @@ -587,10 +655,8 @@ export class MarkersView extends ViewPane implements IMarkersView { private renderContent(): void { this.cachedFilterStats = undefined; - this.resetTree(); - if (this.tree) { - this.tree.toggleVisibility(this.hasNoProblems()); - } + this.resetWidget(); + this.toggleVisibility(this.hasNoProblems()); this.renderMessage(); } @@ -663,9 +729,7 @@ export class MarkersView extends ViewPane implements IMarkersView { } private setAriaLabel(label: string): void { - if (this.tree) { - this.tree.ariaLabel = label; - } + this.widget.setAriaLabel(label); this.ariaLabelElement!.setAttribute('aria-label', label); } @@ -679,33 +743,13 @@ export class MarkersView extends ViewPane implements IMarkersView { private autoReveal(focus: boolean = false): void { // No need to auto reveal if active file filter is on - if (this.filters.activeFile || !this.tree) { + if (this.filters.activeFile) { return; } - let autoReveal = this.configurationService.getValue('problems.autoReveal'); + const autoReveal = this.configurationService.getValue('problems.autoReveal'); if (typeof autoReveal === 'boolean' && autoReveal) { - let currentActiveResource = this.getResourceForCurrentActiveResource(); - if (currentActiveResource) { - if (this.tree.hasElement(currentActiveResource)) { - if (!this.tree.isCollapsed(currentActiveResource) && this.hasSelectedMarkerFor(currentActiveResource)) { - this.tree.reveal(this.tree.getSelection()[0], this.lastSelectedRelativeTop); - if (focus) { - this.tree.setFocus(this.tree.getSelection()); - } - } else { - this.tree.expand(currentActiveResource); - this.tree.reveal(currentActiveResource, 0); - - if (focus) { - this.tree.setFocus([currentActiveResource]); - this.tree.setSelection([currentActiveResource]); - } - } - } - } else if (focus) { - this.tree.setSelection([]); - this.tree.focusFirst(); - } + const currentActiveResource = this.getResourceForCurrentActiveResource(); + this.widget.revealMarkers(currentActiveResource, focus, this.lastSelectedRelativeTop); } } @@ -713,29 +757,15 @@ export class MarkersView extends ViewPane implements IMarkersView { return this.currentActiveResource ? this.markersModel.getResourceMarkers(this.currentActiveResource) : null; } - private hasSelectedMarkerFor(resource: ResourceMarkers): boolean { - if (this.tree) { - let selectedElement = this.tree.getSelection(); - if (selectedElement && selectedElement.length > 0) { - if (selectedElement[0] instanceof Marker) { - if (resource.has((selectedElement[0]).marker.resource)) { - return true; - } - } - } - } - return false; - } - private updateRangeHighlights() { this.rangeHighlightDecorations.removeHighlightRange(); - if (this.tree && this.tree.getHTMLElement() === document.activeElement) { + if (this.widget.getHTMLElement() === document.activeElement) { this.highlightCurrentSelectedMarkerRange(); } } private highlightCurrentSelectedMarkerRange() { - const selections = this.tree ? this.tree.getSelection() : []; + const selections = this.widget.getSelection() ?? []; if (selections.length !== 1) { return; @@ -750,13 +780,18 @@ export class MarkersView extends ViewPane implements IMarkersView { this.rangeHighlightDecorations.highlightRange(selection); } - private onContextMenu(e: ITreeContextMenuEvent): void { + private onContextMenu(e: ITreeContextMenuEvent | ITableContextMenuEvent): void { + const element = e.element; + if (!element) { + return; + } + e.browserEvent.preventDefault(); e.browserEvent.stopPropagation(); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor!, - getActions: () => this.getMenuActions(e.element), + getActions: () => this.getMenuActions(element), getActionViewItem: (action) => { const keybinding = this.keybindingService.lookupKeybinding(action.id); if (keybinding) { @@ -766,7 +801,7 @@ export class MarkersView extends ViewPane implements IMarkersView { }, onHide: (wasCancelled?: boolean) => { if (wasCancelled) { - this.tree!.domFocus(); + this.widget.domFocus(); } } }); @@ -786,14 +821,14 @@ export class MarkersView extends ViewPane implements IMarkersView { } } - const menu = this.menuService.createMenu(MenuId.ProblemsPanelContext, this.tree!.contextKeyService); + const menu = this.menuService.createMenu(MenuId.ProblemsPanelContext, this.widget.contextKeyService); createAndFillInContextMenuActions(menu, undefined, result); menu.dispose(); return result; } public getFocusElement(): MarkerElement | undefined { - return this.tree?.getFocus()[0] || undefined; + return this.widget.getFocus()[0] ?? undefined; } public getFocusedSelectedElements(): MarkerElement[] | null { @@ -801,7 +836,7 @@ export class MarkersView extends ViewPane implements IMarkersView { if (!focus) { return null; } - const selection = this.tree!.getSelection(); + const selection = this.widget.getSelection(); if (selection.includes(focus)) { const result: MarkerElement[] = []; for (const selected of selection) { @@ -828,27 +863,18 @@ export class MarkersView extends ViewPane implements IMarkersView { getFilterStats(): { total: number; filtered: number } { if (!this.cachedFilterStats) { - this.cachedFilterStats = this.computeFilterStats(); + this.cachedFilterStats = { + total: this.markersModel.total, + filtered: this.widget?.getVisibleItemCount() ?? 0 + }; } return this.cachedFilterStats; } - private computeFilterStats(): { total: number; filtered: number } { - let filtered = 0; - if (this.tree) { - const root = this.tree.getNode(); - - for (const resourceMarkerNode of root.children) { - for (const markerNode of resourceMarkerNode.children) { - if (resourceMarkerNode.visible && markerNode.visible) { - filtered++; - } - } - } - } - - return { total: this.markersModel.total, filtered }; + private toggleVisibility(hide: boolean): void { + this.widget.toggleVisibility(hide); + this.layoutBody(); } override saveState(): void { @@ -860,6 +886,7 @@ export class MarkersView extends ViewPane implements IMarkersView { this.panelState['useFilesExclude'] = this.filters.excludedFiles; this.panelState['activeFile'] = this.filters.activeFile; this.panelState['multiline'] = this.markersViewModel.multiline; + this.panelState['viewMode'] = this.markersViewModel.viewMode; super.saveState(); } @@ -870,13 +897,13 @@ export class MarkersView extends ViewPane implements IMarkersView { } -class MarkersTree extends WorkbenchObjectTree { +class MarkersTree extends WorkbenchObjectTree implements IProblemsWidget { private readonly visibilityContextKey: IContextKey; constructor( user: string, - readonly container: HTMLElement, + private readonly container: HTMLElement, delegate: IListVirtualDelegate, renderers: ITreeRenderer[], options: IWorkbenchObjectTreeOptions, @@ -891,9 +918,35 @@ class MarkersTree extends WorkbenchObjectTree { this.visibilityContextKey = Constants.MarkersTreeVisibilityContextKey.bindTo(contextKeyService); } - override layout(height: number, width: number): void { - this.container.style.height = `${height}px`; - super.layout(height, width); + collapseMarkers(): void { + this.collapseAll(); + this.setSelection([]); + this.setFocus([]); + this.getHTMLElement().focus(); + this.focusFirst(); + } + + filterMarkers(): void { + this.refilter(); + } + + getVisibleItemCount(): number { + let filtered = 0; + const root = this.getNode(); + + for (const resourceMarkerNode of root.children) { + for (const markerNode of resourceMarkerNode.children) { + if (resourceMarkerNode.visible && markerNode.visible) { + filtered++; + } + } + } + + return filtered; + } + + isVisible(): boolean { + return !this.container.classList.contains('hidden'); } toggleVisibility(hide: boolean): void { @@ -901,10 +954,106 @@ class MarkersTree extends WorkbenchObjectTree { this.container.classList.toggle('hidden', hide); } - isVisible(): boolean { - return !this.container.classList.contains('hidden'); + reset(resourceMarkers: ResourceMarkers[]): void { + this.setChildren(null, Iterable.map(resourceMarkers, m => ({ element: m, children: createResourceMarkersIterator(m) }))); + } + + revealMarkers(activeResource: ResourceMarkers | null, focus: boolean, lastSelectedRelativeTop: number): void { + if (activeResource) { + if (this.hasElement(activeResource)) { + if (!this.isCollapsed(activeResource) && this.hasSelectedMarkerFor(activeResource)) { + this.reveal(this.getSelection()[0], lastSelectedRelativeTop); + if (focus) { + this.setFocus(this.getSelection()); + } + } else { + this.expand(activeResource); + this.reveal(activeResource, 0); + + if (focus) { + this.setFocus([activeResource]); + this.setSelection([activeResource]); + } + } + } + } else if (focus) { + this.setSelection([]); + this.focusFirst(); + } + } + + setAriaLabel(label: string): void { + this.ariaLabel = label; } + setMarkerSelection(marker: Marker): void { + if (this.isVisible()) { + if (marker) { + const markerNode = this.findMarkerNode(marker); + + if (markerNode) { + this.setFocus([markerNode]); + this.setSelection([markerNode]); + } + } else if (this.getSelection().length === 0) { + const firstVisibleElement = this.firstVisibleElement; + const marker = firstVisibleElement ? + firstVisibleElement instanceof ResourceMarkers ? firstVisibleElement.markers[0] : + firstVisibleElement instanceof Marker ? firstVisibleElement : undefined + : undefined; + + if (marker) { + this.setFocus([marker]); + this.setSelection([marker]); + } + } + } + } + + update(resourceMarkers: ResourceMarkers[]): void { + for (const resourceMarker of resourceMarkers) { + this.setChildren(resourceMarker, createResourceMarkersIterator(resourceMarker)); + this.rerender(resourceMarker); + } + } + + updateMarker(marker: Marker): void { + this.rerender(marker); + } + + private findMarkerNode(marker: Marker) { + for (const resourceNode of this.getNode().children) { + for (const markerNode of resourceNode.children) { + if (markerNode.element instanceof Marker && markerNode.element.marker === marker.marker) { + return markerNode.element; + } + } + } + + return undefined; + } + + private hasSelectedMarkerFor(resource: ResourceMarkers): boolean { + let selectedElement = this.getSelection(); + if (selectedElement && selectedElement.length > 0) { + if (selectedElement[0] instanceof Marker) { + if (resource.has((selectedElement[0]).marker.resource)) { + return true; + } + } + } + + return false; + } + + override dispose(): void { + super.dispose(); + } + + override layout(height: number, width: number): void { + this.container.style.height = `${height}px`; + super.layout(height, width); + } } registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { diff --git a/src/vs/workbench/contrib/markers/browser/media/markers.css b/src/vs/workbench/contrib/markers/browser/media/markers.css index 07bf1366e8135..356ec95eab4c8 100644 --- a/src/vs/workbench/contrib/markers/browser/media/markers.css +++ b/src/vs/workbench/contrib/markers/browser/media/markers.css @@ -217,3 +217,87 @@ .markers-panel .monaco-tl-contents .actions .action-item.disabled { display: none; } + +/* Table */ + +.markers-panel .markers-table-container { + margin-top: 8px; + padding-left: 20px; +} + +.markers-panel .markers-table-container .monaco-table .monaco-table-th { + display: flex; + font-weight: 600; + align-items: center; + padding-left: 10px; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td { + display: flex; + align-items: center; + padding-left: 10px; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td .highlight { + font-weight: bold; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code, +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .message, +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .file, +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .owner { + overflow: hidden; + text-overflow: ellipsis; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .severity { + display: flex; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row.selected .monaco-table-tr > .monaco-table-td.quickFix > .severity, +.markers-panel .markers-table-container .monaco-table .monaco-list-row.focused .monaco-table-tr > .monaco-table-td.quickFix > .severity, +.markers-panel .markers-table-container .monaco-table .monaco-list-row:hover .monaco-table-tr > .monaco-table-td.quickFix > .severity { + display: none; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .actions { + margin-left: -3px; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .actions > .monaco-action-bar .action-item { + display: none; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row.selected .monaco-table-tr > .monaco-table-td.quickFix > .actions > .monaco-action-bar .action-item, +.markers-panel .markers-table-container .monaco-table .monaco-list-row.focused .monaco-table-tr > .monaco-table-td.quickFix > .actions > .monaco-action-bar .action-item, +.markers-panel .markers-table-container .monaco-table .monaco-list-row:hover .monaco-table-tr > .monaco-table-td.quickFix > .actions > .monaco-action-bar .action-item { + display: flex; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code > .monaco-link::before, +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code > .code-label::before { + content: '('; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code > .monaco-link::after, +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code > .code-label::after { + content: ')'; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code.code-link > .code-label { + display: none; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code.code-link > .monaco-link { + display: inline; + text-decoration: underline; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code > .monaco-link { + display: none; +} + +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .file > .file-position { + margin-left: 6px; + opacity: 0.7; +} diff --git a/src/vs/workbench/contrib/markers/browser/messages.ts b/src/vs/workbench/contrib/markers/browser/messages.ts index e5d0ab10ba41a..ed66fa20c78bb 100644 --- a/src/vs/workbench/contrib/markers/browser/messages.ts +++ b/src/vs/workbench/contrib/markers/browser/messages.ts @@ -15,6 +15,7 @@ export default class Messages { public static PROBLEMS_PANEL_CONFIGURATION_TITLE: string = nls.localize('problems.panel.configuration.title', "Problems View"); public static PROBLEMS_PANEL_CONFIGURATION_AUTO_REVEAL: string = nls.localize('problems.panel.configuration.autoreveal', "Controls whether Problems view should automatically reveal files when opening them."); + public static PROBLEMS_PANEL_CONFIGURATION_VIEW_MODE: string = nls.localize('problems.panel.configuration.viewMode', "Controls the default view mode of the Problems view."); public static PROBLEMS_PANEL_CONFIGURATION_SHOW_CURRENT_STATUS: string = nls.localize('problems.panel.configuration.showCurrentInStatus', "When enabled shows the current problem in the status bar."); public static PROBLEMS_PANEL_CONFIGURATION_COMPARE_ORDER: string = nls.localize('problems.panel.configuration.compareOrder', "Controls the order in which problems are navigated."); public static PROBLEMS_PANEL_CONFIGURATION_COMPARE_ORDER_SEVERITY: string = nls.localize('problems.panel.configuration.compareOrder.severity', "Navigate problems ordered by severity"); From 96479074a4c233d229567c8494bb78d32a9c80d0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 23 May 2022 07:22:15 -0700 Subject: [PATCH 230/942] Create bash explicitly on linux/mac in shell integration smoke tests Part of #149324 --- .../terminal/terminal-shellIntegration.test.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts index b94b40045f7a3..fb3c40d15474d 100644 --- a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts +++ b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Application, Terminal, SettingsEditor } from '../../../../automation'; +import { Application, Terminal, SettingsEditor, TerminalCommandIdWithValue } from '../../../../automation'; import { setTerminalTestSettings } from './terminal-helpers'; export function setup() { @@ -24,28 +24,31 @@ export function setup() { await settingsEditor.clearUserSettings(); }); + async function createShellIntegrationProfile() { + await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, process.platform === 'win32' ? 'PowerShell' : 'bash'); + } + describe('Shell integration', function () { - // TODO: Fix on Linux, some distros use sh as the default shell in which case shell integration will fail - (process.platform === 'win32' || process.platform === 'linux' ? describe.skip : describe)('Decorations', function () { + (process.platform === 'win32' ? describe.skip : describe)('Decorations', function () { describe('Should show default icons', function () { it('Placeholder', async () => { - await terminal.createTerminal(); + await createShellIntegrationProfile(); await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); }); it('Success', async () => { - await terminal.createTerminal(); + await createShellIntegrationProfile(); await terminal.runCommandInTerminal(`ls`); await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 0 }); }); it('Error', async () => { - await terminal.createTerminal(); + await createShellIntegrationProfile(); await terminal.runCommandInTerminal(`fsdkfsjdlfksjdkf`); await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 1 }); }); }); describe('Custom configuration', function () { it('Should update and show custom icons', async () => { - await terminal.createTerminal(); + await createShellIntegrationProfile(); await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); await terminal.runCommandInTerminal(`ls`); await terminal.runCommandInTerminal(`fsdkfsjdlfksjdkf`); From 15e4ae374b642d60604b9ece83f5a077cad86278 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Mon, 23 May 2022 10:42:01 -0400 Subject: [PATCH 231/942] Bump distro (#150191) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ad35220a1e4e7..3768321d2bb04 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.68.0", - "distro": "19bbe01e3aa85625f35f1fb2df6818a8e491670f", + "distro": "36f3417e7298756bb2943725f4bce75f6096f246", "author": { "name": "Microsoft Corporation" }, From 89bff206a4325573abd69ba807debd645fb00e10 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Sunkara Date: Sun, 22 May 2022 14:48:10 +0100 Subject: [PATCH 232/942] feat: inlay hints padding option --- src/vs/editor/common/config/editorOptions.ts | 26 +++++++------------ .../browser/inlayHintsController.ts | 9 +++---- src/vs/monaco.d.ts | 7 +++-- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 022895e3e554c..9df6bf5054298 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2523,11 +2523,10 @@ export interface IEditorInlayHintsOptions { fontFamily?: string; /** - * The display style to render inlay hints with. - * Compact mode disables the borders and padding around the inlay hint. - * Defaults to 'standard'. + * Enables the padding around the inlay hint. + * Defaults to false. */ - displayStyle: 'standard' | 'compact'; + padding?: boolean; } /** @@ -2538,7 +2537,7 @@ export type EditorInlayHintsOptions = Readonly { constructor() { - const defaults: EditorInlayHintsOptions = { enabled: 'on', fontSize: 0, fontFamily: '', displayStyle: 'compact' }; + const defaults: EditorInlayHintsOptions = { enabled: 'on', fontSize: 0, fontFamily: '', padding: false }; super( EditorOption.inlayHints, 'inlayHints', defaults, { @@ -2564,16 +2563,11 @@ class EditorInlayHints extends BaseEditorOption(input.enabled, this.defaultValue.enabled, ['on', 'off', 'offUnlessPressed', 'onUnlessPressed']), fontSize: EditorIntOption.clampedInt(input.fontSize, this.defaultValue.fontSize, 0, 100), fontFamily: EditorStringOption.string(input.fontFamily, this.defaultValue.fontFamily), - displayStyle: stringSet<'standard' | 'compact'>(input.displayStyle, this.defaultValue.displayStyle, ['standard', 'compact']) + padding: boolean(input.padding, this.defaultValue.padding) }; } } diff --git a/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts b/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts index 9044a21c70d82..5a7289d812e59 100644 --- a/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts +++ b/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts @@ -468,7 +468,7 @@ export class InlayHintsController implements IEditorContribution { // - const { fontSize, fontFamily, displayStyle } = this._getLayoutInfo(); + const { fontSize, fontFamily, padding } = this._getLayoutInfo(); const fontFamilyVar = '--code-editorInlayHintsFontFamily'; this._editor.getContainerDomNode().style.setProperty(fontFamilyVar, fontFamily); @@ -493,6 +493,7 @@ export class InlayHintsController implements IEditorContribution { const cssProperties: CssProperties = { fontSize: `${fontSize}px`, fontFamily: `var(${fontFamilyVar}), ${EDITOR_FONT_DEFAULTS.fontFamily}`, + verticalAlign: 'middle', }; if (isNonEmptyArray(item.hint.textEdits)) { @@ -510,9 +511,7 @@ export class InlayHintsController implements IEditorContribution { } } - if (displayStyle === 'standard') { - cssProperties.verticalAlign = 'middle'; - + if (padding) { if (isFirst && isLast) { // only element cssProperties.padding = `1px ${Math.max(1, fontSize / 4) | 0}px`; @@ -594,7 +593,7 @@ export class InlayHintsController implements IEditorContribution { fontSize = editorFontSize; } const fontFamily = options.fontFamily || this._editor.getOption(EditorOption.fontFamily); - return { fontSize, fontFamily, displayStyle: options.displayStyle }; + return { fontSize, fontFamily, padding: options.padding }; } private _removeAllDecorations(): void { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index ee1b68b4155e3..37a8579dcc57b 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3798,11 +3798,10 @@ declare namespace monaco.editor { */ fontFamily?: string; /** - * The display style to render inlay hints with. - * Compact mode disables the borders and padding around the inlay hint. - * Defaults to 'standard'. + * Enables the padding around the inlay hint. + * Defaults to false. */ - displayStyle: 'standard' | 'compact'; + padding?: boolean; } /** From 138f4e9abd2316a001687130068c519fbcf2621c Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Mon, 23 May 2022 11:59:00 -0700 Subject: [PATCH 233/942] Fix ignorefile parsing on windows (#150213) --- .../workbench/contrib/files/browser/views/explorerViewer.ts | 2 +- src/vs/workbench/services/search/common/ignoreFile.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 73c8913b840e8..241b06a7fae66 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -739,7 +739,7 @@ export class FilesFilter implements ITreeFilter { // Maybe we need a cancellation token here in case it's super long? const content = await this.fileService.readFile(ignoreFileResource); const ignoreParent = ignoreTree.findSubstr(dirUri); - const ignoreFile = new IgnoreFile(content.value.toString(), dirUri.path + path.sep, ignoreParent); + const ignoreFile = new IgnoreFile(content.value.toString(), dirUri.path, ignoreParent); ignoreTree.set(dirUri, ignoreFile); // If we haven't seen this resource before then we need to add it to the list of resources we're tracking if (!this.ignoreFileResourcesPerRoot.get(root)?.has(ignoreFileResource)) { diff --git a/src/vs/workbench/services/search/common/ignoreFile.ts b/src/vs/workbench/services/search/common/ignoreFile.ts index a1a0b181a017f..35c4dea27ffe2 100644 --- a/src/vs/workbench/services/search/common/ignoreFile.ts +++ b/src/vs/workbench/services/search/common/ignoreFile.ts @@ -11,6 +11,12 @@ export class IgnoreFile { private isPathIgnored: (path: string, isDir: boolean, parent?: IgnoreFile) => boolean; constructor(contents: string, location: string, parent?: IgnoreFile) { + if (location[location.length - 1] === '\\') { + throw Error('Unexpected path format, do not use trailing backslashes'); + } + if (location[location.length - 1] !== '/') { + location += '/'; + } this.isPathIgnored = this.parseIgnoreFile(contents, location, parent); } From 99065b484f7fadddec2ad7c89028d92787e10224 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 23 May 2022 12:05:21 -0700 Subject: [PATCH 234/942] add tests --- .../contrib/tasks/common/taskConfiguration.ts | 26 ++-- .../tasks/test/common/configuration.test.ts | 126 +++++++++++++++++- 2 files changed, 137 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index 9c6f0ba9e3455..1fd6ca72d5bf5 100644 --- a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -14,8 +14,8 @@ import * as UUID from 'vs/base/common/uuid'; import { ValidationStatus, IProblemReporter as IProblemReporterBase } from 'vs/base/common/parsers'; import { - NamedProblemMatcher, ProblemMatcher, ProblemMatcherParser, Config as ProblemMatcherConfig, - isNamedProblemMatcher, ProblemMatcherRegistry + NamedProblemMatcher, ProblemMatcherParser, Config as ProblemMatcherConfig, + isNamedProblemMatcher, ProblemMatcherRegistry, ProblemMatcher } from 'vs/workbench/contrib/tasks/common/problemMatcher'; import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; @@ -710,7 +710,7 @@ export namespace RunOptions { } } -interface ParseContext { +export interface ParseContext { workspaceFolder: IWorkspaceFolder; workspace: IWorkspace | undefined; problemReporter: IProblemReporter; @@ -1130,7 +1130,7 @@ namespace CommandConfiguration { } } -namespace ProblemMatcherConverter { +export namespace ProblemMatcherConverter { export function namedFrom(this: void, declares: ProblemMatcherConfig.NamedProblemMatcher[] | undefined, context: ParseContext): IStringDictionary { let result: IStringDictionary = Object.create(null); @@ -1389,7 +1389,7 @@ namespace ConfiguringTask { customize: string; } - export function from(this: void, external: ConfiguringTask, context: ParseContext, index: number, source: TaskConfigSource): Tasks.ConfiguringTask | undefined { + export function from(this: void, external: ConfiguringTask, context: ParseContext, index: number, source: TaskConfigSource, testTaskDefinition?: Tasks.TaskDefinition): Tasks.ConfiguringTask | undefined { if (!external) { return undefined; } @@ -1399,7 +1399,7 @@ namespace ConfiguringTask { context.problemReporter.error(nls.localize('ConfigurationParser.noTaskType', 'Error: tasks configuration must have a type property. The configuration will be ignored.\n{0}\n', JSON.stringify(external, null, 4))); return undefined; } - let typeDeclaration = type ? TaskDefinitionRegistry.get(type) : undefined; + let typeDeclaration = type ? testTaskDefinition || TaskDefinitionRegistry.get(type) : undefined; if (!typeDeclaration) { let message = nls.localize('ConfigurationParser.noTypeDefinition', 'Error: there is no registered task type \'{0}\'. Did you miss installing an extension that provides a corresponding task provider?', type); context.problemReporter.error(message); @@ -1654,12 +1654,12 @@ namespace CustomTask { } } -interface TaskParseResult { +export interface TaskParseResult { custom: Tasks.CustomTask[]; configured: Tasks.ConfiguringTask[]; } -namespace TaskParser { +export namespace TaskParser { function isCustomTask(value: CustomTask | ConfiguringTask): value is CustomTask { let type = value.type; @@ -1672,7 +1672,7 @@ namespace TaskParser { process: ProcessExecutionSupportedContext }; - export function from(this: void, externals: Array | undefined, globals: Globals, context: ParseContext, source: TaskConfigSource): TaskParseResult { + export function from(this: void, externals: Array | undefined, globals: Globals, context: ParseContext, source: TaskConfigSource, testTaskDefinition?: Tasks.TaskDefinition): TaskParseResult { let result: TaskParseResult = { custom: [], configured: [] }; if (!externals) { return result; @@ -1683,7 +1683,7 @@ namespace TaskParser { const baseLoadIssues = Objects.deepClone(context.taskLoadIssues); for (let index = 0; index < externals.length; index++) { let external = externals[index]; - const definition = external.type ? TaskDefinitionRegistry.get(external.type) : undefined; + const definition = external.type ? testTaskDefinition || TaskDefinitionRegistry.get(external.type) : undefined; let typeNotSupported: boolean = false; if (definition && definition.when && !context.contextKeyService.contextMatchesRules(definition.when)) { typeNotSupported = true; @@ -1743,7 +1743,7 @@ namespace TaskParser { result.custom.push(customTask); } } else { - let configuredTask = ConfiguringTask.from(external, context, index, source); + let configuredTask = ConfiguringTask.from(external, context, index, source, testTaskDefinition); if (configuredTask) { configuredTask.addTaskLoadMessages(context.taskLoadIssues); result.configured.push(configuredTask); @@ -1795,7 +1795,7 @@ namespace TaskParser { } } -interface Globals { +export interface Globals { command?: Tasks.CommandConfiguration; problemMatcher?: ProblemMatcher[]; promptOnClose?: boolean; @@ -1933,7 +1933,7 @@ export interface ParseResult { export interface IProblemReporter extends IProblemReporterBase { } -class UUIDMap { +export class UUIDMap { private last: IStringDictionary | undefined; private current: IStringDictionary; diff --git a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts b/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts index c1ef36f8b17c1..577893066c814 100644 --- a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts @@ -10,14 +10,15 @@ import * as UUID from 'vs/base/common/uuid'; import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; import { ValidationStatus } from 'vs/base/common/parsers'; -import { ProblemMatcher, FileLocationKind, ProblemPattern, ApplyToKind } from 'vs/workbench/contrib/tasks/common/problemMatcher'; +import { ProblemMatcher, FileLocationKind, ProblemPattern, ApplyToKind, NamedProblemMatcher } from 'vs/workbench/contrib/tasks/common/problemMatcher'; import { WorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; import * as Tasks from 'vs/workbench/contrib/tasks/common/tasks'; -import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration, CustomTask, TaskConfigSource } from 'vs/workbench/contrib/tasks/common/taskConfiguration'; +import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration, CustomTask, TaskConfigSource, ParseContext, ProblemMatcherConverter, Globals, TaskParseResult, UUIDMap, TaskParser } from 'vs/workbench/contrib/tasks/common/taskConfiguration'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IContext } from 'vs/platform/contextkey/common/contextkey'; import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; const workspaceFolder: WorkspaceFolder = new WorkspaceFolder({ uri: URI.file('/workspace/folderOne'), @@ -1760,3 +1761,124 @@ suite('Bugs / regression tests', () => { testConfiguration(external, builder); }); }); + +class TestNamedProblemMatcher implements Partial { + +} + +class TestParseContext implements Partial { + +} + +suite('Task Configuration Test', () => { + let instantiationService: TestInstantiationService; + let parseContext: ParseContext; + let namedProblemMatcher: NamedProblemMatcher; + let problemReporter: ProblemReporter; + setup(() => { + instantiationService = new TestInstantiationService(); + namedProblemMatcher = instantiationService.createInstance(TestNamedProblemMatcher); + namedProblemMatcher.name = 'real'; + namedProblemMatcher.label = 'real label'; + problemReporter = new ProblemReporter(); + parseContext = instantiationService.createInstance(TestParseContext); + parseContext.problemReporter = problemReporter; + parseContext.namedProblemMatchers = { + 'real': namedProblemMatcher + }; + parseContext.uuidMap = new UUIDMap(); + }); + suite('ProblemMatcherConverter', () => { + test('returns [] and an error for an unknown problem matcher', () => { + const result = (ProblemMatcherConverter.from('$fake', parseContext)); + assert.deepEqual(result.value, []); + assert.strictEqual(result.errors?.length, 1); + }); + test('returns config for a known problem matcher', () => { + const result = (ProblemMatcherConverter.from('$real', parseContext)); + assert.strictEqual(result.errors?.length, 0); + assert.deepEqual(result.value, [{ "label": "real label" }]); + }); + test('returns config for a known problem matcher including applyTo', () => { + namedProblemMatcher.applyTo = ApplyToKind.closedDocuments; + const result = (ProblemMatcherConverter.from('$real', parseContext)); + assert.strictEqual(result.errors?.length, 0); + assert.deepEqual(result.value, [{ "label": "real label", "applyTo": ApplyToKind.closedDocuments }]); + }); + }); + suite('TaskParser from', () => { + suite('CustomTask', () => { + suite('incomplete config reports an appropriate error for missing', () => { + test('name', () => { + const result = TaskParser.from([{} as CustomTask], {} as Globals, parseContext, {} as TaskConfigSource); + assertTaskParseResult(result, undefined, problemReporter, 'Error: a task must provide a label property'); + }); + test('command', () => { + const result = TaskParser.from([{ taskName: 'task' } as CustomTask], {} as Globals, parseContext, {} as TaskConfigSource); + assertTaskParseResult(result, undefined, problemReporter, "Error: the task 'task' doesn't define a command"); + }); + }); + suite('returns expected result', () => { + test('single', () => { + const expected = [{ taskName: 'task', command: 'echo test' } as CustomTask]; + const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource); + assertTaskParseResult(result, { custom: expected }, problemReporter, undefined); + }); + test('multiple', () => { + const expected = [{ taskName: 'task', command: 'echo test' } as CustomTask, { taskName: 'task 2', command: 'echo test' } as CustomTask]; + const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource); + assertTaskParseResult(result, { custom: expected }, problemReporter, undefined); + }); + }); + }); + suite('ConfiguredTask', () => { + suite('returns expected result', () => { + test('single', () => { + const expected = [{ taskName: 'task', command: 'echo test', type: 'any', label: 'task' }]; + const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource, { extensionId: 'registered', taskType: 'any', properties: {} } as Tasks.TaskDefinition); + assertTaskParseResult(result, { configured: expected }, problemReporter, undefined); + }); + test('multiple', () => { + const expected = [{ taskName: 'task', command: 'echo test', type: 'any', label: 'task' }, { taskName: 'task 2', command: 'echo test', type: 'any', label: 'task 2' }]; + const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource, { extensionId: 'registered', taskType: 'any', properties: {} } as Tasks.TaskDefinition); + assertTaskParseResult(result, { configured: expected }, problemReporter, undefined); + }); + }); + }); + }); +}); + +function assertTaskParseResult(actual: TaskParseResult, expected: ITestTaskParseResult | undefined, problemReporter: ProblemReporter, expectedMessage?: string): void { + if (expectedMessage === undefined) { + assert.strictEqual(problemReporter.lastMessage, undefined); + } else { + assert.ok(problemReporter.lastMessage?.includes(expectedMessage)); + } + + assert.deepEqual(actual.custom.length, expected?.custom?.length || 0); + assert.deepEqual(actual.configured.length, expected?.configured?.length || 0); + + let index = 0; + if (expected?.configured) { + for (const taskParseResult of expected?.configured) { + assert.strictEqual(actual.configured[index]._label, taskParseResult.label); + index++; + } + } + index = 0; + if (expected?.custom) { + for (const taskParseResult of expected?.custom) { + assert.strictEqual(actual.custom[index]._label, taskParseResult.taskName); + index++; + } + } +} + +interface ITestTaskParseResult { + custom?: Partial[]; + configured?: Partial[]; +} + +interface TestConfiguringTask extends Partial { + label: string; +} From d94e927723503dceec903f5f69e7bd4f1bb54fb6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 23 May 2022 21:15:32 +0200 Subject: [PATCH 235/942] - fix installing packs and deps in profiles - fix do not downloading same version --- .../abstractExtensionManagementService.ts | 94 ++++++++----------- .../node/extensionManagementService.ts | 90 +++++++++--------- .../common/webExtensionManagementService.ts | 32 +++---- 3 files changed, 100 insertions(+), 116 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 1013592e6f925..94f26d9d38485 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -27,12 +27,12 @@ export interface IInstallExtensionTask { readonly identifier: IExtensionIdentifier; readonly source: IGalleryExtension | URI; readonly operation: InstallOperation; - run(): Promise; - waitUntilTaskIsFinished(): Promise; + run(): Promise<{ local: ILocalExtension; metadata: Metadata }>; + waitUntilTaskIsFinished(): Promise<{ local: ILocalExtension; metadata: Metadata }>; cancel(): void; } -export type UninstallExtensionTaskOptions = { readonly remove?: boolean; readonly versionOnly?: boolean; readonly profileLocation?: URI }; +export type UninstallExtensionTaskOptions = { readonly remove?: boolean; readonly versionOnly?: boolean }; export interface IUninstallExtensionTask { readonly extension: ILocalExtension; @@ -66,6 +66,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl constructor( @IExtensionGalleryService protected readonly galleryService: IExtensionGalleryService, + @IExtensionsProfileScannerService protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService, @ITelemetryService protected readonly telemetryService: ITelemetryService, @ILogService protected readonly logService: ILogService, @IProductService protected readonly productService: IProductService @@ -116,7 +117,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl throw new Error(nls.localize('Not a Marketplace extension', "Only Marketplace Extensions can be reinstalled")); } - await this.createUninstallExtensionTask(extension, { remove: true, versionOnly: true }).run(); + await this.createDefaultUninstallExtensionTask(extension, { remove: true, versionOnly: true }).run(); await this.installFromGallery(galleryExtension); } @@ -141,14 +142,18 @@ export abstract class AbstractExtensionManagementService extends Disposable impl let installExtensionTask = this.installingExtensions.get(ExtensionKey.create(extension).toString()); if (installExtensionTask) { this.logService.info('Extensions is already requested to install', extension.identifier.id); - return installExtensionTask.waitUntilTaskIsFinished(); + const { local, metadata } = await installExtensionTask.waitUntilTaskIsFinished(); + if (options.profileLocation) { + await this.extensionsProfileScannerService.addExtensionsToProfile([[local, metadata]], options.profileLocation); + } + return local; } options = { ...options, installOnlyNewlyAddedFromExtensionPack: true /* always true for gallery extensions */ }; } const allInstallExtensionTasks: { task: IInstallExtensionTask; manifest: IExtensionManifest }[] = []; const installResults: (InstallExtensionResult & { local: ILocalExtension })[] = []; - const installExtensionTask = this.createInstallExtensionTask(manifest, extension, options); + const installExtensionTask = this.createDefaultInstallExtensionTask(manifest, extension, options); if (!URI.isUri(extension)) { this.installingExtensions.set(ExtensionKey.create(extension).toString(), installExtensionTask); } @@ -162,14 +167,14 @@ export abstract class AbstractExtensionManagementService extends Disposable impl this.logService.info('Installing the extension without checking dependencies and pack', installExtensionTask.identifier.id); } else { try { - const allDepsAndPackExtensionsToInstall = await this.getAllDepsAndPackExtensionsToInstall(installExtensionTask.identifier, manifest, !!options.installOnlyNewlyAddedFromExtensionPack, !!options.installPreReleaseVersion); + const allDepsAndPackExtensionsToInstall = await this.getAllDepsAndPackExtensionsToInstall(installExtensionTask.identifier, manifest, !!options.installOnlyNewlyAddedFromExtensionPack, !!options.installPreReleaseVersion, options.profileLocation); for (const { gallery, manifest } of allDepsAndPackExtensionsToInstall) { installExtensionHasDependents = installExtensionHasDependents || !!manifest.extensionDependencies?.some(id => areSameExtensions({ id }, installExtensionTask.identifier)); const key = ExtensionKey.create(gallery).toString(); if (this.installingExtensions.has(key)) { this.logService.info('Extension is already requested to install', gallery.identifier.id); } else { - const task = this.createInstallExtensionTask(manifest, gallery, { ...options, donotIncludePackAndDependencies: true }); + const task = this.createDefaultInstallExtensionTask(manifest, gallery, { ...options, donotIncludePackAndDependencies: true }); this.installingExtensions.set(key, task); this._onInstallExtension.fire({ identifier: task.identifier, source: gallery }); this.logService.info('Installing extension:', task.identifier.id); @@ -214,7 +219,10 @@ export abstract class AbstractExtensionManagementService extends Disposable impl await this.joinAllSettled(extensionsToInstall.map(async ({ task }) => { const startTime = new Date().getTime(); try { - const local = await task.run(); + const { local, metadata } = await task.run(); + if (options.profileLocation) { + await this.extensionsProfileScannerService.addExtensionsToProfile([[local, metadata]], options.profileLocation); + } await this.joinAllSettled(this.participants.map(participant => participant.postInstall(local, task.source, options, CancellationToken.None))); if (!URI.isUri(task.source)) { const isUpdate = task.operation === InstallOperation.Update; @@ -253,7 +261,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl // rollback installed extensions if (installResults.length) { try { - const result = await Promise.allSettled(installResults.map(({ local }) => this.createUninstallExtensionTask(local, { versionOnly: true, profileLocation: options.profileLocation }).run())); + const result = await Promise.allSettled(installResults.map(({ local }) => this.createUninstallExtensionTask(local, { versionOnly: true }, options.profileLocation).run())); for (let index = 0; index < result.length; index++) { const r = result[index]; const { identifier } = installResults[index]; @@ -300,12 +308,12 @@ export abstract class AbstractExtensionManagementService extends Disposable impl return results; } - private async getAllDepsAndPackExtensionsToInstall(extensionIdentifier: IExtensionIdentifier, manifest: IExtensionManifest, getOnlyNewlyAddedFromExtensionPack: boolean, installPreRelease: boolean): Promise<{ gallery: IGalleryExtension; manifest: IExtensionManifest }[]> { + private async getAllDepsAndPackExtensionsToInstall(extensionIdentifier: IExtensionIdentifier, manifest: IExtensionManifest, getOnlyNewlyAddedFromExtensionPack: boolean, installPreRelease: boolean, profile: URI | undefined): Promise<{ gallery: IGalleryExtension; manifest: IExtensionManifest }[]> { if (!this.galleryService.isEnabled()) { return []; } - let installed = await this.getInstalled(); + let installed = await this.getInstalled(undefined, profile); const knownIdentifiers = [extensionIdentifier, ...(installed).map(i => i.identifier)]; const allDependenciesAndPacks: { gallery: IGalleryExtension; manifest: IExtensionManifest }[] = []; @@ -354,7 +362,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl }; await collectDependenciesAndPackExtensionsToInstall(extensionIdentifier, manifest); - installed = await this.getInstalled(); + installed = await this.getInstalled(undefined, profile); return allDependenciesAndPacks.filter(e => !installed.some(i => areSameExtensions(i.identifier, e.gallery.identifier))); } @@ -422,14 +430,16 @@ export abstract class AbstractExtensionManagementService extends Disposable impl } private async unininstallExtension(extension: ILocalExtension, options: ServerUninstallOptions): Promise { - const uninstallExtensionTask = this.uninstallingExtensions.get(extension.identifier.id.toLowerCase()); - if (uninstallExtensionTask) { - this.logService.info('Extensions is already requested to uninstall', extension.identifier.id); - return uninstallExtensionTask.waitUntilTaskIsFinished(); + if (!options.profileLocation) { + const uninstallExtensionTask = this.uninstallingExtensions.get(extension.identifier.id.toLowerCase()); + if (uninstallExtensionTask) { + this.logService.info('Extensions is already requested to uninstall', extension.identifier.id); + return uninstallExtensionTask.waitUntilTaskIsFinished(); + } } - const createUninstallExtensionTask = (extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask => { - const uninstallExtensionTask = this.createUninstallExtensionTask(extension, options); + const createUninstallExtensionTask = (extension: ILocalExtension, uninstallOptions: UninstallExtensionTaskOptions): IUninstallExtensionTask => { + const uninstallExtensionTask = this.createUninstallExtensionTask(extension, uninstallOptions, options.profileLocation); this.uninstallingExtensions.set(uninstallExtensionTask.extension.identifier.id.toLowerCase(), uninstallExtensionTask); this.logService.info('Uninstalling extension:', extension.identifier.id); this._onUninstallExtension.fire(extension.identifier); @@ -450,9 +460,8 @@ export abstract class AbstractExtensionManagementService extends Disposable impl const processedTasks: IUninstallExtensionTask[] = []; try { - allTasks.push(createUninstallExtensionTask(extension, { profileLocation: options.profileLocation })); - const installed = await this.getInstalled(ExtensionType.User); - + allTasks.push(createUninstallExtensionTask(extension, {})); + const installed = await this.getInstalled(ExtensionType.User, options.profileLocation); if (options.donotIncludePack) { this.logService.info('Uninstalling the extension without including packed extension', extension.identifier.id); } else { @@ -587,6 +596,10 @@ export abstract class AbstractExtensionManagementService extends Disposable impl } } + private createUninstallExtensionTask(extension: ILocalExtension, options: UninstallExtensionTaskOptions, profile?: URI): IUninstallExtensionTask { + return profile ? new UninstallExtensionFromProfileTask(extension, profile, this.extensionsProfileScannerService) : this.createDefaultUninstallExtensionTask(extension, options); + } + abstract getTargetPlatform(): Promise; abstract zip(extension: ILocalExtension): Promise; abstract unzip(zipLocation: URI): Promise; @@ -598,8 +611,8 @@ export abstract class AbstractExtensionManagementService extends Disposable impl abstract updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise; abstract updateExtensionScope(local: ILocalExtension, isMachineScoped: boolean): Promise; - protected abstract createInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask; - protected abstract createUninstallExtensionTask(extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask; + protected abstract createDefaultInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask; + protected abstract createDefaultUninstallExtensionTask(extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask; } export function joinErrors(errorOrErrors: (Error | string) | (Array)): Error { @@ -694,45 +707,18 @@ export abstract class AbstractExtensionTask { protected abstract doRun(token: CancellationToken): Promise; } -export abstract class AbstractInstallExtensionTask extends AbstractExtensionTask { - - constructor( - protected readonly options: ServerInstallOptions, - private readonly extensionsProfileScannerService: IExtensionsProfileScannerService, - ) { - super(); - } - - protected async doRun(token: CancellationToken): Promise { - const { local, metadata } = await this.install(token); - if (this.options.profileLocation) { - await this.extensionsProfileScannerService.addExtensionsToProfile([[local, metadata]], this.options.profileLocation); - } - return local; - } - - protected abstract install(token: CancellationToken): Promise<{ local: ILocalExtension; metadata: Metadata }>; - -} - -export abstract class AbstractUninstallExtensionTask extends AbstractExtensionTask { +export class UninstallExtensionFromProfileTask extends AbstractExtensionTask implements IUninstallExtensionTask { constructor( readonly extension: ILocalExtension, - protected readonly options: UninstallExtensionTaskOptions, + private readonly profileLocation: URI, private readonly extensionsProfileScannerService: IExtensionsProfileScannerService, ) { super(); } protected async doRun(token: CancellationToken): Promise { - if (this.options.profileLocation) { - await this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension.identifier, this.options.profileLocation); - } else { - await this.uninstall(token); - } + await this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension.identifier, this.profileLocation); } - protected abstract uninstall(token: CancellationToken): Promise; - } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 445cf64b5b315..752f3ec1ec3ee 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -22,7 +22,7 @@ import { extract, ExtractError, IFile, zip } from 'vs/base/node/zip'; import * as nls from 'vs/nls'; import { IDownloadService } from 'vs/platform/download/common/download'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -import { AbstractExtensionManagementService, AbstractInstallExtensionTask, AbstractUninstallExtensionTask, IInstallExtensionTask, IUninstallExtensionTask, joinErrors, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService'; +import { AbstractExtensionManagementService, AbstractExtensionTask, IInstallExtensionTask, IUninstallExtensionTask, joinErrors, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService'; import { ExtensionManagementError, ExtensionManagementErrorCode, IExtensionGalleryService, IExtensionIdentifier, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallOperation, IServerExtensionManagementService, Metadata, ServerInstallOptions, ServerInstallVSIXOptions @@ -62,14 +62,14 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi @ILogService logService: ILogService, @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, @IExtensionsScannerService private readonly extensionsScannerService: IExtensionsScannerService, - @IExtensionsProfileScannerService protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService, + @IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService, @IDownloadService private downloadService: IDownloadService, @IInstantiationService instantiationService: IInstantiationService, @IFileService private readonly fileService: IFileService, @IProductService productService: IProductService, @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(galleryService, telemetryService, logService, productService); + super(galleryService, extensionsProfileScannerService, telemetryService, logService, productService); const extensionLifecycle = this._register(instantiationService.createInstance(ExtensionsLifecycle)); this.extensionsScanner = this._register(instantiationService.createInstance(ExtensionsScanner, extension => extensionLifecycle.postUninstall(extension))); this.manifestCache = this._register(new ExtensionsManifestCache(environmentService, this)); @@ -162,12 +162,12 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi return downloadedLocation; } - protected createInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask { - return URI.isUri(extension) ? new InstallVSIXTask(manifest, extension, options, this.galleryService, this.extensionsScanner, this.logService, this.extensionsProfileScannerService) : new InstallGalleryExtensionTask(extension, options, this.extensionsDownloader, this.extensionsScanner, this.logService, this.extensionsProfileScannerService); + protected createDefaultInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask { + return URI.isUri(extension) ? new InstallVSIXTask(manifest, extension, options, this.galleryService, this.extensionsScanner, this.logService) : new InstallGalleryExtensionTask(extension, options, this.extensionsDownloader, this.extensionsScanner, this.logService); } - protected createUninstallExtensionTask(extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask { - return new UninstallExtensionTask(extension, options, this.extensionsScanner, this.extensionsProfileScannerService); + protected createDefaultUninstallExtensionTask(extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask { + return new UninstallExtensionTask(extension, options, this.extensionsScanner); } private async collectFiles(extension: ILocalExtension): Promise { @@ -464,7 +464,7 @@ class ExtensionsScanner extends Disposable { } -abstract class InstallExtensionTask extends AbstractInstallExtensionTask implements IInstallExtensionTask { +abstract class InstallExtensionTask extends AbstractExtensionTask<{ local: ILocalExtension; metadata: Metadata }> implements IInstallExtensionTask { protected _operation = InstallOperation.Install; get operation() { return isUndefined(this.options.operation) ? this._operation : this.options.operation; } @@ -472,12 +472,11 @@ abstract class InstallExtensionTask extends AbstractInstallExtensionTask impleme constructor( readonly identifier: IExtensionIdentifier, readonly source: URI | IGalleryExtension, - options: ServerInstallOptions, + protected readonly options: ServerInstallOptions, protected readonly extensionsScanner: ExtensionsScanner, protected readonly logService: ILogService, - extensionsProfileScannerService: IExtensionsProfileScannerService, ) { - super(options, extensionsProfileScannerService); + super(); } protected async installExtension(installableExtension: InstallableExtension, token: CancellationToken): Promise { @@ -531,37 +530,47 @@ class InstallGalleryExtensionTask extends InstallExtensionTask { private readonly extensionsDownloader: ExtensionsDownloader, extensionsScanner: ExtensionsScanner, logService: ILogService, - extensionsProfileScannerService: IExtensionsProfileScannerService, ) { - super(gallery.identifier, gallery, options, extensionsScanner, logService, extensionsProfileScannerService); + super(gallery.identifier, gallery, options, extensionsScanner, logService); } - protected async install(token: CancellationToken): Promise<{ local: ILocalExtension; metadata: Metadata }> { + protected async doRun(token: CancellationToken): Promise<{ local: ILocalExtension; metadata: Metadata }> { const installed = await this.extensionsScanner.scanExtensions(null, undefined); const existingExtension = installed.find(i => areSameExtensions(i.identifier, this.gallery.identifier)); + if (existingExtension) { this._operation = InstallOperation.Update; } - const installableExtension = await this.downloadInstallableExtension(this.gallery, this._operation); - installableExtension.metadata.isMachineScoped = this.options.isMachineScoped || existingExtension?.isMachineScoped; - installableExtension.metadata.isBuiltin = this.options.isBuiltin || existingExtension?.isBuiltin; - installableExtension.metadata.isSystem = existingExtension?.type === ExtensionType.System ? true : undefined; - installableExtension.metadata.updated = !!existingExtension; - installableExtension.metadata.isPreReleaseVersion = this.gallery.properties.isPreReleaseVersion; - installableExtension.metadata.preRelease = this.gallery.properties.isPreReleaseVersion || - (isBoolean(this.options.installPreReleaseVersion) - ? this.options.installPreReleaseVersion /* Respect the passed flag */ - : existingExtension?.preRelease /* Respect the existing pre-release flag if it was set */); + const metadata: Metadata = { + id: this.gallery.identifier.uuid, + publisherId: this.gallery.publisherId, + publisherDisplayName: this.gallery.publisherDisplayName, + targetPlatform: this.gallery.properties.targetPlatform, + isMachineScoped: this.options.isMachineScoped || existingExtension?.isMachineScoped, + isBuiltin: this.options.isBuiltin || existingExtension?.isBuiltin, + isSystem: existingExtension?.type === ExtensionType.System ? true : undefined, + updated: !!existingExtension, + isPreReleaseVersion: this.gallery.properties.isPreReleaseVersion, + preRelease: this.gallery.properties.isPreReleaseVersion || + (isBoolean(this.options.installPreReleaseVersion) + ? this.options.installPreReleaseVersion /* Respect the passed flag */ + : existingExtension?.preRelease /* Respect the existing pre-release flag if it was set */) + }; + + if (existingExtension?.manifest.version === this.gallery.version) { + return { local: existingExtension, metadata }; + } + const zipPath = await this.downloadExtension(this.gallery, this._operation); try { - const local = await this.installExtension(installableExtension, token); + const local = await this.installExtension({ zipPath, key: ExtensionKey.create(this.gallery), metadata }, token); if (existingExtension && (existingExtension.targetPlatform !== local.targetPlatform || semver.neq(existingExtension.manifest.version, local.manifest.version))) { await this.extensionsScanner.setUninstalled(existingExtension); } - return { local, metadata: installableExtension.metadata }; + return { local, metadata }; } catch (error) { - await this.deleteDownloadedVSIX(installableExtension.zipPath); + await this.deleteDownloadedVSIX(zipPath); throw error; } } @@ -575,14 +584,7 @@ class InstallGalleryExtensionTask extends InstallExtensionTask { } } - private async downloadInstallableExtension(extension: IGalleryExtension, operation: InstallOperation): Promise> { - const metadata = { - id: extension.identifier.uuid, - publisherId: extension.publisherId, - publisherDisplayName: extension.publisherDisplayName, - targetPlatform: extension.properties.targetPlatform - }; - + private async downloadExtension(extension: IGalleryExtension, operation: InstallOperation): Promise { let zipPath: string | undefined; try { this.logService.trace('Started downloading extension:', extension.identifier.id); @@ -594,7 +596,7 @@ class InstallGalleryExtensionTask extends InstallExtensionTask { try { await getManifest(zipPath); - return (>{ zipPath, key: ExtensionKey.create(extension), metadata }); + return zipPath; } catch (error) { await this.deleteDownloadedVSIX(zipPath); throw new ExtensionManagementError(joinErrors(error).message, ExtensionManagementErrorCode.Invalid); @@ -611,12 +613,11 @@ class InstallVSIXTask extends InstallExtensionTask { private readonly galleryService: IExtensionGalleryService, extensionsScanner: ExtensionsScanner, logService: ILogService, - extensionsProfileScannerService: IExtensionsProfileScannerService, ) { - super({ id: getGalleryExtensionId(manifest.publisher, manifest.name) }, location, options, extensionsScanner, logService, extensionsProfileScannerService); + super({ id: getGalleryExtensionId(manifest.publisher, manifest.name) }, location, options, extensionsScanner, logService); } - protected async install(token: CancellationToken): Promise<{ local: ILocalExtension; metadata: Metadata }> { + protected async doRun(token: CancellationToken): Promise<{ local: ILocalExtension; metadata: Metadata }> { const extensionKey = new ExtensionKey(this.identifier, this.manifest.version); const installedExtensions = await this.extensionsScanner.scanExtensions(ExtensionType.User, undefined); const existing = installedExtensions.find(i => areSameExtensions(this.identifier, i.identifier)); @@ -674,18 +675,17 @@ class InstallVSIXTask extends InstallExtensionTask { } } -class UninstallExtensionTask extends AbstractUninstallExtensionTask implements IUninstallExtensionTask { +class UninstallExtensionTask extends AbstractExtensionTask implements IUninstallExtensionTask { constructor( - extension: ILocalExtension, - options: UninstallExtensionTaskOptions, + readonly extension: ILocalExtension, + private readonly options: UninstallExtensionTaskOptions, private readonly extensionsScanner: ExtensionsScanner, - extensionsProfileScannerService: IExtensionsProfileScannerService, ) { - super(extension, options, extensionsProfileScannerService); + super(); } - protected async uninstall(token: CancellationToken): Promise { + protected async doRun(token: CancellationToken): Promise { const toUninstall: ILocalExtension[] = []; const userExtensions = await this.extensionsScanner.scanUserExtensions(false); if (this.options.versionOnly) { diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts index 04f014323962a..21dac6848682a 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts @@ -10,7 +10,7 @@ import { areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionM import { IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { AbstractExtensionManagementService, AbstractInstallExtensionTask, AbstractUninstallExtensionTask, IInstallExtensionTask, IUninstallExtensionTask, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService'; +import { AbstractExtensionManagementService, AbstractExtensionTask, IInstallExtensionTask, IUninstallExtensionTask, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -27,10 +27,10 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe @ILogService logService: ILogService, @IWebExtensionsScannerService private readonly webExtensionsScannerService: IWebExtensionsScannerService, @IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService, - @IExtensionsProfileScannerService private readonly extensionsProfileScannerService: IExtensionsProfileScannerService, + @IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService, @IProductService productService: IProductService ) { - super(extensionGalleryService, telemetryService, logService, productService); + super(extensionGalleryService, extensionsProfileScannerService, telemetryService, logService, productService); } async getTargetPlatform(): Promise { @@ -93,12 +93,12 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe return local; } - protected createInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions): IInstallExtensionTask { - return new InstallExtensionTask(manifest, extension, options, this.webExtensionsScannerService, this.extensionsProfileScannerService); + protected createDefaultInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions): IInstallExtensionTask { + return new InstallExtensionTask(manifest, extension, options, this.webExtensionsScannerService); } - protected createUninstallExtensionTask(extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask { - return new UninstallExtensionTask(extension, options, this.webExtensionsScannerService, this.extensionsProfileScannerService); + protected createDefaultUninstallExtensionTask(extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask { + return new UninstallExtensionTask(extension, options, this.webExtensionsScannerService); } zip(extension: ILocalExtension): Promise { throw new Error('unsupported'); } @@ -129,7 +129,7 @@ function getMetadata(options?: InstallOptions, existingExtension?: IExtension): return metadata; } -class InstallExtensionTask extends AbstractInstallExtensionTask implements IInstallExtensionTask { +class InstallExtensionTask extends AbstractExtensionTask<{ local: ILocalExtension; metadata: Metadata }> implements IInstallExtensionTask { readonly identifier: IExtensionIdentifier; readonly source: URI | IGalleryExtension; @@ -140,16 +140,15 @@ class InstallExtensionTask extends AbstractInstallExtensionTask implements IInst constructor( manifest: IExtensionManifest, private readonly extension: URI | IGalleryExtension, - options: InstallOptions, + private readonly options: InstallOptions, private readonly webExtensionsScannerService: IWebExtensionsScannerService, - extensionsProfileScannerService: IExtensionsProfileScannerService, ) { - super(options, extensionsProfileScannerService); + super(); this.identifier = URI.isUri(extension) ? { id: getGalleryExtensionId(manifest.publisher, manifest.name) } : extension.identifier; this.source = extension; } - protected async install(token: CancellationToken): Promise<{ local: ILocalExtension; metadata: Metadata }> { + protected async doRun(token: CancellationToken): Promise<{ local: ILocalExtension; metadata: Metadata }> { const userExtensions = await this.webExtensionsScannerService.scanUserExtensions(); const existingExtension = userExtensions.find(e => areSameExtensions(e.identifier, this.identifier)); if (existingExtension) { @@ -178,18 +177,17 @@ class InstallExtensionTask extends AbstractInstallExtensionTask implements IInst } } -class UninstallExtensionTask extends AbstractUninstallExtensionTask implements IUninstallExtensionTask { +class UninstallExtensionTask extends AbstractExtensionTask implements IUninstallExtensionTask { constructor( - extension: ILocalExtension, + readonly extension: ILocalExtension, options: UninstallExtensionTaskOptions, private readonly webExtensionsScannerService: IWebExtensionsScannerService, - extensionsProfileScannerService: IExtensionsProfileScannerService, ) { - super(extension, options, extensionsProfileScannerService); + super(); } - protected uninstall(token: CancellationToken): Promise { + protected doRun(token: CancellationToken): Promise { return this.webExtensionsScannerService.removeExtension(this.extension.identifier); } } From b27cfaccfd19f7bf0a27488c3af8784678c2c67d Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Mon, 23 May 2022 15:21:37 -0400 Subject: [PATCH 236/942] Close brace on redacted property (#150207) --- src/vs/platform/telemetry/common/telemetryService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index adcec1d16f6c6..f3dab6ded3f29 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -209,7 +209,7 @@ export class TelemetryService implements ITelemetryService { // Check for common user data in the telemetry events for (const secretRegex of userDataRegexes) { if (secretRegex.regex.test(value)) { - return ``; } } From 977123e6d840c9a5df5611b4504bf46cf3606068 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 23 May 2022 12:38:51 -0700 Subject: [PATCH 237/942] Update src/vs/workbench/contrib/tasks/test/common/configuration.test.ts --- src/vs/workbench/contrib/tasks/test/common/configuration.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts b/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts index 577893066c814..56ffdbced3b92 100644 --- a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts @@ -1763,7 +1763,6 @@ suite('Bugs / regression tests', () => { }); class TestNamedProblemMatcher implements Partial { - } class TestParseContext implements Partial { From 2f0de4444998f992763eb58b5ced1a6f5186594a Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 23 May 2022 12:39:16 -0700 Subject: [PATCH 238/942] Update src/vs/workbench/contrib/tasks/test/common/configuration.test.ts --- src/vs/workbench/contrib/tasks/test/common/configuration.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts b/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts index 56ffdbced3b92..083d47dcf2840 100644 --- a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts @@ -1766,7 +1766,6 @@ class TestNamedProblemMatcher implements Partial { } class TestParseContext implements Partial { - } suite('Task Configuration Test', () => { From a48a9f8fc94c94a1c95104d2200852758db480cd Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 23 May 2022 12:39:44 -0700 Subject: [PATCH 239/942] rename --- .../common/{configuration.test.ts => taskConfiguration.test.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/vs/workbench/contrib/tasks/test/common/{configuration.test.ts => taskConfiguration.test.ts} (99%) diff --git a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts b/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts similarity index 99% rename from src/vs/workbench/contrib/tasks/test/common/configuration.test.ts rename to src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts index 083d47dcf2840..1353ab1376840 100644 --- a/src/vs/workbench/contrib/tasks/test/common/configuration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts @@ -1768,7 +1768,7 @@ class TestNamedProblemMatcher implements Partial { class TestParseContext implements Partial { } -suite('Task Configuration Test', () => { +suite('Problem matcher and task parser', () => { let instantiationService: TestInstantiationService; let parseContext: ParseContext; let namedProblemMatcher: NamedProblemMatcher; From 45aefc8f376223ce0eabd0d5641eca855973b226 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 23 May 2022 12:52:56 -0700 Subject: [PATCH 240/942] cleanup --- .../test/common/taskConfiguration.test.ts | 45 +++++++------------ 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts b/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts index 1353ab1376840..105d5d3dce660 100644 --- a/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts @@ -59,6 +59,10 @@ class ProblemReporter implements IProblemReporter { this.receivedMessage = true; this.lastMessage = message; } + + public clearMessage(): void { + this.lastMessage = undefined; + } } class ConfiguationBuilder { @@ -1768,7 +1772,7 @@ class TestNamedProblemMatcher implements Partial { class TestParseContext implements Partial { } -suite('Problem matcher and task parser', () => { +suite.only('To task configuration from', () => { let instantiationService: TestInstantiationService; let parseContext: ParseContext; let namedProblemMatcher: NamedProblemMatcher; @@ -1781,12 +1785,10 @@ suite('Problem matcher and task parser', () => { problemReporter = new ProblemReporter(); parseContext = instantiationService.createInstance(TestParseContext); parseContext.problemReporter = problemReporter; - parseContext.namedProblemMatchers = { - 'real': namedProblemMatcher - }; + parseContext.namedProblemMatchers = { 'real': namedProblemMatcher }; parseContext.uuidMap = new UUIDMap(); }); - suite('ProblemMatcherConverter', () => { + suite('ProblemMatcher config', () => { test('returns [] and an error for an unknown problem matcher', () => { const result = (ProblemMatcherConverter.from('$fake', parseContext)); assert.deepEqual(result.value, []); @@ -1804,7 +1806,7 @@ suite('Problem matcher and task parser', () => { assert.deepEqual(result.value, [{ "label": "real label", "applyTo": ApplyToKind.closedDocuments }]); }); }); - suite('TaskParser from', () => { + suite('TaskParser external config', () => { suite('CustomTask', () => { suite('incomplete config reports an appropriate error for missing', () => { test('name', () => { @@ -1816,31 +1818,17 @@ suite('Problem matcher and task parser', () => { assertTaskParseResult(result, undefined, problemReporter, "Error: the task 'task' doesn't define a command"); }); }); - suite('returns expected result', () => { - test('single', () => { - const expected = [{ taskName: 'task', command: 'echo test' } as CustomTask]; - const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource); - assertTaskParseResult(result, { custom: expected }, problemReporter, undefined); - }); - test('multiple', () => { - const expected = [{ taskName: 'task', command: 'echo test' } as CustomTask, { taskName: 'task 2', command: 'echo test' } as CustomTask]; - const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource); - assertTaskParseResult(result, { custom: expected }, problemReporter, undefined); - }); + test('returns expected result', () => { + const expected = [{ taskName: 'task', command: 'echo test' } as CustomTask, { taskName: 'task 2', command: 'echo test' } as CustomTask]; + const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource); + assertTaskParseResult(result, { custom: expected }, problemReporter, undefined); }); }); suite('ConfiguredTask', () => { - suite('returns expected result', () => { - test('single', () => { - const expected = [{ taskName: 'task', command: 'echo test', type: 'any', label: 'task' }]; - const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource, { extensionId: 'registered', taskType: 'any', properties: {} } as Tasks.TaskDefinition); - assertTaskParseResult(result, { configured: expected }, problemReporter, undefined); - }); - test('multiple', () => { - const expected = [{ taskName: 'task', command: 'echo test', type: 'any', label: 'task' }, { taskName: 'task 2', command: 'echo test', type: 'any', label: 'task 2' }]; - const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource, { extensionId: 'registered', taskType: 'any', properties: {} } as Tasks.TaskDefinition); - assertTaskParseResult(result, { configured: expected }, problemReporter, undefined); - }); + test('returns expected result', () => { + const expected = [{ taskName: 'task', command: 'echo test', type: 'any', label: 'task' }, { taskName: 'task 2', command: 'echo test', type: 'any', label: 'task 2' }]; + const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource, { extensionId: 'registered', taskType: 'any', properties: {} } as Tasks.TaskDefinition); + assertTaskParseResult(result, { configured: expected }, problemReporter, undefined); }); }); }); @@ -1870,6 +1858,7 @@ function assertTaskParseResult(actual: TaskParseResult, expected: ITestTaskParse index++; } } + problemReporter.clearMessage(); } interface ITestTaskParseResult { From 276e982310b5944b5bb4ba9bcc1340abf20695ed Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 23 May 2022 12:53:43 -0700 Subject: [PATCH 241/942] Update src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts --- .../contrib/tasks/test/common/taskConfiguration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts b/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts index 105d5d3dce660..95fb3eb7b55e8 100644 --- a/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts @@ -1772,7 +1772,7 @@ class TestNamedProblemMatcher implements Partial { class TestParseContext implements Partial { } -suite.only('To task configuration from', () => { +suite('To task configuration from', () => { let instantiationService: TestInstantiationService; let parseContext: ParseContext; let namedProblemMatcher: NamedProblemMatcher; From f5f375d295f4ef7be4d8fdb398d22fccc08142ea Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Mon, 23 May 2022 14:39:33 -0700 Subject: [PATCH 242/942] Scrolling working --- .../browser/interactive.contribution.ts | 16 ++++ .../interactive/browser/interactiveEditor.ts | 8 +- .../browser/diff/notebookTextDiffEditor.ts | 3 + .../notebook/browser/notebookEditorWidget.ts | 12 ++- .../view/renderers/backLayerWebView.ts | 23 +++-- .../browser/view/renderers/webviewMessages.ts | 19 ++-- .../browser/view/renderers/webviewPreloads.ts | 87 +++++++++++-------- 7 files changed, 117 insertions(+), 51 deletions(-) diff --git a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts index 5559096223ee9..ddd9a857eaae7 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts @@ -23,6 +23,7 @@ import { peekViewBorder /*, peekViewEditorBackground, peekViewResultsBackground import { Context as SuggestContext } from 'vs/editor/contrib/suggest/browser/suggest'; import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { EditorActivation } from 'vs/platform/editor/common/editor'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @@ -58,6 +59,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; + Registry.as(EditorExtensions.EditorPane).registerEditorPane( EditorPaneDescriptor.create( InteractiveEditor, @@ -711,3 +713,17 @@ registerThemingParticipant((theme) => { // hc: Color.black // }, localize('interactive.inactiveCodeBackground', 'The backgorund color for the current interactive code cell when the editor does not have focus.')); }); + +Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ + id: 'interactive', + order: 100, + type: 'object', + 'properties': { + 'interactive.alwaysScrollOnNewCell': { + type: 'boolean', + default: true, + markdownDescription: localize('interactive.alwaysScrollOnNewCell', "Automatically scroll the interactive window to show the output of the last statement executed. If this value is false, the window will only scroll if the last cell was already the one scrolled to.") + }, + } +}); + diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts index 52fe78d35bb1c..9265c2fca7972 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts @@ -125,7 +125,7 @@ export class InteractiveEditor extends EditorPane { @INotebookKernelService notebookKernelService: INotebookKernelService, @ILanguageService languageService: ILanguageService, @IKeybindingService keybindingService: IKeybindingService, - @IConfigurationService configurationService: IConfigurationService, + @IConfigurationService private configurationService: IConfigurationService, @IMenuService menuService: IMenuService, @IContextMenuService contextMenuService: IContextMenuService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @@ -402,6 +402,12 @@ export class InteractiveEditor extends EditorPane { this.#notebookWidget.value!.setOptions({ isReadOnly: true }); + this.#widgetDisposableStore.add(this.#notebookWidget.value!.onDidResizeOutput((cvm) => { + // If we're already at the bottom or auto scroll is enabled, scroll to the bottom + if (this.configurationService.getValue('interactive.alwaysScrollOnNewCell') || this.#state === ScrollingState.StickyToBottom) { + this.#notebookWidget.value!.scrollToBottom(); + } + })); this.#widgetDisposableStore.add(this.#notebookWidget.value!.onDidFocusWidget(() => this.#onDidFocusWidget.fire())); this.#widgetDisposableStore.add(model.notebook.onDidChangeContent(() => { (model as ComplexNotebookEditorModel).setDirty(false); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index a791887d6396a..61c5daf994955 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -202,6 +202,9 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD didDropMarkupCell(cellId: string) { // throw new Error('Method not implemented.'); } + didResizeOutput(cellId: string): void { + // throw new Error('Method not implemented.'); + } protected createEditor(parent: HTMLElement): void { this._rootElement = DOM.append(parent, DOM.$('.notebook-text-diff-editor')); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 7197952d03c13..4bd1f431a7ff3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -239,6 +239,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD readonly onDidReceiveMessage: Event = this._onDidReceiveMessage.event; private readonly _onDidRenderOutput = this._register(new Emitter()); private readonly onDidRenderOutput = this._onDidRenderOutput.event; + private readonly _onDidResizeOutputEmitter = this._register(new Emitter()); + readonly onDidResizeOutput = this._onDidResizeOutputEmitter.event; //#endregion @@ -1396,7 +1398,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD didStartDragMarkupCell: that._didStartDragMarkupCell.bind(that), didDragMarkupCell: that._didDragMarkupCell.bind(that), didDropMarkupCell: that._didDropMarkupCell.bind(that), - didEndDragMarkupCell: that._didEndDragMarkupCell.bind(that) + didEndDragMarkupCell: that._didEndDragMarkupCell.bind(that), + didResizeOutput: that._didResizeOutput.bind(that) }, id, resource, { ...this._notebookOptions.computeWebviewOptions(), fontFamily: this._generateFontFamily() @@ -2980,6 +2983,13 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } } + private _didResizeOutput(cellId: string): void { + const cell = this._getCellById(cellId); + if (cell) { + this._onDidResizeOutputEmitter.fire(cell); + } + } + //#endregion //#region Editor Contributions diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 59046e6159e11..513f8b4856eb2 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -36,7 +36,7 @@ import { NOTEBOOK_WEBVIEW_BOUNDARY } from 'vs/workbench/contrib/notebook/browser import { preloadsScriptStr, RendererMetadata } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads'; import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; -import { CellUri, INotebookRendererInfo, NotebookSetting, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellUri, INotebookRendererInfo, NotebookCellExecutionState, NotebookSetting, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookKernel, IResolvedNotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IScopedRendererMessaging } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; @@ -77,6 +77,7 @@ export interface INotebookDelegateForWebview { didDragMarkupCell(cellId: string, event: { dragOffsetY: number }): void; didDropMarkupCell(cellId: string, event: { dragOffsetY: number; ctrlKey: boolean; altKey: boolean }): void; didEndDragMarkupCell(cellId: string): void; + didResizeOutput(cellId: string): void; setScrollTop(scrollTop: number): void; triggerScroll(event: IMouseWheelEvent): void; } @@ -178,12 +179,21 @@ export class BackLayerWebView extends Disposable { })); this._register(notebookExecutionStateService.onDidChangeCellExecution((e) => { - // If e.changed is undefined, it means notebook execution just finished. - if (e.changed === undefined && e.affectsNotebook(this.documentUri)) { + // If e.changed state is pending, it means notebook execution just started + if (e.changed && e.changed.state === NotebookCellExecutionState.Pending && e.affectsNotebook(this.documentUri)) { const cell = this.notebookEditor.getCellByHandle(e.cellHandle); if (cell) { this._sendMessageToWebview({ - type: 'trackFinalOutputRender', + type: 'startWatchingOutputResize', + cellId: cell.id + }); + } + // If e.changed is undefined, it means notebook execution just finished + } else if (e.changed === undefined && e.affectsNotebook(this.documentUri)) { + const cell = this.notebookEditor.getCellByHandle(e.cellHandle); + if (cell) { + this._sendMessageToWebview({ + type: 'stopWatchingOutputResize', cellId: cell.id }); } @@ -835,10 +845,9 @@ var requirejs = (function() { break; } - case 'didRenderFinalOutput': { - console.log(`Rendered final output for ${data.cellId}`); + case 'outputResized': + this.notebookEditor.didResizeOutput(data.cellId); break; - } } })); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index e37283b180904..9c0941b1ffc33 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -401,16 +401,22 @@ export interface IDidFindHighlightMessage extends BaseToWebviewMessage { readonly offset: number; } -export interface ITrackFinalOutputRenderMessage { - readonly type: 'trackFinalOutputRender'; +export interface IStartWatchingOutputMessage { + readonly type: 'startWatchingOutputResize'; readonly cellId: string; } -export interface IDidRenderFinalOutputMessage extends BaseToWebviewMessage { - readonly type: 'didRenderFinalOutput'; +export interface IStopWatchingOutputMessage { + readonly type: 'stopWatchingOutputResize'; readonly cellId: string; } +export interface IOutputResizedMessage extends BaseToWebviewMessage { + readonly type: 'outputResized'; + readonly cellId: string; +} + + export type FromWebviewMessage = WebviewInitialized | IDimensionMessage | IMouseEnterMessage | @@ -439,7 +445,7 @@ export type FromWebviewMessage = WebviewInitialized | IRenderedCellOutputMessage | IDidFindMessage | IDidFindHighlightMessage | - IDidRenderFinalOutputMessage; + IOutputResizedMessage; export type ToWebviewMessage = IClearMessage | IFocusOutputMessage | @@ -470,7 +476,8 @@ export type ToWebviewMessage = IClearMessage | IFindHighlightMessage | IFindUnHighlightMessage | IFindStopMessage | - ITrackFinalOutputRenderMessage; + IStartWatchingOutputMessage | + IStopWatchingOutputMessage; export type AnyMessage = FromWebviewMessage | ToWebviewMessage; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 786cc9377ce19..41efb7a678f4d 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -387,6 +387,7 @@ async function webviewPreloads(ctx: PreloadContext) { function createFocusSink(cellId: string, focusNext?: boolean) { const element = document.createElement('div'); + element.id = `focus-sink-${cellId}`; element.tabIndex = 0; element.addEventListener('focus', () => { postNotebookMessage('focus-editor', { @@ -1172,8 +1173,12 @@ async function webviewPreloads(ctx: PreloadContext) { _highlighter?.dispose(); break; } - case 'trackFinalOutputRender': { - viewModel.trackFinalOutput(event.data.cellId); + case 'startWatchingOutputResize': { + viewModel.startWatchingOutput(event.data.cellId); + break; + } + case 'stopWatchingOutputResize': { + viewModel.stopWatchingOutput(event.data.cellId); break; } } @@ -1398,7 +1403,9 @@ async function webviewPreloads(ctx: PreloadContext) { private readonly _markupCells = new Map(); private readonly _outputCells = new Map(); - private _pendingFinalOutput: string | undefined; + private _trackingResize: string | undefined; + private _outputResizeObservers: ResizeObserver[] = []; + private _outputResizeTimer: any; public clearAll() { this._markupCells.clear(); @@ -1410,13 +1417,14 @@ async function webviewPreloads(ctx: PreloadContext) { this.renderOutputCells(); } - public trackFinalOutput(cellId: string) { - const cell = this._outputCells.get(cellId); - if (cell) { - cell.addFinalOutputTracker(); - } else { - this._pendingFinalOutput = cellId; - } + public startWatchingOutput(cellId: string) { + this._trackingResize = cellId; + this._outputResizeObservers.forEach(o => o.disconnect()); + this._outputResizeObservers = []; + } + + public stopWatchingOutput(cellId: string) { + this._trackingResize = undefined; } private async createMarkupCell(init: webviewMessages.IMarkupCellInitialization, top: number, visible: boolean): Promise { @@ -1529,14 +1537,11 @@ async function webviewPreloads(ctx: PreloadContext) { const outputNode = cellOutput.createOutputElement(data.outputId, data.outputOffset, data.left); outputNode.render(data.content, preloadsAndErrors); + // Track resizes on this output + this.trackOutputResize(data.cellId, outputNode.element); + // don't hide until after this step so that the height is right cellOutput.element.style.visibility = data.initiallyHidden ? 'hidden' : 'visible'; - - // If notebook side has indicated we are done (and we haven't added it yet), stick in the final render - if (data.cellId === this._pendingFinalOutput) { - this._pendingFinalOutput = undefined; - cellOutput.addFinalOutputTracker(); - } } public ensureOutputCell(cellId: string, cellTop: number, skipCellTopUpdateIfExist: boolean): OutputCell { @@ -1586,6 +1591,33 @@ async function webviewPreloads(ctx: PreloadContext) { cell?.updateScroll(request); } } + + private outputResizeHandler(cellId: string) { + // If no longer tracking, disconnect. However + // send one more resize event. + if (this._trackingResize !== cellId) { + this._outputResizeObservers.forEach(o => o.disconnect()); + this._outputResizeObservers = []; + } + + // Debounce this callback to only happen after + // 250 ms. Don't need resize events that often. + clearTimeout(this._outputResizeTimer); + this._outputResizeTimer = setTimeout(() => { + postNotebookMessage('outputResized', { + cellId + }); + }, 250); + } + + private trackOutputResize(cellId: string, outputContainer: HTMLElement) { + if (this._trackingResize === cellId) { + const handler = this.outputResizeHandler.bind(this, cellId); + const observer = new ResizeObserver(handler); + this._outputResizeObservers.push(observer); + observer.observe(outputContainer); + } + } }(); class MarkdownCodeBlock { @@ -1819,6 +1851,7 @@ async function webviewPreloads(ctx: PreloadContext) { class OutputCell { public readonly element: HTMLElement; + public readonly bottomElement: HTMLElement; private readonly outputElements = new Map(); @@ -1838,8 +1871,8 @@ async function webviewPreloads(ctx: PreloadContext) { this.element = this.element; - const lowerWrapperElement = createFocusSink(cellId, true); - container.appendChild(lowerWrapperElement); + this.bottomElement = createFocusSink(cellId, true); + container.appendChild(this.bottomElement); // New thoughts. // Send message to indicate force scroll. @@ -1905,24 +1938,6 @@ async function webviewPreloads(ctx: PreloadContext) { this.element.style.visibility = 'visible'; } } - - public addFinalOutputTracker() { - const id = `${this.element.id}-final-output-tracker`; - let tracker: HTMLImageElement | null = document.getElementById(id) as HTMLImageElement; - if (!tracker) { - tracker = document.createElement('img') as HTMLImageElement; - tracker.id = id; - tracker.style.height = '0px'; - tracker.src = ''; - tracker.addEventListener('error', (ev) => { - console.log(`Final cell output has rendered`); - - // Src is empty so this should fire as soon as it renders - postNotebookMessage('didRenderFinalOutput', { cellId: this.element.id }); - }); - this.element.appendChild(tracker); - } - } } class OutputContainer { From a6eb00568fb0ff6e509814108b75da18c27fe366 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 23 May 2022 23:59:17 +0200 Subject: [PATCH 243/942] - Add Show Extension option to Install Anyway dialog (#150218) - Provide Migrate extension action for deprecated extensions --- .../abstractExtensionManagementService.ts | 7 -- .../extensions/browser/extensionEditor.ts | 3 +- .../extensions/browser/extensionsActions.ts | 81 ++++++++++++++++--- .../browser/media/extensionActions.css | 1 + 4 files changed, 73 insertions(+), 19 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 94e9a31abc7f8..9279c520625d4 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -363,13 +363,6 @@ export abstract class AbstractExtensionManagementService extends Disposable impl throw new ExtensionManagementError(nls.localize('malicious extension', "Can't install '{0}' extension since it was reported to be problematic.", extension.identifier.id), ExtensionManagementErrorCode.Malicious); } - const deprecated = report.deprecated[extension.identifier.id.toLowerCase()]; - if (deprecated?.disallowInstall) { - const message = deprecated.extension ? nls.localize('unsupported extension with alternative', "Can't install '{0}' extension because it is deprecated. Use {1} extension instead.", extension.identifier.id, deprecated.extension.displayName) - : nls.localize('unsupported extension without alternative and no message', "Can't install '{0}' extension because it is deprecated.", extension.identifier.id); - throw new ExtensionManagementError(message, ExtensionManagementErrorCode.Deprecated); - } - if (!await this.canInstall(extension)) { const targetPlatform = await this.getTargetPlatform(); throw new ExtensionManagementError(nls.localize('incompatible platform', "The '{0}' extension is not available in {1} for {2}.", extension.identifier.id, this.productService.nameLong, TargetPlatformToString(targetPlatform)), ExtensionManagementErrorCode.IncompatibleTargetPlatform); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index da2bc141afc45..eb5589276f25e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -29,7 +29,7 @@ import { UpdateAction, ReloadAction, EnableDropDownAction, DisableDropDownAction, ExtensionStatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ToggleSyncExtensionAction, SetProductIconThemeAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, - InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction + InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtension } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; @@ -338,6 +338,7 @@ export class ExtensionEditor extends EditorPane { this.instantiationService.createInstance(InstallingLabelAction), this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.uninstall', UninstallAction.UninstallLabel, [ [ + this.instantiationService.createInstance(MigrateDeprecatedExtension, false), this.instantiationService.createInstance(UninstallAction), this.instantiationService.createInstance(InstallAnotherVersionAction), ] diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index e578b8359cbf1..7032389c964fa 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -276,13 +276,22 @@ export abstract class AbstractInstallAction extends ExtensionAction { } if (this.extension.deprecationInfo) { - const result = await this.dialogService.confirm({ - type: 'warning', - message: localize('install confirmation', "Are you sure you want to install '{0}'?", this.extension.displayName), - detail: localize('deprecated message', "This extension is no longer being maintained and is deprecated."), - primaryButton: localize('install anyway', "Install Anyway"), - }); - if (!result.confirmed) { + const result = await this.dialogService.show( + Severity.Warning, + localize('install confirmation', "Are you sure you want to install '{0}'?", this.extension.displayName), + [ + localize('install anyway', "Install Anyway"), + localize('open extension', "Open Extension"), + localize('cancel', "Cancel"), + ], + { + detail: localize('deprecated message', "This extension is no longer being maintained and is deprecated."), + cancelId: 2, + }); + if (result.choice === 1) { + return this.extensionsWorkbenchService.open(this.extension, { showPreReleaseVersion: this.installPreReleaseVersion }); + } + if (result.choice === 2) { return; } } @@ -757,10 +766,15 @@ export class UpdateAction extends ExtensionAction { } private async computeAndUpdateEnablement(): Promise { + this.enabled = false; + this.class = UpdateAction.DisabledClass; + this.label = this.getLabel(); + if (!this.extension) { - this.enabled = false; - this.class = UpdateAction.DisabledClass; - this.label = this.getLabel(); + return; + } + + if (this.extension.deprecationInfo) { return; } @@ -800,6 +814,51 @@ export class UpdateAction extends ExtensionAction { } } +export class MigrateDeprecatedExtension extends ExtensionAction { + + private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} prominent migrate`; + private static readonly DisabledClass = `${MigrateDeprecatedExtension.EnabledClass} disabled`; + + constructor( + private readonly small: boolean, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super('extensions.uninstall', localize('migrateExtension', "Migrate"), MigrateDeprecatedExtension.DisabledClass, false); + this.update(); + } + + update(): void { + this.enabled = false; + this.class = MigrateDeprecatedExtension.DisabledClass; + if (!this.extension?.local) { + return; + } + if (this.extension.state !== ExtensionState.Installed) { + return; + } + if (!this.extension.deprecationInfo?.extension) { + return; + } + const id = this.extension.deprecationInfo.extension.id; + if (this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, { id }))) { + return; + } + this.enabled = true; + this.class = MigrateDeprecatedExtension.EnabledClass; + this.tooltip = localize('migrate to', "Migrate to {0}", this.extension.deprecationInfo.extension.displayName); + this.label = this.small ? localize('migrate', "Migrate") : this.tooltip; + } + + override async run(): Promise { + if (!this.extension?.deprecationInfo?.extension) { + return; + } + await this.extensionsWorkbenchService.uninstall(this.extension); + const [extension] = await this.extensionsWorkbenchService.getExtensions([{ id: this.extension.deprecationInfo.extension.id, preRelease: this.extension.deprecationInfo?.extension?.preRelease }], CancellationToken.None); + await this.extensionsWorkbenchService.install(extension); + } +} + export class ExtensionActionWithDropdownActionViewItem extends ActionWithDropdownActionViewItem { constructor( @@ -1138,7 +1197,7 @@ export class InstallAnotherVersionAction extends ExtensionAction { } update(): void { - this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery && !!this.extension.local && !!this.extension.server && this.extension.state === ExtensionState.Installed; + this.enabled = !!this.extension && !this.extension.isBuiltin && !!this.extension.gallery && !!this.extension.local && !!this.extension.server && this.extension.state === ExtensionState.Installed && !this.extension.deprecationInfo; } override async run(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css index 75327c8834404..13d0d87abca06 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css @@ -52,6 +52,7 @@ .monaco-action-bar .action-item.disabled .action-label.extension-action.uninstall:not(.uninstalling), .monaco-action-bar .action-item.disabled .action-label.extension-action.hide-when-disabled, .monaco-action-bar .action-item.disabled .action-label.extension-action.update, +.monaco-action-bar .action-item.disabled .action-label.extension-action.migrate, .monaco-action-bar .action-item.disabled .action-label.extension-action.theme, .monaco-action-bar .action-item.disabled .action-label.extension-action.extension-sync, .monaco-action-bar .action-item.action-dropdown-item.disabled, From b8420578fd0acf3c5593503b1f918c683d30304c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 23 May 2022 15:30:42 -0700 Subject: [PATCH 244/942] Add additional allowed tags in untrusted notebook markdown (#150228) This adds some extra allowed tags when rendering markdown in notebooks. Additions are: ``` "b", "br", "caption", "center", "col", "colgroup", "details", "em", "font", "i", "kbd", "sub", "summary", "sup", "table", "tbody", "tfoot", "thead", "td", "th", "tr", "tt", "u", "video" ``` --- .../notebook/index.ts | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/notebook/index.ts b/extensions/markdown-language-features/notebook/index.ts index 079ed928f3f57..a036374fed1d5 100644 --- a/extensions/markdown-language-features/notebook/index.ts +++ b/extensions/markdown-language-features/notebook/index.ts @@ -9,7 +9,57 @@ import type * as MarkdownItToken from 'markdown-it/lib/token'; import type { ActivationFunction } from 'vscode-notebook-renderer'; const sanitizerOptions: DOMPurify.Config = { - ALLOWED_TAGS: ['a', 'button', 'blockquote', 'code', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'img', 'input', 'label', 'li', 'p', 'pre', 'select', 'small', 'span', 'strong', 'textarea', 'ul', 'ol'], + ALLOWED_TAGS: [ + 'a', + 'b', + 'blockquote', + 'br', + 'button', + 'caption', + 'center', + 'code', + 'col', + 'colgroup', + 'details', + 'div', + 'em', + 'font', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'hr', + 'i', + 'img', + 'input', + 'kbd', + 'label', + 'li', + 'ol', + 'p', + 'pre', + 'select', + 'small', + 'span', + 'strong', + 'sub', + 'summary', + 'sup', + 'table', + 'tbody', + 'td', + 'textarea', + 'tfoot', + 'th', + 'thead', + 'tr', + 'tt', + 'u', + 'ul', + 'video', + ], }; export const activate: ActivationFunction = (ctx) => { From 033e4eadda87433c107b2ff106039101fc82dacd Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Mon, 23 May 2022 15:42:51 -0700 Subject: [PATCH 245/942] Always watch resize so can handle post execute resizes --- .../interactive/browser/interactiveEditor.ts | 10 +++-- .../view/renderers/backLayerWebView.ts | 24 +---------- .../browser/view/renderers/webviewMessages.ts | 14 +------ .../browser/view/renderers/webviewPreloads.ts | 42 +++++-------------- 4 files changed, 19 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts index 9265c2fca7972..5093e65b993ad 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts @@ -403,9 +403,13 @@ export class InteractiveEditor extends EditorPane { isReadOnly: true }); this.#widgetDisposableStore.add(this.#notebookWidget.value!.onDidResizeOutput((cvm) => { - // If we're already at the bottom or auto scroll is enabled, scroll to the bottom - if (this.configurationService.getValue('interactive.alwaysScrollOnNewCell') || this.#state === ScrollingState.StickyToBottom) { - this.#notebookWidget.value!.scrollToBottom(); + // Ignore resizes on anything but the last cell. + const index = this.#notebookWidget.value!.getCellIndex(cvm); + if (index === this.#notebookWidget.value!.getLength() - 1) { + // If we're already at the bottom or auto scroll is enabled, scroll to the bottom + if (this.configurationService.getValue('interactive.alwaysScrollOnNewCell') || this.#state === ScrollingState.StickyToBottom) { + this.#notebookWidget.value!.scrollToBottom(); + } } })); this.#widgetDisposableStore.add(this.#notebookWidget.value!.onDidFocusWidget(() => this.#onDidFocusWidget.fire())); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 513f8b4856eb2..af57430b06ec8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -36,7 +36,7 @@ import { NOTEBOOK_WEBVIEW_BOUNDARY } from 'vs/workbench/contrib/notebook/browser import { preloadsScriptStr, RendererMetadata } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads'; import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; -import { CellUri, INotebookRendererInfo, NotebookCellExecutionState, NotebookSetting, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellUri, INotebookRendererInfo, NotebookSetting, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookKernel, IResolvedNotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IScopedRendererMessaging } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; @@ -177,28 +177,6 @@ export class BackLayerWebView extends Disposable { css: getTokenizationCss(), }); })); - - this._register(notebookExecutionStateService.onDidChangeCellExecution((e) => { - // If e.changed state is pending, it means notebook execution just started - if (e.changed && e.changed.state === NotebookCellExecutionState.Pending && e.affectsNotebook(this.documentUri)) { - const cell = this.notebookEditor.getCellByHandle(e.cellHandle); - if (cell) { - this._sendMessageToWebview({ - type: 'startWatchingOutputResize', - cellId: cell.id - }); - } - // If e.changed is undefined, it means notebook execution just finished - } else if (e.changed === undefined && e.affectsNotebook(this.documentUri)) { - const cell = this.notebookEditor.getCellByHandle(e.cellHandle); - if (cell) { - this._sendMessageToWebview({ - type: 'stopWatchingOutputResize', - cellId: cell.id - }); - } - } - })); } updateOptions(options: BacklayerWebviewOptions) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index 9c0941b1ffc33..4888d56bd8fe4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -401,16 +401,6 @@ export interface IDidFindHighlightMessage extends BaseToWebviewMessage { readonly offset: number; } -export interface IStartWatchingOutputMessage { - readonly type: 'startWatchingOutputResize'; - readonly cellId: string; -} - -export interface IStopWatchingOutputMessage { - readonly type: 'stopWatchingOutputResize'; - readonly cellId: string; -} - export interface IOutputResizedMessage extends BaseToWebviewMessage { readonly type: 'outputResized'; readonly cellId: string; @@ -475,9 +465,7 @@ export type ToWebviewMessage = IClearMessage | IFindMessage | IFindHighlightMessage | IFindUnHighlightMessage | - IFindStopMessage | - IStartWatchingOutputMessage | - IStopWatchingOutputMessage; + IFindStopMessage; export type AnyMessage = FromWebviewMessage | ToWebviewMessage; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 41efb7a678f4d..5aff1145fd87e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -1173,14 +1173,6 @@ async function webviewPreloads(ctx: PreloadContext) { _highlighter?.dispose(); break; } - case 'startWatchingOutputResize': { - viewModel.startWatchingOutput(event.data.cellId); - break; - } - case 'stopWatchingOutputResize': { - viewModel.stopWatchingOutput(event.data.cellId); - break; - } } }); @@ -1403,13 +1395,14 @@ async function webviewPreloads(ctx: PreloadContext) { private readonly _markupCells = new Map(); private readonly _outputCells = new Map(); - private _trackingResize: string | undefined; private _outputResizeObservers: ResizeObserver[] = []; private _outputResizeTimer: any; public clearAll() { this._markupCells.clear(); this._outputCells.clear(); + this._outputResizeObservers.forEach(o => o.disconnect()); + this._outputResizeObservers = []; } public rerender() { @@ -1417,16 +1410,6 @@ async function webviewPreloads(ctx: PreloadContext) { this.renderOutputCells(); } - public startWatchingOutput(cellId: string) { - this._trackingResize = cellId; - this._outputResizeObservers.forEach(o => o.disconnect()); - this._outputResizeObservers = []; - } - - public stopWatchingOutput(cellId: string) { - this._trackingResize = undefined; - } - private async createMarkupCell(init: webviewMessages.IMarkupCellInitialization, top: number, visible: boolean): Promise { const existing = this._markupCells.get(init.cellId); if (existing) { @@ -1550,6 +1533,10 @@ async function webviewPreloads(ctx: PreloadContext) { if (!cell) { cell = new OutputCell(cellId); this._outputCells.set(cellId, cell); + + // New output cell, clear resize handlers + this._outputResizeObservers.forEach(o => o.disconnect); + this._outputResizeObservers = []; } if (existed && skipCellTopUpdateIfExist) { @@ -1593,13 +1580,6 @@ async function webviewPreloads(ctx: PreloadContext) { } private outputResizeHandler(cellId: string) { - // If no longer tracking, disconnect. However - // send one more resize event. - if (this._trackingResize !== cellId) { - this._outputResizeObservers.forEach(o => o.disconnect()); - this._outputResizeObservers = []; - } - // Debounce this callback to only happen after // 250 ms. Don't need resize events that often. clearTimeout(this._outputResizeTimer); @@ -1611,12 +1591,10 @@ async function webviewPreloads(ctx: PreloadContext) { } private trackOutputResize(cellId: string, outputContainer: HTMLElement) { - if (this._trackingResize === cellId) { - const handler = this.outputResizeHandler.bind(this, cellId); - const observer = new ResizeObserver(handler); - this._outputResizeObservers.push(observer); - observer.observe(outputContainer); - } + const handler = this.outputResizeHandler.bind(this, cellId); + const observer = new ResizeObserver(handler); + this._outputResizeObservers.push(observer); + observer.observe(outputContainer); } }(); From 7075f2db1eed67ec4b079bec9a97fee96788e16d Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Mon, 23 May 2022 16:05:31 -0700 Subject: [PATCH 246/942] Move out Settings editor indicator label impl (#150230) This PR reduces technical debt. --- .../preferences/browser/settingsEditor2.ts | 3 +- .../settingsEditorSettingIndicators.ts | 172 +++++++++++++++++ .../preferences/browser/settingsTree.ts | 178 ++---------------- 3 files changed, 188 insertions(+), 165 deletions(-) create mode 100644 src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 4bbcafeb7c2d5..76234de0e4879 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -40,7 +40,7 @@ import { IEditorMemento, IEditorOpenContext, IEditorPane } from 'vs/workbench/co import { attachSuggestEnabledInputBoxStyler, SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; import { commonlyUsedData, tocData } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; -import { AbstractSettingRenderer, HeightChangeParams, ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveConfiguredUntrustedSettings, createTocTreeForExtensionSettings, resolveSettingsTree, SettingsTree, SettingTreeRenderers } from 'vs/workbench/contrib/preferences/browser/settingsTree'; +import { AbstractSettingRenderer, HeightChangeParams, ISettingLinkClickEvent, resolveConfiguredUntrustedSettings, createTocTreeForExtensionSettings, resolveSettingsTree, SettingsTree, SettingTreeRenderers } from 'vs/workbench/contrib/preferences/browser/settingsTree'; import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree'; import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, ENABLE_LANGUAGE_FILTER, EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, ID_SETTING_TAG, IPreferencesSearchService, ISearchProvider, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG, POLICY_SETTING_TAG, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SUGGEST_FILTERS, WORKSPACE_TRUST_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; @@ -60,6 +60,7 @@ import { Color } from 'vs/base/common/color'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { SettingsSearchFilterDropdownMenuActionViewItem } from 'vs/workbench/contrib/preferences/browser/settingsSearchMenu'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ISettingOverrideClickEvent } from 'vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators'; export const enum SettingsFocusContext { Search, diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts new file mode 100644 index 0000000000000..2f5cfdd2caa42 --- /dev/null +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts @@ -0,0 +1,172 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel'; +import { Emitter } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; +import { getDefaultIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync'; +import { SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; + +const $ = DOM.$; + +export interface ISettingOverrideClickEvent { + scope: string; + targetKey: string; +} + +/** + * Renders the indicators next to a setting, such as Sync Ignored, Also Modified In, etc. + */ +export class SettingsTreeIndicatorsLabel { + /** + * This element wraps around the other elements. + */ + private labelElement: HTMLElement; + private scopeOverridesElement: HTMLElement; + private syncIgnoredElement: HTMLElement; + private defaultOverrideIndicatorElement: HTMLElement; + private defaultOverrideIndicatorLabel: SimpleIconLabel; + + constructor(container: HTMLElement) { + this.labelElement = DOM.append(container, $('.misc-label')); + this.labelElement.style.display = 'inline'; + + this.scopeOverridesElement = this.createScopeOverridesElement(); + this.syncIgnoredElement = this.createSyncIgnoredElement(); + const { element: defaultOverrideElement, label: defaultOverrideLabel } = this.createDefaultOverrideIndicator(); + this.defaultOverrideIndicatorElement = defaultOverrideElement; + this.defaultOverrideIndicatorLabel = defaultOverrideLabel; + } + + private createScopeOverridesElement(): HTMLElement { + const otherOverridesElement = $('span.setting-item-overrides'); + return otherOverridesElement; + } + + private createSyncIgnoredElement(): HTMLElement { + const syncIgnoredElement = $('span.setting-item-ignored'); + const syncIgnoredLabel = new SimpleIconLabel(syncIgnoredElement); + syncIgnoredLabel.text = `$(sync-ignored) ${localize('extensionSyncIgnoredLabel', 'Sync: Ignored')}`; + syncIgnoredLabel.title = localize('syncIgnoredTitle', "Settings sync does not sync this setting"); + return syncIgnoredElement; + } + + private createDefaultOverrideIndicator(): { element: HTMLElement; label: SimpleIconLabel } { + const defaultOverrideIndicator = $('span.setting-item-default-overridden'); + const defaultOverrideLabel = new SimpleIconLabel(defaultOverrideIndicator); + return { element: defaultOverrideIndicator, label: defaultOverrideLabel }; + } + + private render() { + const elementsToShow = [this.scopeOverridesElement, this.syncIgnoredElement, this.defaultOverrideIndicatorElement].filter(element => { + return element.style.display !== 'none'; + }); + + this.labelElement.innerText = ''; + this.labelElement.style.display = 'none'; + if (elementsToShow.length) { + this.labelElement.style.display = 'inline'; + DOM.append(this.labelElement, $('span', undefined, '(')); + for (let i = 0; i < elementsToShow.length - 1; i++) { + DOM.append(this.labelElement, elementsToShow[i]); + DOM.append(this.labelElement, $('span.comma', undefined, ', ')); + } + DOM.append(this.labelElement, elementsToShow[elementsToShow.length - 1]); + DOM.append(this.labelElement, $('span', undefined, ')')); + } + } + + updateSyncIgnored(element: SettingsTreeSettingElement, ignoredSettings: string[]) { + this.syncIgnoredElement.style.display = ignoredSettings.includes(element.setting.key) ? 'inline' : 'none'; + this.render(); + } + + updateScopeOverrides(element: SettingsTreeSettingElement, elementDisposables: DisposableStore, onDidClickOverrideElement: Emitter) { + this.scopeOverridesElement.innerText = ''; + this.scopeOverridesElement.style.display = 'none'; + if (element.overriddenScopeList.length) { + this.scopeOverridesElement.style.display = 'inline'; + const otherOverridesLabel = element.isConfigured ? + localize('alsoConfiguredIn', "Also modified in") : + localize('configuredIn', "Modified in"); + + DOM.append(this.scopeOverridesElement, $('span', undefined, `${otherOverridesLabel}: `)); + + for (let i = 0; i < element.overriddenScopeList.length; i++) { + const view = DOM.append(this.scopeOverridesElement, $('a.modified-scope', undefined, element.overriddenScopeList[i])); + + if (i !== element.overriddenScopeList.length - 1) { + DOM.append(this.scopeOverridesElement, $('span', undefined, ', ')); + } + + elementDisposables.add( + DOM.addStandardDisposableListener(view, DOM.EventType.CLICK, (e: IMouseEvent) => { + onDidClickOverrideElement.fire({ + targetKey: element.setting.key, + scope: element.overriddenScopeList[i] + }); + e.preventDefault(); + e.stopPropagation(); + })); + } + } + this.render(); + } + + updateDefaultOverrideIndicator(element: SettingsTreeSettingElement) { + this.defaultOverrideIndicatorElement.style.display = 'none'; + const defaultValueSource = element.setting.defaultValueSource; + if (defaultValueSource) { + this.defaultOverrideIndicatorElement.style.display = 'inline'; + if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { + const extensionSource = defaultValueSource.displayName ?? defaultValueSource.id; + this.defaultOverrideIndicatorLabel.title = localize('defaultOverriddenDetails', "Default setting value overridden by {0}", extensionSource); + this.defaultOverrideIndicatorLabel.text = localize('defaultOverrideLabelText', "$(replace) {0}", extensionSource); + } else if (typeof defaultValueSource === 'string') { + this.defaultOverrideIndicatorLabel.title = localize('defaultOverriddenDetails', "Default setting value overridden by {0}", defaultValueSource); + this.defaultOverrideIndicatorLabel.text = localize('defaultOverrideLabelText', "$(replace) {0}", defaultValueSource); + } + } + this.render(); + } +} + +export function getIndicatorsLabelAriaLabel(element: SettingsTreeSettingElement, configurationService: IConfigurationService): string { + const ariaLabelSections: string[] = []; + + // Add other overrides text + const otherOverridesStart = element.isConfigured ? + localize('alsoConfiguredIn', "Also modified in") : + localize('configuredIn', "Modified in"); + const otherOverridesList = element.overriddenScopeList.join(', '); + if (element.overriddenScopeList.length) { + ariaLabelSections.push(`${otherOverridesStart} ${otherOverridesList}`); + } + + // Add sync ignored text + const ignoredSettings = getIgnoredSettings(getDefaultIgnoredSettings(), configurationService); + if (ignoredSettings.includes(element.setting.key)) { + ariaLabelSections.push(localize('syncIgnoredTitle', "Settings sync does not sync this setting")); + } + + // Add default override indicator text + if (element.setting.defaultValueSource) { + const defaultValueSource = element.setting.defaultValueSource; + if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { + const extensionSource = defaultValueSource.displayName ?? defaultValueSource.id; + ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", extensionSource)); + } else if (typeof defaultValueSource === 'string') { + ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", defaultValueSource)); + } + } + + const ariaLabel = ariaLabelSections.join('. '); + return ariaLabel; +} diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 2e2ee4e7257e8..5a9aa77dcf2b0 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -62,6 +62,7 @@ import { SettingsTarget } from 'vs/workbench/contrib/preferences/browser/prefere import { MarkdownRenderer } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { focusedRowBackground, focusedRowBorder, rowHoverBackground, settingsHeaderForeground, settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; +import { getIndicatorsLabelAriaLabel, ISettingOverrideClickEvent, SettingsTreeIndicatorsLabel } from 'vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators'; const $ = DOM.$; @@ -588,7 +589,7 @@ interface ISettingItemTemplate extends IDisposableTemplate { descriptionElement: HTMLElement; controlElement: HTMLElement; deprecationWarningElement: HTMLElement; - miscLabel: SettingsTreeMiscLabel; + indicatorsLabel: SettingsTreeIndicatorsLabel; toolbar: ToolBar; elementDisposables: DisposableStore; } @@ -666,11 +667,6 @@ export interface ISettingLinkClickEvent { targetKey: string; } -export interface ISettingOverrideClickEvent { - scope: string; - targetKey: string; -} - function removeChildrenFromTabOrder(node: Element): void { const focusableElements = node.querySelectorAll(` [tabindex="0"], @@ -783,7 +779,8 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const categoryElement = DOM.append(labelCategoryContainer, $('span.setting-item-category')); const labelElementContainer = DOM.append(labelCategoryContainer, $('span.setting-item-label')); const labelElement = new SimpleIconLabel(labelElementContainer); - const miscLabel = new SettingsTreeMiscLabel(titleElement); + const indicatorsLabel = new SettingsTreeIndicatorsLabel(titleElement); + const descriptionElement = DOM.append(container, $('.setting-item-description')); const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator')); modifiedIndicatorElement.title = localize('modified', "The setting has been configured in the current scope."); @@ -810,7 +807,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre descriptionElement, controlElement, deprecationWarningElement, - miscLabel, + indicatorsLabel, toolbar }; @@ -914,7 +911,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre template.descriptionElement.innerText = element.description; } - template.miscLabel.updateOtherOverrides(element, template.elementDisposables, this._onDidClickOverrideElement); + template.indicatorsLabel.updateScopeOverrides(element, template.elementDisposables, this._onDidClickOverrideElement); const onChange = (value: any) => this._onDidChangeSetting.fire({ key: element.setting.key, value, type: template.context!.valueType, manualReset: false }); const deprecationText = element.setting.deprecationMessage || ''; @@ -931,10 +928,10 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre this.renderValue(element, template, onChange); - template.miscLabel.updateSyncIgnored(element, this.ignoredSettings); - template.miscLabel.updateDefaultOverrideIndicator(element); + template.indicatorsLabel.updateSyncIgnored(element, this.ignoredSettings); + template.indicatorsLabel.updateDefaultOverrideIndicator(element); template.elementDisposables.add(this.onDidChangeIgnoredSettings(() => { - template.miscLabel.updateSyncIgnored(element, this.ignoredSettings); + template.indicatorsLabel.updateSyncIgnored(element, this.ignoredSettings); })); template.policyWarningElement.hidden = !element.hasPolicyValue; @@ -1819,7 +1816,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre const categoryElement = DOM.append(titleElement, $('span.setting-item-category')); const labelElementContainer = DOM.append(titleElement, $('span.setting-item-label')); const labelElement = new SimpleIconLabel(labelElementContainer); - const miscLabel = new SettingsTreeMiscLabel(titleElement); + const indicatorsLabel = new SettingsTreeIndicatorsLabel(titleElement); const descriptionAndValueElement = DOM.append(container, $('.setting-item-value-description')); const controlElement = DOM.append(descriptionAndValueElement, $('.setting-item-bool-control')); @@ -1870,7 +1867,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre policyWarningElement, descriptionElement, deprecationWarningElement, - miscLabel, + indicatorsLabel, toolbar }; @@ -2142,120 +2139,6 @@ function escapeInvisibleChars(enumValue: string): string { .replace(/\r/g, '\\r'); } -/** - * Controls logic and rendering of the label next to each setting header. - * For example, the "Modified by" and "Overridden by" labels go here. - */ -class SettingsTreeMiscLabel { - private labelElement: HTMLElement; - private otherOverridesElement: HTMLElement; - private syncIgnoredElement: HTMLElement; - private defaultOverrideIndicatorElement: HTMLElement; - private defaultOverrideIndicatorLabel: SimpleIconLabel; - - constructor(container: HTMLElement) { - this.labelElement = DOM.append(container, $('.misc-label')); - this.labelElement.style.display = 'inline'; - - this.otherOverridesElement = this.createOtherOverridesElement(); - this.syncIgnoredElement = this.createSyncIgnoredElement(); - const { element, label } = this.createDefaultOverrideIndicator(); - this.defaultOverrideIndicatorElement = element; - this.defaultOverrideIndicatorLabel = label; - } - - private createOtherOverridesElement(): HTMLElement { - const otherOverridesElement = $('span.setting-item-overrides'); - return otherOverridesElement; - } - - private createSyncIgnoredElement(): HTMLElement { - const syncIgnoredElement = $('span.setting-item-ignored'); - const syncIgnoredLabel = new SimpleIconLabel(syncIgnoredElement); - syncIgnoredLabel.text = `$(sync-ignored) ${localize('extensionSyncIgnoredLabel', 'Sync: Ignored')}`; - syncIgnoredLabel.title = localize('syncIgnoredTitle', "Settings sync does not sync this setting"); - return syncIgnoredElement; - } - - private createDefaultOverrideIndicator(): { element: HTMLElement; label: SimpleIconLabel } { - const defaultOverrideIndicator = $('span.setting-item-default-overridden'); - const defaultOverrideLabel = new SimpleIconLabel(defaultOverrideIndicator); - return { element: defaultOverrideIndicator, label: defaultOverrideLabel }; - } - - private render() { - const elementsToShow = [this.otherOverridesElement, this.syncIgnoredElement, this.defaultOverrideIndicatorElement].filter(element => { - return element.style.display !== 'none'; - }); - - this.labelElement.innerText = ''; - this.labelElement.style.display = 'none'; - if (elementsToShow.length) { - this.labelElement.style.display = 'inline'; - DOM.append(this.labelElement, $('span', undefined, '(')); - for (let i = 0; i < elementsToShow.length - 1; i++) { - DOM.append(this.labelElement, elementsToShow[i]); - DOM.append(this.labelElement, $('span.comma', undefined, ', ')); - } - DOM.append(this.labelElement, elementsToShow[elementsToShow.length - 1]); - DOM.append(this.labelElement, $('span', undefined, ')')); - } - } - - updateSyncIgnored(element: SettingsTreeSettingElement, ignoredSettings: string[]) { - this.syncIgnoredElement.style.display = ignoredSettings.includes(element.setting.key) ? 'inline' : 'none'; - this.render(); - } - - updateOtherOverrides(element: SettingsTreeSettingElement, elementDisposables: DisposableStore, onDidClickOverrideElement: Emitter) { - this.otherOverridesElement.innerText = ''; - this.otherOverridesElement.style.display = 'none'; - if (element.overriddenScopeList.length) { - this.otherOverridesElement.style.display = 'inline'; - const otherOverridesLabel = element.isConfigured ? - localize('alsoConfiguredIn', "Also modified in") : - localize('configuredIn', "Modified in"); - - DOM.append(this.otherOverridesElement, $('span', undefined, `${otherOverridesLabel}: `)); - - for (let i = 0; i < element.overriddenScopeList.length; i++) { - const view = DOM.append(this.otherOverridesElement, $('a.modified-scope', undefined, element.overriddenScopeList[i])); - - if (i !== element.overriddenScopeList.length - 1) { - DOM.append(this.otherOverridesElement, $('span', undefined, ', ')); - } - - elementDisposables.add( - DOM.addStandardDisposableListener(view, DOM.EventType.CLICK, (e: IMouseEvent) => { - onDidClickOverrideElement.fire({ - targetKey: element.setting.key, - scope: element.overriddenScopeList[i] - }); - e.preventDefault(); - e.stopPropagation(); - })); - } - } - this.render(); - } - - updateDefaultOverrideIndicator(element: SettingsTreeSettingElement) { - this.defaultOverrideIndicatorElement.style.display = 'none'; - if (element.setting.defaultValueSource) { - this.defaultOverrideIndicatorElement.style.display = 'inline'; - const defaultValueSource = element.setting.defaultValueSource; - if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { - const extensionSource = defaultValueSource.displayName ?? defaultValueSource.id; - this.defaultOverrideIndicatorLabel.title = localize('defaultOverriddenDetails', "Default value overridden by {0}", extensionSource); - this.defaultOverrideIndicatorLabel.text = localize('defaultOverrideLabelText', "$(wrench) Overridden by: {0}", extensionSource); - } else if (typeof defaultValueSource === 'string') { - this.defaultOverrideIndicatorLabel.title = localize('defaultOverriddenDetails', "Default value overridden by {0}", defaultValueSource); - this.defaultOverrideIndicatorLabel.text = localize('defaultOverrideLabelText', "$(wrench) Overridden by: {0}", defaultValueSource); - } - } - this.render(); - } -} export class SettingsTreeFilter implements ITreeFilter { constructor( @@ -2419,9 +2302,9 @@ class SettingsTreeAccessibilityProvider implements IListAccessibilityProvider { From 45304da73d51e8df7ef4da00347db0ca3e5c263f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 23 May 2022 16:27:17 -0700 Subject: [PATCH 247/942] Finalize NotebookEditor api proposal (#149767) * Finalize NotebookEditor api proposal Fixes #149271 This finalizes most parts of the NotebookEditor api proposal. I haven't removed the proposal entirely as there are still a few parts being left behind: - The deprecated properties/functions - A few contribution points such as `notebook/cell/executePrimary` * remove extra quote --- extensions/ipynb/package.json | 1 - extensions/ipynb/tsconfig.json | 1 - extensions/notebook-renderers/tsconfig.json | 1 - extensions/vscode-api-tests/package.json | 1 - extensions/vscode-notebook-tests/package.json | 1 - .../workbench/api/common/extHost.api.impl.ts | 11 +- .../api/common/extHostNotebookRenderers.ts | 18 +- src/vscode-dts/vscode.d.ts | 168 +++++++++++++++++ .../vscode.proposed.notebookEditor.d.ts | 173 +----------------- 9 files changed, 176 insertions(+), 199 deletions(-) diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index 901f65e56e170..1e251a5a19be2 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -9,7 +9,6 @@ "vscode": "^1.57.0" }, "enabledApiProposals": [ - "notebookEditor", "notebookWorkspaceEdit" ], "activationEvents": [ diff --git a/extensions/ipynb/tsconfig.json b/extensions/ipynb/tsconfig.json index 918db6ea6c258..a800601745861 100644 --- a/extensions/ipynb/tsconfig.json +++ b/extensions/ipynb/tsconfig.json @@ -9,7 +9,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.notebookEditor.d.ts", "../../src/vscode-dts/vscode.proposed.notebookWorkspaceEdit.d.ts" ] } diff --git a/extensions/notebook-renderers/tsconfig.json b/extensions/notebook-renderers/tsconfig.json index 2032bf87b0d2c..23609811f3a19 100644 --- a/extensions/notebook-renderers/tsconfig.json +++ b/extensions/notebook-renderers/tsconfig.json @@ -9,7 +9,6 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.notebookEditor.d.ts", "../../src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts", ] } diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 0f7f928dc81b3..8b2dab07a145e 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -24,7 +24,6 @@ "notebookControllerKind", "notebookDebugOptions", "notebookDeprecated", - "notebookEditor", "notebookEditorDecorationType", "notebookEditorEdit", "notebookLiveShare", diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json index 8bb9eb8d3b354..bd3f3586db031 100644 --- a/extensions/vscode-notebook-tests/package.json +++ b/extensions/vscode-notebook-tests/package.json @@ -15,7 +15,6 @@ "notebookControllerKind", "notebookDebugOptions", "notebookDeprecated", - "notebookEditor", "notebookEditorDecorationType", "notebookLiveShare", "notebookMessaging", diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 313a9aadfd12e..1c1d4cbcaea64 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -759,31 +759,28 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostWebviewViews.registerWebviewViewProvider(extension, viewId, provider, options?.webviewOptions); }, get activeNotebookEditor(): vscode.NotebookEditor | undefined { - checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.activeNotebookEditor; }, onDidChangeActiveNotebookEditor(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.onDidChangeActiveNotebookEditor(listener, thisArgs, disposables); }, get visibleNotebookEditors() { - checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.visibleNotebookEditors; }, get onDidChangeVisibleNotebookEditors() { - checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebook.onDidChangeVisibleNotebookEditors; }, onDidChangeNotebookEditorSelection(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebookEditors.onDidChangeNotebookEditorSelection(listener, thisArgs, disposables); }, onDidChangeNotebookEditorVisibleRanges(listener, thisArgs?, disposables?) { - checkProposedApiEnabled(extension, 'notebookEditor'); return extHostNotebookEditors.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables); }, showNotebookDocument(uriOrDocument, options?) { - checkProposedApiEnabled(extension, 'notebookEditor'); + if (URI.isUri(uriOrDocument)) { + extHostApiDeprecation.report('window.showNotebookDocument(uri)', extension, + `Please use 'window.openNotebookDocument' and 'window.showTextDocument'`); + } return extHostNotebook.showNotebookDocument(uriOrDocument, options); }, registerExternalUriOpener(id: string, opener: vscode.ExternalUriOpener, metadata: vscode.ExternalUriOpenerMetadata) { diff --git a/src/vs/workbench/api/common/extHostNotebookRenderers.ts b/src/vs/workbench/api/common/extHostNotebookRenderers.ts index 1b8925f8757cd..d3a3e0dd18ef0 100644 --- a/src/vs/workbench/api/common/extHostNotebookRenderers.ts +++ b/src/vs/workbench/api/common/extHostNotebookRenderers.ts @@ -8,7 +8,6 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { ExtHostNotebookRenderersShape, IMainContext, MainContext, MainThreadNotebookRenderersShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import { ExtHostNotebookEditor } from 'vs/workbench/api/common/extHostNotebookEditor'; -import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import * as vscode from 'vscode'; @@ -30,29 +29,16 @@ export class ExtHostNotebookRenderers implements ExtHostNotebookRenderersShape { throw new Error(`Extensions may only call createRendererMessaging() for renderers they contribute (got ${rendererId})`); } - // In the stable API, the editor is given as an empty object, and this map - // is used to maintain references. This can be removed after editor finalization. - const notebookEditorVisible = isProposedApiEnabled(manifest, 'notebookEditor'); - const notebookEditorAliases = new WeakMap<{}, vscode.NotebookEditor>(); - const messaging: vscode.NotebookRendererMessaging = { onDidReceiveMessage: (listener, thisArg, disposables) => { - const wrappedListener = notebookEditorVisible ? listener : (evt: { editor: vscode.NotebookEditor; message: any }) => { - const obj = {}; - notebookEditorAliases.set(obj, evt.editor); - listener({ editor: obj as vscode.NotebookEditor, message: evt.message }); - }; - - return this.getOrCreateEmitterFor(rendererId).event(wrappedListener, thisArg, disposables); + return this.getOrCreateEmitterFor(rendererId).event(listener, thisArg, disposables); }, postMessage: (message, editorOrAlias) => { if (ExtHostNotebookEditor.apiEditorsToExtHost.has(message)) { // back compat for swapped args [message, editorOrAlias] = [editorOrAlias, message]; } - - const editor = notebookEditorVisible ? editorOrAlias : notebookEditorAliases.get(editorOrAlias!); - const extHostEditor = editor && ExtHostNotebookEditor.apiEditorsToExtHost.get(editor); + const extHostEditor = editorOrAlias && ExtHostNotebookEditor.apiEditorsToExtHost.get(editorOrAlias); return this.proxy.$postMessage(extHostEditor?.id, rendererId, message); }, }; diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index a34508112e780..3d7b87599f1a8 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -777,6 +777,67 @@ declare module 'vscode' { selection?: Range; } + /** + * Represents an event describing the change in a {@link NotebookEditor.selections notebook editor's selections}. + */ + export interface NotebookEditorSelectionChangeEvent { + /** + * The {@link NotebookEditor notebook editor} for which the selections have changed. + */ + readonly notebookEditor: NotebookEditor; + + /** + * The new value for the {@link NotebookEditor.selections notebook editor's selections}. + */ + readonly selections: readonly NotebookRange[]; + } + + /** + * Represents an event describing the change in a {@link NotebookEditor.visibleRanges notebook editor's visibleRanges}. + */ + export interface NotebookEditorVisibleRangesChangeEvent { + /** + * The {@link NotebookEditor notebook editor} for which the visible ranges have changed. + */ + readonly notebookEditor: NotebookEditor; + + /** + * The new value for the {@link NotebookEditor.visibleRanges notebook editor's visibleRanges}. + */ + readonly visibleRanges: readonly NotebookRange[]; + } + + /** + * Represents options to configure the behavior of showing a {@link NotebookDocument notebook document} in an {@link NotebookEditor notebook editor}. + */ + export interface NotebookDocumentShowOptions { + /** + * An optional view column in which the {@link NotebookEditor notebook editor} should be shown. + * The default is the {@link ViewColumn.Active active}, other values are adjusted to + * be `Min(column, columnCount + 1)`, the {@link ViewColumn.Active active}-column is + * not adjusted. Use {@linkcode ViewColumn.Beside} to open the + * editor to the side of the currently active one. + */ + readonly viewColumn?: ViewColumn; + + /** + * An optional flag that when `true` will stop the {@link NotebookEditor notebook editor} from taking focus. + */ + readonly preserveFocus?: boolean; + + /** + * An optional flag that controls if an {@link NotebookEditor notebook editor}-tab shows as preview. Preview tabs will + * be replaced and reused until set to stay - either explicitly or through editing. The default behaviour depends + * on the `workbench.editor.enablePreview`-setting. + */ + readonly preview?: boolean; + + /** + * An optional selection to apply for the document in the {@link NotebookEditor notebook editor}. + */ + readonly selections?: readonly NotebookRange[]; + } + /** * A reference to one of the workbench colors as defined in https://code.visualstudio.com/docs/getstarted/theme-color-reference. * Using a theme color is preferred over a custom color as it gives theme authors and users the possibility to change the color. @@ -9121,6 +9182,43 @@ declare module 'vscode' { */ export const onDidChangeTextEditorViewColumn: Event; + /** + * The currently visible {@link NotebookEditor notebook editors} or an empty array. + */ + export const visibleNotebookEditors: readonly NotebookEditor[]; + + /** + * An {@link Event} which fires when the {@link window.visibleNotebookEditors visible notebook editors} + * has changed. + */ + export const onDidChangeVisibleNotebookEditors: Event; + + /** + * The currently active {@link NotebookEditor notebook editor} or `undefined`. The active editor is the one + * that currently has focus or, when none has focus, the one that has changed + * input most recently. + */ + export const activeNotebookEditor: NotebookEditor | undefined; + + /** + * An {@link Event} which fires when the {@link window.activeNotebookEditor active notebook editor} + * has changed. *Note* that the event also fires when the active editor changes + * to `undefined`. + */ + export const onDidChangeActiveNotebookEditor: Event; + + /** + * An {@link Event} which fires when the {@link NotebookEditor.selections notebook editor selections} + * have changed. + */ + export const onDidChangeNotebookEditorSelection: Event; + + /** + * An {@link Event} which fires when the {@link NotebookEditor.visibleRanges notebook editor visible ranges} + * have changed. + */ + export const onDidChangeNotebookEditorVisibleRanges: Event; + /** * The currently opened terminals or an empty array. */ @@ -9200,6 +9298,16 @@ declare module 'vscode' { */ export function showTextDocument(uri: Uri, options?: TextDocumentShowOptions): Thenable; + /** + * Show the given {@link NotebookDocument} in a {@link NotebookEditor notebook editor}. + * + * @param document A text document to be shown. + * @param options {@link NotebookDocumentShowOptions Editor options} to configure the behavior of showing the {@link NotebookEditor notebook editor}. + * + * @return A promise that resolves to an {@link NotebookEditor notebook editor}. + */ + export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable; + /** * Create a TextEditorDecorationType that can be used to add decorations to text editors. * @@ -12482,6 +12590,32 @@ declare module 'vscode' { } + /** + * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. + */ + export enum NotebookEditorRevealType { + /** + * The range will be revealed with as little scrolling as possible. + */ + Default = 0, + + /** + * The range will always be revealed in the center of the viewport. + */ + InCenter = 1, + + /** + * If the range is outside the viewport, it will be revealed in the center of the viewport. + * Otherwise, it will be revealed with as little scrolling as possible. + */ + InCenterIfOutsideViewport = 2, + + /** + * The range will always be revealed at the top of the viewport. + */ + AtTop = 3 + } + /** * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. * Additional properties of the NotebookEditor are available in the proposed @@ -12489,6 +12623,40 @@ declare module 'vscode' { */ export interface NotebookEditor { + /** + * The {@link NotebookDocument notebook document} associated with this notebook editor. + */ + readonly notebook: NotebookDocument; + + /** + * The primary selection in this notebook editor. + */ + selection: NotebookRange; + + /** + * All selections in this notebook editor. + * + * The primary selection (or focused range) is `selections[0]`. When the document has no cells, the primary selection is empty `{ start: 0, end: 0 }`; + */ + selections: readonly NotebookRange[]; + + /** + * The current visible ranges in the editor (vertically). + */ + readonly visibleRanges: readonly NotebookRange[]; + + /** + * The column in which this editor shows. + */ + readonly viewColumn?: ViewColumn; + + /** + * Scroll as indicated by `revealType` in order to reveal the given range. + * + * @param range A range. + * @param revealType The scrolling strategy for revealing `range`. + */ + revealRange(range: NotebookRange, revealType?: NotebookEditorRevealType): void; } /** diff --git a/src/vscode-dts/vscode.proposed.notebookEditor.d.ts b/src/vscode-dts/vscode.proposed.notebookEditor.d.ts index fd1d91db119af..af681fc77ee15 100644 --- a/src/vscode-dts/vscode.proposed.notebookEditor.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookEditor.d.ts @@ -7,35 +7,9 @@ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/149271 - /** - * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. - */ - export enum NotebookEditorRevealType { - /** - * The range will be revealed with as little scrolling as possible. - */ - Default = 0, - - /** - * The range will always be revealed in the center of the viewport. - */ - InCenter = 1, + // ❗️ Important: The main NotebookEditor api has been finalized. + // This file only contains deprecated properties/functions from the proposal. - /** - * If the range is outside the viewport, it will be revealed in the center of the viewport. - * Otherwise, it will be revealed with as little scrolling as possible. - */ - InCenterIfOutsideViewport = 2, - - /** - * The range will always be revealed at the top of the viewport. - */ - AtTop = 3 - } - - /** - * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. - */ export interface NotebookEditor { /** * The document associated with this notebook editor. @@ -43,152 +17,9 @@ declare module 'vscode' { * @deprecated Use {@linkcode NotebookEditor.notebook} instead. */ readonly document: NotebookDocument; - - /** - * The {@link NotebookDocument notebook document} associated with this notebook editor. - */ - readonly notebook: NotebookDocument; - - /** - * The primary selection in this notebook editor. - */ - selection: NotebookRange; - - /** - * All selections in this notebook editor. - * - * The primary selection (or focused range) is `selections[0]`. When the document has no cells, the primary selection is empty `{ start: 0, end: 0 }`; - */ - selections: readonly NotebookRange[]; - - /** - * The current visible ranges in the editor (vertically). - */ - readonly visibleRanges: readonly NotebookRange[]; - - /** - * The column in which this editor shows. - */ - readonly viewColumn?: ViewColumn; - - /** - * Scroll as indicated by `revealType` in order to reveal the given range. - * - * @param range A range. - * @param revealType The scrolling strategy for revealing `range`. - */ - revealRange(range: NotebookRange, revealType?: NotebookEditorRevealType): void; - } - - /** - * Represents an event describing the change in a {@link NotebookEditor.selections notebook editor's selections}. - */ - export interface NotebookEditorSelectionChangeEvent { - /** - * The {@link NotebookEditor notebook editor} for which the selections have changed. - */ - readonly notebookEditor: NotebookEditor; - - /** - * The new value for the {@link NotebookEditor.selections notebook editor's selections}. - */ - readonly selections: readonly NotebookRange[]; - } - - /** - * Represents an event describing the change in a {@link NotebookEditor.visibleRanges notebook editor's visibleRanges}. - */ - export interface NotebookEditorVisibleRangesChangeEvent { - /** - * The {@link NotebookEditor notebook editor} for which the visible ranges have changed. - */ - readonly notebookEditor: NotebookEditor; - - /** - * The new value for the {@link NotebookEditor.visibleRanges notebook editor's visibleRanges}. - */ - readonly visibleRanges: readonly NotebookRange[]; - } - - /** - * Represents options to configure the behavior of showing a {@link NotebookDocument notebook document} in an {@link NotebookEditor notebook editor}. - */ - export interface NotebookDocumentShowOptions { - /** - * An optional view column in which the {@link NotebookEditor notebook editor} should be shown. - * The default is the {@link ViewColumn.Active active}, other values are adjusted to - * be `Min(column, columnCount + 1)`, the {@link ViewColumn.Active active}-column is - * not adjusted. Use {@linkcode ViewColumn.Beside} to open the - * editor to the side of the currently active one. - */ - readonly viewColumn?: ViewColumn; - - /** - * An optional flag that when `true` will stop the {@link NotebookEditor notebook editor} from taking focus. - */ - readonly preserveFocus?: boolean; - - /** - * An optional flag that controls if an {@link NotebookEditor notebook editor}-tab shows as preview. Preview tabs will - * be replaced and reused until set to stay - either explicitly or through editing. The default behaviour depends - * on the `workbench.editor.enablePreview`-setting. - */ - readonly preview?: boolean; - - /** - * An optional selection to apply for the document in the {@link NotebookEditor notebook editor}. - */ - readonly selections?: readonly NotebookRange[]; } export namespace window { - /** - * The currently visible {@link NotebookEditor notebook editors} or an empty array. - */ - export const visibleNotebookEditors: readonly NotebookEditor[]; - - /** - * An {@link Event} which fires when the {@link window.visibleNotebookEditors visible notebook editors} - * has changed. - */ - export const onDidChangeVisibleNotebookEditors: Event; - - /** - * The currently active {@link NotebookEditor notebook editor} or `undefined`. The active editor is the one - * that currently has focus or, when none has focus, the one that has changed - * input most recently. - */ - export const activeNotebookEditor: NotebookEditor | undefined; - - /** - * An {@link Event} which fires when the {@link window.activeNotebookEditor active notebook editor} - * has changed. *Note* that the event also fires when the active editor changes - * to `undefined`. - */ - export const onDidChangeActiveNotebookEditor: Event; - - /** - * An {@link Event} which fires when the {@link NotebookEditor.selections notebook editor selections} - * have changed. - */ - export const onDidChangeNotebookEditorSelection: Event; - - /** - * An {@link Event} which fires when the {@link NotebookEditor.visibleRanges notebook editor visible ranges} - * have changed. - */ - export const onDidChangeNotebookEditorVisibleRanges: Event; - - /** - * Show the given {@link NotebookDocument} in a {@link NotebookEditor notebook editor}. - * - * @param document A text document to be shown. - * @param options {@link NotebookDocumentShowOptions Editor options} to configure the behavior of showing the {@link NotebookEditor notebook editor}. - * - * @return A promise that resolves to an {@link NotebookEditor notebook editor}. - */ - export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable; - /** * A short-hand for `openNotebookDocument(uri).then(document => showNotebookDocument(document, options))`. * From 91923bab48edf9511b00b903302c80dcf098f520 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 23 May 2022 16:28:02 -0700 Subject: [PATCH 248/942] Cleanup data transfer types (#149774) This change attempts to clean up our internal data transfer types - Replace `IDataTransfer` (which was just an alias for map) with a `VSDataTransfer` helper class. This lets us add helpers and also restrict what you can do on a datatransfer - Move `DataTransferDTO` types into `protocol` - Move `DataTransferTypeConverter` into `typeConvert` - Don't return internal types to ext host callers For example, previously we leaked `IDataTransfer` out into providers / controllers. Instead we should always return an instance of `vscode.DataTransfer` to extensions --- src/vs/base/common/dataTransfer.ts | 49 +++++++++++++- src/vs/editor/common/languages.ts | 4 +- .../browser/dropIntoEditorContribution.ts | 39 +++-------- .../api/browser/mainThreadLanguageFeatures.ts | 8 +-- .../api/browser/mainThreadTreeViews.ts | 21 +++--- .../workbench/api/common/extHost.protocol.ts | 15 ++++- .../api/common/extHostLanguageFeatures.ts | 7 +- .../workbench/api/common/extHostTreeViews.ts | 35 +++------- .../api/common/extHostTypeConverters.ts | 50 ++++++++++++++ src/vs/workbench/api/common/extHostTypes.ts | 13 ++-- .../api/common/shared/dataTransfer.ts | 66 ------------------- .../api/common/shared/dataTransferCache.ts | 4 +- src/vs/workbench/browser/dnd.ts | 4 +- .../workbench/browser/parts/views/treeView.ts | 19 ++---- src/vs/workbench/common/views.ts | 6 +- .../views/browser/treeViewsService.ts | 4 +- 16 files changed, 176 insertions(+), 168 deletions(-) delete mode 100644 src/vs/workbench/api/common/shared/dataTransfer.ts diff --git a/src/vs/base/common/dataTransfer.ts b/src/vs/base/common/dataTransfer.ts index 748362a176af9..1f56e79f75190 100644 --- a/src/vs/base/common/dataTransfer.ts +++ b/src/vs/base/common/dataTransfer.ts @@ -17,4 +17,51 @@ export interface IDataTransferItem { value: any; } -export type IDataTransfer = Map; +export class VSDataTransfer { + + private readonly _data = new Map(); + + public get size(): number { + return this._data.size; + } + + public has(mimeType: string): boolean { + return this._data.has(mimeType); + } + + public get(mimeType: string): IDataTransferItem | undefined { + return this._data.get(mimeType); + } + + public set(mimeType: string, value: IDataTransferItem): void { + this._data.set(mimeType, value); + } + + public setString(mimeType: string, stringOrPromise: string | Promise) { + this.set(mimeType, { + asString: async () => stringOrPromise, + asFile: () => undefined, + value: typeof stringOrPromise === 'string' ? stringOrPromise : undefined, + }); + } + + public setFile(mimeType: string, fileName: string, uri: URI | undefined, data: () => Promise) { + this.set(mimeType, { + asString: async () => '', + asFile: () => ({ name: fileName, uri, data }), + value: undefined, + }); + } + + public entries(): IterableIterator<[string, IDataTransferItem]> { + return this._data.entries(); + } + + public values(): IterableIterator { + return this._data.values(); + } + + public forEach(f: (value: IDataTransferItem, key: string) => void) { + this._data.forEach(f); + } +} diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index b5c7361ce88ad..1c366d79a5abe 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -6,7 +6,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Codicon, CSSIcon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; -import { IDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -1991,5 +1991,5 @@ export enum ExternalUriOpenerPriority { * @internal */ export interface DocumentOnDropEditProvider { - provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: IDataTransfer, token: CancellationToken): ProviderResult; + provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: VSDataTransfer, token: CancellationToken): ProviderResult; } diff --git a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts index d113f9e513c45..edbc29db56ee5 100644 --- a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts +++ b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts @@ -5,7 +5,7 @@ import { distinct } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { IDataTransfer, IDataTransferItem } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Disposable } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import { relativePath } from 'vs/base/common/resources'; @@ -93,8 +93,8 @@ export class DropIntoEditorController extends Disposable implements IEditorContr } } - public async extractDataTransferData(dragEvent: DragEvent): Promise { - const textEditorDataTransfer: IDataTransfer = new Map(); + public async extractDataTransferData(dragEvent: DragEvent): Promise { + const textEditorDataTransfer = new VSDataTransfer(); if (!dragEvent.dataTransfer) { return textEditorDataTransfer; } @@ -103,27 +103,13 @@ export class DropIntoEditorController extends Disposable implements IEditorContr const type = item.type; if (item.kind === 'string') { const asStringValue = new Promise(resolve => item.getAsString(resolve)); - textEditorDataTransfer.set(type, { - asString: () => asStringValue, - asFile: () => undefined, - value: undefined - }); + textEditorDataTransfer.setString(type, asStringValue); } else if (item.kind === 'file') { const file = item.getAsFile(); if (file) { - textEditorDataTransfer.set(type, { - asString: () => Promise.resolve(''), - asFile: () => { - const uri = (file as FileAdditionalNativeProperties).path ? URI.parse((file as FileAdditionalNativeProperties).path!) : undefined; - return { - name: file.name, - uri: uri, - data: async () => { - return new Uint8Array(await file.arrayBuffer()); - }, - }; - }, - value: undefined + const uri = (file as FileAdditionalNativeProperties).path ? URI.parse((file as FileAdditionalNativeProperties).path!) : undefined; + textEditorDataTransfer.setFile(type, file.name, uri, async () => { + return new Uint8Array(await file.arrayBuffer()); }); } } @@ -135,14 +121,9 @@ export class DropIntoEditorController extends Disposable implements IEditorContr .map(input => input.resource!.toString()); if (editorData.length) { - const added: IDataTransfer = new Map(); - + const added = new VSDataTransfer(); const str = distinct(editorData).join('\n'); - added.set(Mimes.uriList.toLowerCase(), { - asFile: () => undefined, - asString: async () => str, - value: str, - }); + added.setString(Mimes.uriList.toLowerCase(), str); return added; } } @@ -157,7 +138,7 @@ class DefaultOnDropProvider implements DocumentOnDropEditProvider { @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, ) { } - async provideDocumentOnDropEdits(model: ITextModel, position: IPosition, dataTransfer: IDataTransfer, _token: CancellationToken): Promise { + async provideDocumentOnDropEdits(model: ITextModel, position: IPosition, dataTransfer: VSDataTransfer, _token: CancellationToken): Promise { const range = new Range(position.lineNumber, position.column, position.lineNumber, position.column); const urlListEntry = dataTransfer.get('text/uri-list'); diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 16fada27b5b4a..ff3bd15f82a46 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -24,13 +24,13 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { DataTransferCache } from 'vs/workbench/api/common/shared/dataTransferCache'; -import { DataTransferConverter } from 'vs/workbench/api/common/shared/dataTransfer'; import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import * as search from 'vs/workbench/contrib/search/common/search'; import * as typeh from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { ExtHostContext, ExtHostLanguageFeaturesShape, ICallHierarchyItemDto, ICodeActionDto, ICodeActionProviderMetadataDto, IdentifiableInlineCompletion, IdentifiableInlineCompletions, IDocumentFilterDto, IIndentationRuleDto, IInlayHintDto, ILanguageConfigurationDto, ILanguageWordDefinitionDto, ILinkDto, ILocationDto, ILocationLinkDto, IOnEnterRuleDto, IRegExpDto, ISignatureHelpProviderMetadataDto, ISuggestDataDto, ISuggestDataDtoField, ISuggestResultDtoField, ITypeHierarchyItemDto, IWorkspaceSymbolDto, MainContext, MainThreadLanguageFeaturesShape, reviveWorkspaceEditDto } from '../common/extHost.protocol'; -import { IDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; +import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) export class MainThreadLanguageFeatures extends Disposable implements MainThreadLanguageFeaturesShape { @@ -892,10 +892,10 @@ class MainThreadDocumentOnDropEditProvider implements languages.DocumentOnDropEd private readonly _proxy: ExtHostLanguageFeaturesShape, ) { } - async provideDocumentOnDropEdits(model: ITextModel, position: IPosition, dataTransfer: IDataTransfer, token: CancellationToken): Promise { + async provideDocumentOnDropEdits(model: ITextModel, position: IPosition, dataTransfer: VSDataTransfer, token: CancellationToken): Promise { const request = this.dataTransfers.add(dataTransfer); try { - const dataTransferDto = await DataTransferConverter.toDataTransferDTO(dataTransfer); + const dataTransferDto = await typeConvert.DataTransfer.toDataTransferDTO(dataTransfer); return await this._proxy.$provideDocumentOnDropEdits(this.handle, request.id, model.uri, position, dataTransferDto, token); } finally { request.dispose(); diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 6e96ff8161fc6..f0923d957010e 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -13,11 +13,11 @@ import { isUndefinedOrNull, isNumber } from 'vs/base/common/types'; import { Registry } from 'vs/platform/registry/common/platform'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; -import { DataTransferConverter } from 'vs/workbench/api/common/shared/dataTransfer'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { VSBuffer } from 'vs/base/common/buffer'; import { DataTransferCache } from 'vs/workbench/api/common/shared/dataTransferCache'; +import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; @extHostNamedCustomer(MainContext.MainThreadTreeViews) export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape { @@ -203,25 +203,30 @@ class TreeViewDragAndDropController implements ITreeViewDragAndDropController { readonly hasWillDrop: boolean, private readonly _proxy: ExtHostTreeViewsShape) { } - async handleDrop(dataTransfer: IDataTransfer, targetTreeItem: ITreeItem | undefined, token: CancellationToken, + async handleDrop(dataTransfer: VSDataTransfer, targetTreeItem: ITreeItem | undefined, token: CancellationToken, operationUuid?: string, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise { const request = this.dataTransfersCache.add(dataTransfer); try { - return await this._proxy.$handleDrop(this.treeViewId, request.id, await DataTransferConverter.toDataTransferDTO(dataTransfer), targetTreeItem?.handle, token, operationUuid, sourceTreeId, sourceTreeItemHandles); + return await this._proxy.$handleDrop(this.treeViewId, request.id, await typeConvert.DataTransfer.toDataTransferDTO(dataTransfer), targetTreeItem?.handle, token, operationUuid, sourceTreeId, sourceTreeItemHandles); } finally { request.dispose(); } } - async handleDrag(sourceTreeItemHandles: string[], operationUuid: string, token: CancellationToken): Promise { + async handleDrag(sourceTreeItemHandles: string[], operationUuid: string, token: CancellationToken): Promise { if (!this.hasWillDrop) { return; } - const additionalTransferItems = await this._proxy.$handleDrag(this.treeViewId, sourceTreeItemHandles, operationUuid, token); - if (!additionalTransferItems) { + const additionalDataTransferDTO = await this._proxy.$handleDrag(this.treeViewId, sourceTreeItemHandles, operationUuid, token); + if (!additionalDataTransferDTO) { return; } - return DataTransferConverter.toDataTransfer(additionalTransferItems, () => { throw new Error('not supported'); }); + + const additionalDataTransfer = new VSDataTransfer(); + additionalDataTransferDTO.items.forEach(([type, item]) => { + additionalDataTransfer.setString(type, item.asString); + }); + return additionalDataTransfer; } public resolveDropFileData(requestId: number, dataItemIndex: number): Promise { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 6d936351c3556..9ae91a955be90 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -44,7 +44,6 @@ import { ThemeColor, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { ProvidedPortAttributes, TunnelCreationOptions, TunnelOptions, TunnelPrivacyId, TunnelProviderFeatures } from 'vs/platform/tunnel/common/tunnel'; import { WorkspaceTrustRequestOptions } from 'vs/platform/workspace/common/workspaceTrust'; import * as tasks from 'vs/workbench/api/common/shared/tasks'; -import { DataTransferDTO } from 'vs/workbench/api/common/shared/dataTransfer'; import { SaveReason } from 'vs/workbench/common/editor'; import { IRevealOptions, ITreeItem, IViewBadge } from 'vs/workbench/common/views'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; @@ -1369,6 +1368,20 @@ export interface ExtHostDocumentsAndEditorsShape { $acceptDocumentsAndEditorsDelta(delta: IDocumentsAndEditorsDelta): void; } +export interface IDataTransferFileDTO { + readonly name: string; + readonly uri?: UriComponents; +} + +export interface DataTransferItemDTO { + readonly asString: string; + readonly fileData: IDataTransferFileDTO | undefined; +} + +export interface DataTransferDTO { + readonly items: Array<[/* type */string, DataTransferItemDTO]>; +} + export interface ExtHostTreeViewsShape { $getChildren(treeViewId: string, treeItemHandle?: string): Promise; $handleDrop(destinationViewId: string, requestId: number, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 3b6b789641a62..53f5ad0808164 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -34,7 +34,6 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import { isCancellationError } from 'vs/base/common/errors'; import { raceCancellationError } from 'vs/base/common/async'; import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; -import { DataTransferConverter, DataTransferDTO } from 'vs/workbench/api/common/shared/dataTransfer'; import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier'; // --- adapter @@ -1754,10 +1753,10 @@ class DocumentOnDropEditAdapter { private readonly _handle: number, ) { } - async provideDocumentOnDropEdits(requestId: number, uri: URI, position: IPosition, dataTransferDto: DataTransferDTO, token: CancellationToken): Promise | undefined> { + async provideDocumentOnDropEdits(requestId: number, uri: URI, position: IPosition, dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise | undefined> { const doc = this._documents.getDocument(uri); const pos = typeConvert.Position.to(position); - const dataTransfer = DataTransferConverter.toDataTransfer(dataTransferDto, async (index) => { + const dataTransfer = typeConvert.DataTransfer.toDataTransfer(dataTransferDto, async (index) => { return (await this._proxy.$resolveDocumentOnDropFileData(this._handle, requestId, index)).buffer; }); @@ -2409,7 +2408,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._createDisposable(handle); } - $provideDocumentOnDropEdits(handle: number, requestId: number, resource: UriComponents, position: IPosition, dataTransferDto: DataTransferDTO, token: CancellationToken): Promise | undefined> { + $provideDocumentOnDropEdits(handle: number, requestId: number, resource: UriComponents, position: IPosition, dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise | undefined> { return this._withAdapter(handle, DocumentOnDropEditAdapter, adapter => Promise.resolve(adapter.provideDocumentOnDropEdits(requestId, URI.revive(resource), position, dataTransferDto, token)), undefined, undefined); } diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index b984ae6569e8d..fdbb535e42292 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -5,11 +5,12 @@ import { localize } from 'vs/nls'; import type * as vscode from 'vscode'; +import * as types from './extHostTypes'; import { basename } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol'; +import { DataTransferDTO, ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol'; import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions } from 'vs/workbench/common/views'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { asPromise } from 'vs/base/common/async'; @@ -18,14 +19,12 @@ import { isUndefinedOrNull, isString } from 'vs/base/common/types'; import { equals, coalesce } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { MarkdownString, ViewBadge } from 'vs/workbench/api/common/extHostTypeConverters'; +import { MarkdownString, ViewBadge, DataTransfer } from 'vs/workbench/api/common/extHostTypeConverters'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Command } from 'vs/editor/common/languages'; -import { DataTransferConverter, DataTransferDTO } from 'vs/workbench/api/common/shared/dataTransfer'; import { ITreeViewsService, TreeviewsService } from 'vs/workbench/services/views/common/treeViewsService'; import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; -import { IDataTransfer } from 'vs/base/common/dataTransfer'; type TreeItemHandle = string; @@ -151,7 +150,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { return Promise.reject(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', destinationViewId))); } - const treeDataTransfer = DataTransferConverter.toDataTransfer(treeDataTransferDTO, async dataItemIndex => { + const treeDataTransfer = DataTransfer.toDataTransfer(treeDataTransferDTO, async dataItemIndex => { return (await this._proxy.$resolveDropFileData(destinationViewId, requestId, dataItemIndex)).buffer; }); if ((sourceViewId === destinationViewId) && sourceTreeItemHandles) { @@ -160,27 +159,13 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { return treeView.onDrop(treeDataTransfer, targetItemHandle, token); } - private async addAdditionalTransferItems(treeDataTransfer: IDataTransfer, treeView: ExtHostTreeView, - sourceTreeItemHandles: string[], token: CancellationToken, operationUuid?: string): Promise { + private async addAdditionalTransferItems(treeDataTransfer: vscode.DataTransfer, treeView: ExtHostTreeView, + sourceTreeItemHandles: string[], token: CancellationToken, operationUuid?: string): Promise { const existingTransferOperation = this.treeDragAndDropService.removeDragOperationTransfer(operationUuid); if (existingTransferOperation) { (await existingTransferOperation)?.forEach((value, key) => { if (value) { - const file = value.asFile(); - treeDataTransfer.set(key, { - value: value.value, - asString: value.asString, - asFile() { - if (!file) { - return undefined; - } - return { - name: file.name, - uri: file.uri, - data: async () => await file.data() - }; - }, - }); + treeDataTransfer.set(key, value); } }); } else if (operationUuid && treeView.handleDrag) { @@ -197,12 +182,12 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { return Promise.reject(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', sourceViewId))); } - const treeDataTransfer = await this.addAdditionalTransferItems(new Map(), treeView, sourceTreeItemHandles, token, operationUuid); + const treeDataTransfer = await this.addAdditionalTransferItems(new types.DataTransfer(), treeView, sourceTreeItemHandles, token, operationUuid); if (!treeDataTransfer) { return; } - return DataTransferConverter.toDataTransferDTO(treeDataTransfer); + return DataTransfer.toDataTransferDTO(treeDataTransfer); } async $hasResolve(treeViewId: string): Promise { @@ -472,7 +457,7 @@ class ExtHostTreeView extends Disposable { } } - async handleDrag(sourceTreeItemHandles: TreeItemHandle[], treeDataTransfer: IDataTransfer, token: CancellationToken): Promise { + async handleDrag(sourceTreeItemHandles: TreeItemHandle[], treeDataTransfer: vscode.DataTransfer, token: CancellationToken): Promise { const extensionTreeItems: T[] = []; for (const sourceHandle of sourceTreeItemHandles) { const extensionItem = this.getExtensionElement(sourceHandle); diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 979eebec86ccb..94226d271f2a6 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -38,6 +38,7 @@ import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGro import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import type * as vscode from 'vscode'; import * as types from './extHostTypes'; +import { once } from 'vs/base/common/functional'; export namespace Command { @@ -1956,3 +1957,52 @@ export namespace ViewBadge { }; } } + +export namespace DataTransferItem { + export function toDataTransferItem(item: extHostProtocol.DataTransferItemDTO, resolveFileData: () => Promise): types.DataTransferItem { + const file = item.fileData; + if (file) { + return new class extends types.DataTransferItem { + override asFile(): vscode.DataTransferFile { + return { + name: file.name, + uri: URI.revive(file.uri), + data: once(() => resolveFileData()), + }; + } + }(''); + } else { + return new types.DataTransferItem(item.asString); + } + } +} + +export namespace DataTransfer { + export function toDataTransfer(value: extHostProtocol.DataTransferDTO, resolveFileData: (dataItemIndex: number) => Promise): types.DataTransfer { + const newDataTransfer = new types.DataTransfer(); + value.items.forEach(([type, item], index) => { + newDataTransfer.set(type, DataTransferItem.toDataTransferItem(item, () => resolveFileData(index))); + }); + return newDataTransfer; + } + + export async function toDataTransferDTO(value: vscode.DataTransfer): Promise { + const newDTO: extHostProtocol.DataTransferDTO = { items: [] }; + + const promises: Promise[] = []; + value.forEach((value, key) => { + promises.push((async () => { + const stringValue = await value.asString(); + const fileValue = value.asFile(); + newDTO.items.push([key, { + asString: stringValue, + fileData: fileValue ? { name: fileValue.name, uri: fileValue.uri } : undefined, + }]); + })()); + }); + + await Promise.all(promises); + + return newDTO; + } +} diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index dc08ede3d9868..76ae9232db9ae 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2442,7 +2442,7 @@ export class DataTransferItem { return typeof this.value === 'string' ? this.value : JSON.stringify(this.value); } - asFile(): undefined { + asFile(): undefined | vscode.DataTransferFile { return undefined; } @@ -2451,15 +2451,18 @@ export class DataTransferItem { @es5ClassCompat export class DataTransfer { - private readonly _items: Map = new Map(); + #items = new Map(); + get(mimeType: string): DataTransferItem | undefined { - return this._items.get(mimeType); + return this.#items.get(mimeType); } + set(mimeType: string, value: DataTransferItem): void { - this._items.set(mimeType, value); + this.#items.set(mimeType, value); } + forEach(callbackfn: (value: DataTransferItem, key: string) => void): void { - this._items.forEach(callbackfn); + this.#items.forEach(callbackfn); } } diff --git a/src/vs/workbench/api/common/shared/dataTransfer.ts b/src/vs/workbench/api/common/shared/dataTransfer.ts deleted file mode 100644 index a16dfe65f963a..0000000000000 --- a/src/vs/workbench/api/common/shared/dataTransfer.ts +++ /dev/null @@ -1,66 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { once } from 'vs/base/common/functional'; -import { URI, UriComponents } from 'vs/base/common/uri'; -import { IDataTransfer, IDataTransferItem } from 'vs/base/common/dataTransfer'; - -export interface IDataTransferFileDTO { - readonly name: string; - readonly uri?: UriComponents; -} - -interface DataTransferItemDTO { - readonly asString: string; - readonly fileData: IDataTransferFileDTO | undefined; -} - -export interface DataTransferDTO { - readonly types: string[]; - readonly items: DataTransferItemDTO[]; -} - -export namespace DataTransferConverter { - export function toDataTransfer(value: DataTransferDTO, resolveFileData: (dataItemIndex: number) => Promise): IDataTransfer { - const newDataTransfer: IDataTransfer = new Map(); - value.types.forEach((type, index) => { - newDataTransfer.set(type, { - asString: async () => value.items[index].asString, - asFile: () => { - const file = value.items[index].fileData; - if (!file) { - return undefined; - } - return { - name: file.name, - uri: URI.revive(file.uri), - data: once(() => resolveFileData(index)), - }; - }, - value: undefined - }); - }); - return newDataTransfer; - } - - export async function toDataTransferDTO(value: IDataTransfer): Promise { - const newDTO: DataTransferDTO = { - types: [], - items: [] - }; - - const entries = Array.from(value.entries()); - for (const entry of entries) { - newDTO.types.push(entry[0]); - const stringValue = await entry[1].asString(); - const fileValue = entry[1].asFile(); - newDTO.items.push({ - asString: stringValue, - fileData: fileValue ? { name: fileValue.name, uri: fileValue.uri } : undefined, - }); - } - return newDTO; - } -} diff --git a/src/vs/workbench/api/common/shared/dataTransferCache.ts b/src/vs/workbench/api/common/shared/dataTransferCache.ts index 02184d68d18b6..6cc8a36b6e622 100644 --- a/src/vs/workbench/api/common/shared/dataTransferCache.ts +++ b/src/vs/workbench/api/common/shared/dataTransferCache.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { VSBuffer } from 'vs/base/common/buffer'; -import { IDataTransfer, IDataTransferItem } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer, IDataTransferItem } from 'vs/base/common/dataTransfer'; export class DataTransferCache { private requestIdPool = 0; private readonly dataTransfers = new Map>(); - public add(dataTransfer: IDataTransfer): { id: number; dispose: () => void } { + public add(dataTransfer: VSDataTransfer): { id: number; dispose: () => void } { const requestId = this.requestIdPool++; this.dataTransfers.set(requestId, [...dataTransfer.values()]); return { diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 63cfe74d6deec..6a79fc3c3370c 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -10,7 +10,7 @@ import { IListDragAndDrop } from 'vs/base/browser/ui/list/list'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree'; import { coalesce } from 'vs/base/common/arrays'; -import { IDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { stringify } from 'vs/base/common/marshalling'; @@ -52,7 +52,7 @@ export class DraggedTreeItemsIdentifier { constructor(readonly identifier: string) { } } -export async function extractTreeDropData(dataTransfer: IDataTransfer): Promise> { +export async function extractTreeDropData(dataTransfer: VSDataTransfer): Promise> { const editors: IDraggedResourceEditorInput[] = []; const resourcesKey = Mimes.uriList.toLowerCase(); diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 9d2c5d0dab008..50da8ebb64764 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -31,7 +31,7 @@ import { isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import 'vs/css!./media/views'; -import { IDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Command } from 'vs/editor/common/languages'; import { localize } from 'vs/nls'; import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -1361,7 +1361,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { this.dndController = controller; } - private handleDragAndLog(dndController: ITreeViewDragAndDropController, itemHandles: string[], uuid: string, dragCancellationToken: CancellationToken): Promise { + private handleDragAndLog(dndController: ITreeViewDragAndDropController, itemHandles: string[], uuid: string, dragCancellationToken: CancellationToken): Promise { return dndController.handleDrag(itemHandles, uuid, dragCancellationToken).then(additionalDataTransfer => { if (additionalDataTransfer) { const unlistedTypes: string[] = []; @@ -1501,7 +1501,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { if (!originalEvent.dataTransfer || !dndController) { return; } - const treeDataTransfer: IDataTransfer = new Map(); + const treeDataTransfer = new VSDataTransfer(); const uris: URI[] = []; let itemsCount = Array.from(originalEvent.dataTransfer.items).reduce((previous, current) => { if ((current.kind === 'string') || (current.kind === 'file')) { @@ -1521,11 +1521,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { if (itemsCount === 0) { // Check if there are uris to add and add them if (uris.length) { - treeDataTransfer.set(Mimes.uriList, { - asString: () => Promise.resolve(uris.map(uri => uri.toString()).join('\n')), - asFile: () => undefined, - value: undefined - }); + treeDataTransfer.setString(Mimes.uriList, uris.map(uri => uri.toString()).join('\n')); } resolve(); } @@ -1547,11 +1543,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { } if (dataValue) { const converted = this.convertKnownMimes(type, kind, dataValue); - treeDataTransfer.set(converted.type, { - asString: () => Promise.resolve(converted.value!), - asFile: () => undefined, - value: undefined - }); + treeDataTransfer.setString(converted.type, converted.value + ''); } decrementStringCount(); }); @@ -1580,7 +1572,6 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { } return dndController.handleDrop(treeDataTransfer, targetNode, new CancellationTokenSource().token, willDropUuid, treeSourceInfo?.id, treeSourceInfo?.itemHandles); }); - } onDragEnd(originalEvent: DragEvent): void { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 5c9c7d93a9dd0..ac9b39c0c21e1 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -27,7 +27,7 @@ import { mixin } from 'vs/base/common/objects'; import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; export const defaultViewIcon = registerIcon('default-view-icon', Codicon.window, localize('defaultViewIcon', 'Default view icon.')); @@ -831,8 +831,8 @@ export interface ITreeViewDataProvider { export interface ITreeViewDragAndDropController { readonly dropMimeTypes: string[]; readonly dragMimeTypes: string[]; - handleDrag(sourceTreeItemHandles: string[], operationUuid: string, token: CancellationToken): Promise; - handleDrop(elements: IDataTransfer, target: ITreeItem | undefined, token: CancellationToken, operationUuid?: string, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise; + handleDrag(sourceTreeItemHandles: string[], operationUuid: string, token: CancellationToken): Promise; + handleDrop(elements: VSDataTransfer, target: ITreeItem | undefined, token: CancellationToken, operationUuid?: string, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise; } export interface IEditableData { diff --git a/src/vs/workbench/services/views/browser/treeViewsService.ts b/src/vs/workbench/services/views/browser/treeViewsService.ts index 93d426deed5b1..052cc97c7e0f7 100644 --- a/src/vs/workbench/services/views/browser/treeViewsService.ts +++ b/src/vs/workbench/services/views/browser/treeViewsService.ts @@ -5,10 +5,10 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { ITreeItem } from 'vs/workbench/common/views'; import { ITreeViewsService as ITreeViewsServiceCommon, TreeviewsService } from 'vs/workbench/services/views/common/treeViewsService'; -export interface ITreeViewsService extends ITreeViewsServiceCommon { } +export interface ITreeViewsService extends ITreeViewsServiceCommon { } export const ITreeViewsService = createDecorator('treeViewsService'); registerSingleton(ITreeViewsService, TreeviewsService); From e2816721e14e8e69affdd0334690e66bd7266e64 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Mon, 23 May 2022 16:50:27 -0700 Subject: [PATCH 249/942] Fix scrolling to happen on execution too --- .../browser/interactive.contribution.ts | 8 +- .../interactive/browser/interactiveEditor.ts | 132 +++--------------- .../contrib/notebook/common/notebookCommon.ts | 3 +- 3 files changed, 26 insertions(+), 117 deletions(-) diff --git a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts index ddd9a857eaae7..b276b2a477186 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts @@ -49,7 +49,7 @@ import { IInteractiveHistoryService, InteractiveHistoryService } from 'vs/workbe import { NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; -import { CellEditType, CellKind, ICellOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, ICellOutput, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookContentProvider, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { columnToEditorGroup } from 'vs/workbench/services/editor/common/editorGroupColumn'; @@ -715,14 +715,14 @@ registerThemingParticipant((theme) => { }); Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ - id: 'interactive', + id: 'notebook', order: 100, type: 'object', 'properties': { - 'interactive.alwaysScrollOnNewCell': { + [NotebookSetting.interactiveWindowAlwaysScrollOnNewCell]: { type: 'boolean', default: true, - markdownDescription: localize('interactive.alwaysScrollOnNewCell', "Automatically scroll the interactive window to show the output of the last statement executed. If this value is false, the window will only scroll if the last cell was already the one scrolled to.") + markdownDescription: localize('interactiveWindow.alwaysScrollOnNewCell', "Automatically scroll the interactive window to show the output of the last statement executed. If this value is false, the window will only scroll if the last cell was already the one scrolled to.") }, } }); diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts index 5093e65b993ad..c6e561c2c4d08 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts @@ -22,7 +22,7 @@ import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { EditorPaneSelectionChangeReason, IEditorMemento, IEditorOpenContext, IEditorPaneSelectionChangeEvent } from 'vs/workbench/common/editor'; import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; import { InteractiveEditorInput } from 'vs/workbench/contrib/interactive/browser/interactiveEditorInput'; -import { CodeCellLayoutChangeEvent, IActiveNotebookEditorDelegate, ICellViewModel, INotebookEditorViewState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { ICellViewModel, INotebookEditorViewState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { IBorrowValue, INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService'; import { cellEditorBackground, NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; @@ -35,14 +35,13 @@ import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INTERACTIVE_INPUT_CURSOR_BOUNDARY } from 'vs/workbench/contrib/interactive/browser/interactiveCommon'; import { ComplexNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel'; -import { NotebookCellExecutionState, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { createActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IAction } from 'vs/base/common/actions'; -import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer'; import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; @@ -62,11 +61,6 @@ import { ICursorPositionChangedEvent } from 'vs/editor/common/cursorEvents'; const DECORATION_KEY = 'interactiveInputDecoration'; const INTERACTIVE_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'InteractiveEditorViewState'; -const enum ScrollingState { - Initial = 0, - StickyToBottom = 1 -} - const INPUT_CELL_VERTICAL_PADDING = 8; const INPUT_CELL_HORIZONTAL_PADDING_RIGHT = 10; const INPUT_EDITOR_PADDING = 8; @@ -154,6 +148,12 @@ export class InteractiveEditor extends EditorPane { codeEditorService.registerDecorationType('interactive-decoration', DECORATION_KEY, {}); this._register(this.#keybindingService.onDidUpdateKeybindings(this.#updateInputDecoration, this)); + this._register(this.#notebookExecutionStateService.onDidChangeCellExecution((e) => { + const cell = this.#notebookWidget.value?.getCellByHandle(e.cellHandle); + if (cell && e.changed?.state) { + this.#scrollIfNecessary(cell); + } + })); } get #inputCellContainerHeight() { @@ -403,14 +403,7 @@ export class InteractiveEditor extends EditorPane { isReadOnly: true }); this.#widgetDisposableStore.add(this.#notebookWidget.value!.onDidResizeOutput((cvm) => { - // Ignore resizes on anything but the last cell. - const index = this.#notebookWidget.value!.getCellIndex(cvm); - if (index === this.#notebookWidget.value!.getLength() - 1) { - // If we're already at the bottom or auto scroll is enabled, scroll to the bottom - if (this.configurationService.getValue('interactive.alwaysScrollOnNewCell') || this.#state === ScrollingState.StickyToBottom) { - this.#notebookWidget.value!.scrollToBottom(); - } - } + this.#scrollIfNecessary(cvm); })); this.#widgetDisposableStore.add(this.#notebookWidget.value!.onDidFocusWidget(() => this.#onDidFocusWidget.fire())); this.#widgetDisposableStore.add(model.notebook.onDidChangeContent(() => { @@ -468,10 +461,6 @@ export class InteractiveEditor extends EditorPane { } })); - if (this.#notebookWidget.value?.hasModel()) { - this.#registerExecutionScrollListener(this.#notebookWidget.value); - } - const cursorAtBoundaryContext = INTERACTIVE_INPUT_CURSOR_BOUNDARY.bindTo(this.#contextKeyService); if (input.resource && input.historyService.has(input.resource)) { cursorAtBoundaryContext.set('top'); @@ -521,107 +510,26 @@ export class InteractiveEditor extends EditorPane { } } - #lastCell: ICellViewModel | undefined = undefined; - #lastCellDisposable = new DisposableStore(); - #state: ScrollingState = ScrollingState.Initial; - - #cellAtBottom(widget: IActiveNotebookEditorDelegate, cell: ICellViewModel): boolean { - const visibleRanges = widget.visibleRanges; - const cellIndex = widget.getCellIndex(cell); - if (cellIndex === Math.max(...visibleRanges.map(range => range.end))) { + #cellAtBottom(cell: ICellViewModel): boolean { + const visibleRanges = this.#notebookWidget.value?.visibleRanges || []; + const cellIndex = this.#notebookWidget.value?.getCellIndex(cell); + if (cellIndex === Math.max(...visibleRanges.map(range => range.end - 1))) { return true; } return false; } - /** - * - Init state: 0 - * - Will cell insertion: check if the last cell is at the bottom, false, stay 0 - * if true, state 1 (ready for auto reveal) - * - receive a scroll event (scroll even already happened). If the last cell is at bottom, false, 0, true, state 1 - * - height change of the last cell, if state 0, do nothing, if state 1, scroll the last cell fully into view - */ - #registerExecutionScrollListener(widget: NotebookEditorWidget & IActiveNotebookEditorDelegate) { - this.#widgetDisposableStore.add(widget.textModel.onWillAddRemoveCells(e => { - const lastViewCell = widget.cellAt(widget.getLength() - 1); - - // check if the last cell is at the bottom - if (lastViewCell && this.#cellAtBottom(widget, lastViewCell)) { - this.#state = ScrollingState.StickyToBottom; - } else { - this.#state = ScrollingState.Initial; + #scrollIfNecessary(cvm: ICellViewModel) { + const index = this.#notebookWidget.value!.getCellIndex(cvm); + if (index === this.#notebookWidget.value!.getLength() - 1) { + // If we're already at the bottom or auto scroll is enabled, scroll to the bottom + if (this.configurationService.getValue(NotebookSetting.interactiveWindowAlwaysScrollOnNewCell) || this.#cellAtBottom(cvm)) { + this.#notebookWidget.value!.scrollToBottom(); } - })); - - this.#widgetDisposableStore.add(widget.onDidScroll(() => { - const lastViewCell = widget.cellAt(widget.getLength() - 1); - - // check if the last cell is at the bottom - if (lastViewCell && this.#cellAtBottom(widget, lastViewCell)) { - this.#state = ScrollingState.StickyToBottom; - } else { - this.#state = ScrollingState.Initial; - } - })); - - this.#widgetDisposableStore.add(widget.textModel.onDidChangeContent(e => { - for (let i = 0; i < e.rawEvents.length; i++) { - const event = e.rawEvents[i]; - - if (event.kind === NotebookCellsChangeType.ModelChange && this.#notebookWidget.value?.hasModel()) { - const lastViewCell = this.#notebookWidget.value.cellAt(this.#notebookWidget.value.getLength() - 1); - if (lastViewCell !== this.#lastCell) { - this.#lastCellDisposable.clear(); - this.#lastCell = lastViewCell; - this.#registerListenerForCell(); - } - } - } - })); - } - - #registerListenerForCell() { - if (!this.#lastCell) { - return; } - - this.#lastCellDisposable.add(this.#lastCell.onDidChangeLayout((e) => { - if (e.totalHeight === undefined) { - // not cell height change - return; - } - - if (!this.#notebookWidget.value) { - return; - } - - if (this.#lastCell instanceof CodeCellViewModel && (e as CodeCellLayoutChangeEvent).outputHeight === undefined && !this.#notebookWidget.value.isScrolledToBottom()) { - return; - } - - if (this.#state !== ScrollingState.StickyToBottom) { - return; - } - - if (this.#lastCell) { - const runState = this.#notebookExecutionStateService.getCellExecution(this.#lastCell.uri)?.state; - if (runState === NotebookCellExecutionState.Executing) { - return; - } - - } - - // scroll to bottom - // postpone to next tick as the list view might not process the output height change yet - // e.g., when we register this listener later than the list view - this.#lastCellDisposable.add(DOM.scheduleAtNextAnimationFrame(() => { - if (this.#state === ScrollingState.StickyToBottom) { - this.#notebookWidget.value!.scrollToBottom(); - } - })); - })); } + #syncWithKernel() { const notebook = this.#notebookWidget.value?.textModel; const textModel = this.#codeEditorWidget.getModel(); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 40b6b6a207466..e678891f59a48 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -915,7 +915,8 @@ export const NotebookSetting = { interactiveWindowCollapseCodeCells: 'interactiveWindow.collapseCellInputCode', outputLineHeight: 'notebook.outputLineHeight', outputFontSize: 'notebook.outputFontSize', - outputFontFamily: 'notebook.outputFontFamily' + outputFontFamily: 'notebook.outputFontFamily', + interactiveWindowAlwaysScrollOnNewCell: 'interactiveWindow.alwaysScrollOnNewCell' } as const; export const enum CellStatusbarAlignment { From 6a9108155f39539c6bd0a2f74421a2d541bdec19 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 23 May 2022 16:57:06 -0700 Subject: [PATCH 250/942] Finalize some interactive contribution points (#150234) Follow up on #149271 Finalizing these two contributions based on https://github.com/microsoft/vscode/pull/149767#issuecomment-1135164600 We're leaving the `notebook/cell/executePrimary` point for now as we don't plan to finalize it --- src/vs/workbench/services/actions/common/menusExtensionPoint.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index 7e06c120f9562..216840ff6a15e 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -191,13 +191,11 @@ const apiMenus: IAPIMenu[] = [ key: 'interactive/toolbar', id: MenuId.InteractiveToolbar, description: localize('interactive.toolbar', "The contributed interactive toolbar menu"), - proposed: 'notebookEditor' }, { key: 'interactive/cell/title', id: MenuId.InteractiveCellTitle, description: localize('interactive.cell.title', "The contributed interactive cell title menu"), - proposed: 'notebookEditor' }, { key: 'testing/item/context', From 1c7991f1e8d3794a0688580bdeee469f88571917 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 23 May 2022 17:07:15 -0700 Subject: [PATCH 251/942] Markdown ignoreLink should apply to paths in header links (#150223) This change lets you ignore all links to a given file, even if those links include a header. For example: `[text](/path/to/file#some-header)` can be ignored using `/path/to/file` --- .../src/languageFeatures/diagnostics.ts | 2 +- .../src/test/diagnostic.test.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index 89e6b29f24039..10547f2000e51 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -494,7 +494,7 @@ export class DiagnosticComputer { if (fragmentLinks.length) { const toc = await TableOfContents.create(this.engine, hrefDoc); for (const link of fragmentLinks) { - if (!toc.lookup(link.fragment) && !this.isIgnoredLink(options, link.source.text)) { + if (!toc.lookup(link.fragment) && !this.isIgnoredLink(options, link.source.pathText) && !this.isIgnoredLink(options, link.source.text)) { const msg = localize('invalidLinkToHeaderInOtherFile', 'Header does not exist in file: {0}', link.fragment); diagnostics.push(new LinkDoesNotExistDiagnostic(link.source.hrefRange, msg, severity, link.source.text)); } diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index 1490df29279b5..3d0ffd06a6e40 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -256,4 +256,16 @@ suite('markdown: Diagnostics', () => { assert.deepStrictEqual(diagnostics.length, 0); } }); + + test('ignoreLinks should support ignore header links if file is ignored', async () => { + const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( + `![i](/doc2.md#no-such)`, + )); + const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines('')); + + const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration(true, ['/doc2.md'])); + const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); + assert.deepStrictEqual(diagnostics.length, 0); + }); }); From 5d8bd237564650c7257b65b6839597b7150ffe1a Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Mon, 23 May 2022 17:25:00 -0700 Subject: [PATCH 252/942] Add a test --- .../interactiveWindow.test.ts | 65 +++++++++++++++++++ .../src/singlefolder-tests/notebook.test.ts | 4 +- 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts new file mode 100644 index 0000000000000..eafc18804fdd0 --- /dev/null +++ b/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import { disposeAll } from '../utils'; +import { Kernel, saveAllFilesAndCloseAll } from './notebook.test'; + +export type INativeInteractiveWindow = { notebookUri: vscode.Uri; inputUri: vscode.Uri; notebookEditor: vscode.NotebookEditor }; + +async function createInteractiveWindow(kernel: Kernel) { + const { notebookEditor } = (await vscode.commands.executeCommand( + 'interactive.open', + // Keep focus on the owning file if there is one + { viewColumn: vscode.ViewColumn.Beside, preserveFocus: false }, + undefined, + kernel.controller.id, + undefined + )) as unknown as INativeInteractiveWindow; + + return notebookEditor; +} + + +(vscode.env.uiKind === vscode.UIKind.Web ? suite.skip : suite)('Interactive Window', function () { + + const testDisposables: vscode.Disposable[] = []; + let defaultKernel: Kernel; + + setup(async function () { + // there should be ONE default kernel in this suite + defaultKernel = new Kernel('mainKernel', 'Notebook Default Kernel'); + testDisposables.push(defaultKernel.controller); + await saveAllFilesAndCloseAll(); + }); + + teardown(async function () { + disposeAll(testDisposables); + testDisposables.length = 0; + await saveAllFilesAndCloseAll(); + }); + + test('Can open an interactive window', async () => { + assert.ok(vscode.workspace.workspaceFolders); + const notebookEditor = await createInteractiveWindow(defaultKernel); + assert.ok(notebookEditor); + + // Try adding a cell and running it. + const cell = new vscode.NotebookCellData(vscode.NotebookCellKind.Code, 'print foo', 'typescript'); + const edit = vscode.NotebookEdit.insertCells(0, [cell]); + const workspaceEdit = new vscode.WorkspaceEdit(); + workspaceEdit.set(notebookEditor.notebook.uri, [edit]); + await vscode.workspace.applyEdit(workspaceEdit); + + assert.strictEqual(notebookEditor.notebook.cellCount, 1); + assert.strictEqual(notebookEditor.notebook.cellAt(0).kind, vscode.NotebookCellKind.Code); + + await vscode.commands.executeCommand('notebook.execute'); + assert.strictEqual(notebookEditor.notebook.cellAt(0).outputs.length, 1, 'should execute'); + + }); +}); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 462e311369847..b68eb58792207 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -18,7 +18,7 @@ async function openRandomNotebookDocument() { return vscode.workspace.openNotebookDocument(uri); } -async function saveAllFilesAndCloseAll() { +export async function saveAllFilesAndCloseAll() { await saveAllEditors(); await closeAllEditors(); } @@ -29,7 +29,7 @@ async function withEvent(event: vscode.Event, callback: (e: Promise) => } -class Kernel { +export class Kernel { readonly controller: vscode.NotebookController; From 11d269da26315e9469fa86b77ae18a6f3c70c3c2 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 23 May 2022 17:27:21 -0700 Subject: [PATCH 253/942] update tests --- .../notebook/browser/notebookKernelServiceImpl.ts | 4 ++-- .../browser/notebookExecutionStateService.test.ts | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts index 8466386bbdb8a..ab92e2777bd10 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { INotebookTextModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookKernel, ISelectedNotebooksChangeEvent, INotebookKernelMatchResult, INotebookKernelService, INotebookTextModelLike, ISourceAction } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { LRUCache, ResourceMap } from 'vs/base/common/map'; @@ -164,7 +164,7 @@ export class NotebookKernelService extends Disposable implements INotebookKernel override dispose() { this._kernels.clear(); - this._sourceActions.forEach(a => a[1].dispose()); + dispose(this._sourceActions.map(a => a[1])); super.dispose(); } diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts index 887a4a054431a..0b05037e51eba 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts @@ -10,6 +10,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; +import { IMenu, IMenuService } from 'vs/platform/actions/common/actions'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { insertCellAtIndex } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations'; @@ -47,6 +48,16 @@ suite('NotebookExecutionStateService', () => { } }); + instantiationService.stub(IMenuService, new class extends mock() { + override createMenu() { + return new class extends mock() { + override onDidChange = Event.None; + override getActions() { return []; } + override dispose() { } + }; + } + }); + kernelService = instantiationService.createInstance(NotebookKernelService); instantiationService.set(INotebookKernelService, kernelService); instantiationService.set(INotebookExecutionService, instantiationService.createInstance(NotebookExecutionService)); From 568d036b38b5a8925854f71726de4831d23177b8 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 23 May 2022 17:50:03 -0700 Subject: [PATCH 254/942] shim for menu service in more tests. --- .../test/browser/notebookExecutionService.test.ts | 11 +++++++++++ .../test/browser/notebookKernelService.test.ts | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts index b0a288f1e8ed1..e249f73080acc 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts @@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; import { assertThrowsAsync } from 'vs/base/test/common/utils'; import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; +import { IMenu, IMenuService } from 'vs/platform/actions/common/actions'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { insertCellAtIndex } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations'; @@ -42,6 +43,16 @@ suite('NotebookExecutionService', () => { override getNotebookTextModels() { return []; } }); + instantiationService.stub(IMenuService, new class extends mock() { + override createMenu() { + return new class extends mock() { + override onDidChange = Event.None; + override getActions() { return []; } + override dispose() { } + }; + } + }); + kernelService = instantiationService.createInstance(NotebookKernelService); instantiationService.set(INotebookKernelService, kernelService); diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts index b1ebabc1db227..1b03a84c62322 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts @@ -16,6 +16,7 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { DisposableStore } from 'vs/base/common/lifecycle'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; +import { IMenu, IMenuService } from 'vs/platform/actions/common/actions'; suite('NotebookKernelService', () => { @@ -37,6 +38,15 @@ suite('NotebookKernelService', () => { override onWillRemoveNotebookDocument = Event.None; override getNotebookTextModels() { return []; } }); + instantiationService.stub(IMenuService, new class extends mock() { + override createMenu() { + return new class extends mock() { + override onDidChange = Event.None; + override getActions() { return []; } + override dispose() { } + }; + } + }); kernelService = instantiationService.createInstance(NotebookKernelService); instantiationService.set(INotebookKernelService, kernelService); }); From 300060066d4c63d558a75280f55ff9bab8e87ccb Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 23 May 2022 18:01:54 -0700 Subject: [PATCH 255/942] Show watch value in expression tooltip (#150237) Fixes #150081 --- .../contrib/debug/browser/watchExpressionsView.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index 231269cfc63e2..05642d306ffaf 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -274,7 +274,16 @@ export class WatchExpressionsRenderer extends AbstractExpressionsRenderer { protected renderExpression(expression: IExpression, data: IExpressionTemplateData, highlights: IHighlight[]): void { const text = typeof expression.value === 'string' ? `${expression.name}:` : expression.name; - data.label.set(text, highlights, expression.type ? expression.type : expression.value); + let title: string; + if (expression.type) { + title = expression.type === expression.value ? + expression.type : + `${expression.type}: ${expression.value}`; + } else { + title = expression.value; + } + + data.label.set(text, highlights, title); renderExpressionValue(expression, data.value, { showChanged: true, maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET, From 6099f88ce75a807021ebe5c56df953be0e52ac93 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 24 May 2022 09:15:40 +0200 Subject: [PATCH 256/942] fine tune message (#150251) --- .../workbench/contrib/extensions/browser/extensionsActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 7032389c964fa..ee17618c9582c 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -2233,7 +2233,7 @@ export class ExtensionStatusAction extends ExtensionAction { if (this.extension.deprecationInfo) { if (this.extension.deprecationInfo.extension) { const link = `[${this.extension.deprecationInfo.extension.displayName}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.deprecationInfo.extension.id]))}`)})`; - this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate extension tooltip', "This extension has been deprecated. Use {0} extension instead.", link)) }, true); + this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate extension tooltip', "This extension has been deprecated. Use {0} instead.", link)) }, true); } else if (this.extension.deprecationInfo.settings) { const link = `[${localize('settings', "settings")}](${URI.parse(`command:workbench.action.openSettings?${encodeURIComponent(JSON.stringify([this.extension.deprecationInfo.settings.map(setting => `@id:${setting}`).join(' ')]))}`)})`; this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('deprecated with alternate settings tooltip', "This extension is deprecated and has become a native feature in VS Code. Configure these {0} instead.", link)) }, true); From ebcbd3d6368442a6ed40872e410d5d8090c56f5b Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 09:38:47 +0200 Subject: [PATCH 257/942] well defined order for command center bwd/fwd buttons This isn't https://github.com/microsoft/vscode/issues/150208 but was seen in its screen capture --- src/vs/workbench/browser/parts/editor/editor.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 8e0c175392e9d..1d6605352df84 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -314,8 +314,8 @@ if (isMacintosh) { }); } -MenuRegistry.appendMenuItem(MenuId.TitleMenu, { command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, icon: Codicon.arrowLeft } }); -MenuRegistry.appendMenuItem(MenuId.TitleMenu, { command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, icon: Codicon.arrowRight } }); +MenuRegistry.appendMenuItem(MenuId.TitleMenu, { order: 1, command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, icon: Codicon.arrowLeft } }); +MenuRegistry.appendMenuItem(MenuId.TitleMenu, { order: 2, command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, icon: Codicon.arrowRight } }); // Empty Editor Group Toolbar MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroup, { command: { id: UNLOCK_GROUP_COMMAND_ID, title: localize('unlockGroupAction', "Unlock Group"), icon: Codicon.lock }, group: 'navigation', order: 10, when: ActiveEditorGroupLockedContext }); From faa736ed0ef26b9a0bd3f74e11307e9b2b810636 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 24 May 2022 12:22:02 +0200 Subject: [PATCH 258/942] Fixes #150177: Extract `LinePartMetadata` to a separate file (#150259) --- .../common/viewLayout/lineDecorations.ts | 2 +- src/vs/editor/common/viewLayout/linePart.ts | 36 +++++++++++++++++++ .../common/viewLayout/viewLineRenderer.ts | 33 +---------------- 3 files changed, 38 insertions(+), 33 deletions(-) create mode 100644 src/vs/editor/common/viewLayout/linePart.ts diff --git a/src/vs/editor/common/viewLayout/lineDecorations.ts b/src/vs/editor/common/viewLayout/lineDecorations.ts index 36519fa28756b..381e8cfb4b1bd 100644 --- a/src/vs/editor/common/viewLayout/lineDecorations.ts +++ b/src/vs/editor/common/viewLayout/lineDecorations.ts @@ -5,8 +5,8 @@ import * as strings from 'vs/base/common/strings'; import { Constants } from 'vs/base/common/uint'; +import { LinePartMetadata } from 'vs/editor/common/viewLayout/linePart'; import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel'; -import { LinePartMetadata } from 'vs/editor/common/viewLayout/viewLineRenderer'; export class LineDecoration { _lineDecorationBrand: void = undefined; diff --git a/src/vs/editor/common/viewLayout/linePart.ts b/src/vs/editor/common/viewLayout/linePart.ts new file mode 100644 index 0000000000000..a179915fca60b --- /dev/null +++ b/src/vs/editor/common/viewLayout/linePart.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const enum LinePartMetadata { + IS_WHITESPACE = 1, + PSEUDO_BEFORE = 2, + PSEUDO_AFTER = 4, + + IS_WHITESPACE_MASK = 0b001, + PSEUDO_BEFORE_MASK = 0b010, + PSEUDO_AFTER_MASK = 0b100, +} + +export class LinePart { + _linePartBrand: void = undefined; + + constructor( + /** + * last char index of this token (not inclusive). + */ + public readonly endIndex: number, + public readonly type: string, + public readonly metadata: number, + public readonly containsRTL: boolean + ) { } + + public isWhitespace(): boolean { + return (this.metadata & LinePartMetadata.IS_WHITESPACE_MASK ? true : false); + } + + public isPseudoAfter(): boolean { + return (this.metadata & LinePartMetadata.PSEUDO_AFTER_MASK ? true : false); + } +} diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index 1bb33b6cc488e..0dc97db2edec7 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -9,6 +9,7 @@ import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens'; import { IStringBuilder, createStringBuilder } from 'vs/editor/common/core/stringBuilder'; import { LineDecoration, LineDecorationsNormalizer } from 'vs/editor/common/viewLayout/lineDecorations'; import { InlineDecorationType } from 'vs/editor/common/viewModel'; +import { LinePart, LinePartMetadata } from 'vs/editor/common/viewLayout/linePart'; export const enum RenderWhitespace { None = 0, @@ -18,38 +19,6 @@ export const enum RenderWhitespace { All = 4 } -export const enum LinePartMetadata { - IS_WHITESPACE = 1, - PSEUDO_BEFORE = 2, - PSEUDO_AFTER = 4, - - IS_WHITESPACE_MASK = 0b001, - PSEUDO_BEFORE_MASK = 0b010, - PSEUDO_AFTER_MASK = 0b100, -} - -class LinePart { - _linePartBrand: void = undefined; - - constructor( - /** - * last char index of this token (not inclusive). - */ - public readonly endIndex: number, - public readonly type: string, - public readonly metadata: number, - public readonly containsRTL: boolean - ) { } - - public isWhitespace(): boolean { - return (this.metadata & LinePartMetadata.IS_WHITESPACE_MASK ? true : false); - } - - public isPseudoAfter(): boolean { - return (this.metadata & LinePartMetadata.PSEUDO_AFTER_MASK ? true : false); - } -} - export class LineRange { /** * Zero-based offset on which the range starts, inclusive. From 9ef07308ea7885dcfb3f21076dc09dc9884a5fec Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 24 May 2022 12:52:19 +0200 Subject: [PATCH 259/942] Finalizes inline completions --- .../workbench/api/common/extHost.api.impl.ts | 1 - .../actions/common/menusExtensionPoint.ts | 2 +- .../common/extensionsApiProposals.ts | 1 - src/vscode-dts/vscode.d.ts | 149 ++++++++++++++++ .../vscode.proposed.inlineCompletions.d.ts | 162 ------------------ 5 files changed, 150 insertions(+), 165 deletions(-) delete mode 100644 src/vscode-dts/vscode.proposed.inlineCompletions.d.ts diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 1cac3df22539e..364272a3ecd45 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -524,7 +524,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostLanguageFeatures.registerCompletionItemProvider(extension, checkSelector(selector), provider, triggerCharacters); }, registerInlineCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProvider): vscode.Disposable { - checkProposedApiEnabled(extension, 'inlineCompletions'); if (provider.handleDidShowCompletionItem) { checkProposedApiEnabled(extension, 'inlineCompletionsAdditions'); } diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index 1995534162750..36560ff0d776b 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -254,7 +254,7 @@ const apiMenus: IAPIMenu[] = [ id: MenuId.InlineCompletionsActions, description: localize('inlineCompletions.actions', "The actions shown when hovering on an inline completion"), supportsSubmenus: false, - proposed: 'inlineCompletions' + proposed: 'inlineCompletionsAdditions' }, ]; diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index d5ded005d7276..993118366837b 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -26,7 +26,6 @@ export const allApiProposals = Object.freeze({ findTextInFiles: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts', fsChunks: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.fsChunks.d.ts', idToken: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.idToken.d.ts', - inlineCompletions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts', inlineCompletionsAdditions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts', inlineCompletionsNew: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts', ipc: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.ipc.d.ts', diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 3d7b87599f1a8..8b4e5197a2724 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -12588,6 +12588,18 @@ declare module 'vscode' { */ export function setLanguageConfiguration(language: string, configuration: LanguageConfiguration): Disposable; + /** + * Registers an inline completion provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An inline completion provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; } /** @@ -15936,6 +15948,143 @@ declare module 'vscode' { */ close(tabGroup: TabGroup | readonly TabGroup[], preserveFocus?: boolean): Thenable; } + + /** + * The inline completion item provider interface defines the contract between extensions and + * the inline completion feature. + * + * Providers are asked for completions either explicitly by a user gesture or implicitly when typing. + */ + export interface InlineCompletionItemProvider { + + /** + * Provides inline completion items for the given position and document. + * If inline completions are enabled, this method will be called whenever the user stopped typing. + * It will also be called when the user explicitly triggers inline completions or explicitly asks for the next or previous inline completion. + * In that case, all available inline completions should be returned. + * `context.triggerKind` can be used to distinguish between these scenarios. + * + * @param document The document inline completions are requested for. + * @param position The position inline completions are requested for. + * @param context A context object with additional information. + * @param token A cancellation token. + * @return An array of completion items or a thenable that resolves to an array of completion items. + */ + provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; + } + + /** + * Represents a collection of {@link InlineCompletionItem inline completion items} to be presented + * in the editor. + */ + export class InlineCompletionList { + /** + * The inline completion items. + */ + items: InlineCompletionItem[]; + + /** + * Creates a new list of inline completion items. + */ + constructor(items: InlineCompletionItem[]); + } + + /** + * Provides information about the context in which an inline completion was requested. + */ + export interface InlineCompletionContext { + /** + * Describes how the inline completion was triggered. + */ + readonly triggerKind: InlineCompletionTriggerKind; + + /** + * Provides information about the currently selected item in the autocomplete widget if it is visible. + * + * If set, provided inline completions must extend the text of the selected item + * and use the same range, otherwise they are not shown as preview. + * As an example, if the document text is `console.` and the selected item is `.log` replacing the `.` in the document, + * the inline completion must also replace `.` and start with `.log`, for example `.log()`. + * + * Inline completion providers are requested again whenever the selected item changes. + */ + readonly selectedCompletionInfo: SelectedCompletionInfo | undefined; + } + + /** + * Describes the currently selected completion item. + */ + export interface SelectedCompletionInfo { + /** + * The range that will be replaced if this completion item is accepted. + */ + readonly range: Range; + + /** + * The text the range will be replaced with if this completion is accepted. + */ + readonly text: string; + } + + /** + * Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. + */ + export enum InlineCompletionTriggerKind { + /** + * Completion was triggered explicitly by a user gesture. + * Return multiple completion items to enable cycling through them. + */ + Invoke = 0, + + /** + * Completion was triggered automatically while editing. + * It is sufficient to return a single completion item in this case. + */ + Automatic = 1, + } + + /** + * An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. + * + * @see {@link InlineCompletionItemProvider.provideInlineCompletionItems} + */ + export class InlineCompletionItem { + /** + * The text to replace the range with. Must be set. + * Is used both for the preview and the accept operation. + */ + insertText: string | SnippetString; + + /** + * A text that is used to decide if this inline completion should be shown. When `falsy` + * the {@link InlineCompletionItem.insertText} is used. + * + * An inline completion is shown if the text to replace is a prefix of the filter text. + */ + filterText?: string; + + /** + * The range to replace. + * Must begin and end on the same line. + * + * Prefer replacements over insertions to provide a better experience when the user deletes typed text. + */ + range?: Range; + + /** + * An optional {@link Command} that is executed *after* inserting this completion. + */ + command?: Command; + + /** + * Creates a new inline completion item. + * + * @param insertText The text to replace the range with. + * @param range The range to replace. If not set, the word at the requested position will be used. + * @param command An optional {@link Command} that is executed *after* inserting this completion. + */ + constructor(insertText: string | SnippetString, range?: Range, command?: Command); + } } /** diff --git a/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts b/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts deleted file mode 100644 index c070547c96761..0000000000000 --- a/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts +++ /dev/null @@ -1,162 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - - // https://github.com/microsoft/vscode/issues/124024 @hediet @alexdima - - export namespace languages { - - /** - * Registers an inline completion provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider An inline completion provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; - } - - /** - * The inline completion item provider interface defines the contract between extensions and - * the inline completion feature. - * - * Providers are asked for completions either explicitly by a user gesture or implicitly when typing. - */ - export interface InlineCompletionItemProvider { - - /** - * Provides inline completion items for the given position and document. - * If inline completions are enabled, this method will be called whenever the user stopped typing. - * It will also be called when the user explicitly triggers inline completions or explicitly asks for the next or previous inline completion. - * In that case, all available inline completions should be returned. - * `context.triggerKind` can be used to distinguish between these scenarios. - * - * @param document The document inline completions are requested for. - * @param position The position inline completions are requested for. - * @param context A context object with additional information. - * @param token A cancellation token. - * @return An array of completion items or a thenable that resolves to an array of completion items. - */ - provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; - } - - /** - * Represents a collection of {@link InlineCompletionItem inline completion items} to be presented - * in the editor. - */ - export class InlineCompletionList { - /** - * The inline completion items. - */ - items: InlineCompletionItem[]; - - /** - * Creates a new list of inline completion items. - */ - constructor(items: InlineCompletionItem[]); - } - - /** - * Provides information about the context in which an inline completion was requested. - */ - export interface InlineCompletionContext { - /** - * Describes how the inline completion was triggered. - */ - readonly triggerKind: InlineCompletionTriggerKind; - - /** - * Provides information about the currently selected item in the autocomplete widget if it is visible. - * - * If set, provided inline completions must extend the text of the selected item - * and use the same range, otherwise they are not shown as preview. - * As an example, if the document text is `console.` and the selected item is `.log` replacing the `.` in the document, - * the inline completion must also replace `.` and start with `.log`, for example `.log()`. - * - * Inline completion providers are requested again whenever the selected item changes. - */ - readonly selectedCompletionInfo: SelectedCompletionInfo | undefined; - } - - /** - * Describes the currently selected completion item. - */ - export interface SelectedCompletionInfo { - /** - * The range that will be replaced if this completion item is accepted. - */ - readonly range: Range; - - /** - * The text the range will be replaced with if this completion is accepted. - */ - readonly text: string; - } - - /** - * Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. - */ - export enum InlineCompletionTriggerKind { - /** - * Completion was triggered explicitly by a user gesture. - * Return multiple completion items to enable cycling through them. - */ - Invoke = 0, - - /** - * Completion was triggered automatically while editing. - * It is sufficient to return a single completion item in this case. - */ - Automatic = 1, - } - - /** - * An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. - * - * @see {@link InlineCompletionItemProvider.provideInlineCompletionItems} - */ - export class InlineCompletionItem { - /** - * The text to replace the range with. Must be set. - * Is used both for the preview and the accept operation. - */ - insertText: string | SnippetString; - - /** - * A text that is used to decide if this inline completion should be shown. When `falsy` - * the {@link InlineCompletionItem.insertText} is used. - * - * An inline completion is shown if the text to replace is a prefix of the filter text. - */ - filterText?: string; - - /** - * The range to replace. - * Must begin and end on the same line. - * - * Prefer replacements over insertions to provide a better experience when the user deletes typed text. - */ - range?: Range; - - /** - * An optional {@link Command} that is executed *after* inserting this completion. - */ - command?: Command; - - /** - * Creates a new inline completion item. - * - * @param insertText The text to replace the range with. - * @param range The range to replace. If not set, the word at the requested position will be used. - * @param command An optional {@link Command} that is executed *after* inserting this completion. - */ - constructor(insertText: string | SnippetString, range?: Range, command?: Command); - } -} From 957a9dc194c23b193fd1d613d61afcaa960dd730 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 12:53:25 +0200 Subject: [PATCH 260/942] rename `titleMenu` to `commandCenter` This changes hows types and variables are called but also theme colors and the setting name are changing --- src/vs/platform/actions/common/actions.ts | 2 +- .../browser/actions/quickAccessActions.ts | 2 +- src/vs/workbench/browser/layout.ts | 2 +- .../parts/editor/editor.contribution.ts | 4 +- ...MenuControl.ts => commandCenterControl.ts} | 38 +++++++++---------- .../parts/titlebar/media/titlebarpart.css | 32 ++++++++-------- .../browser/parts/titlebar/titlebarPart.ts | 20 +++++----- .../browser/workbench.contribution.ts | 4 +- .../services/title/common/titleService.ts | 2 +- 9 files changed, 53 insertions(+), 53 deletions(-) rename src/vs/workbench/browser/parts/titlebar/{titleMenuControl.ts => commandCenterControl.ts} (87%) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index beff01998fa62..881043379fc98 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -67,7 +67,7 @@ export class MenuId { static readonly ExplorerContext = new MenuId('ExplorerContext'); static readonly ExtensionContext = new MenuId('ExtensionContext'); static readonly GlobalActivity = new MenuId('GlobalActivity'); - static readonly TitleMenu = new MenuId('TitleMenu'); + static readonly CommandCenter = new MenuId('CommandCenter'); static readonly LayoutControlMenuSubmenu = new MenuId('LayoutControlMenuSubmenu'); static readonly LayoutControlMenu = new MenuId('LayoutControlMenu'); static readonly MenubarMainMenu = new MenuId('MenubarMainMenu'); diff --git a/src/vs/workbench/browser/actions/quickAccessActions.ts b/src/vs/workbench/browser/actions/quickAccessActions.ts index 8c08c8e805727..6cda33c142ce0 100644 --- a/src/vs/workbench/browser/actions/quickAccessActions.ts +++ b/src/vs/workbench/browser/actions/quickAccessActions.ts @@ -141,7 +141,7 @@ registerAction2(class QuickAccessAction extends Action2 { }, f1: true, menu: { - id: MenuId.TitleMenu, + id: MenuId.CommandCenter, order: 100 } }); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 2740f42fde9a3..10a0a5e03ec14 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -148,7 +148,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi let quickPickTop = 0; if (this.isVisible(Parts.TITLEBAR_PART)) { top = this.getPart(Parts.TITLEBAR_PART).maximumHeight; - quickPickTop = this.titleService.titleMenuVisible ? 0 : top; + quickPickTop = this.titleService.isCommandCenterVisible ? 0 : top; } return { top, quickPickTop }; } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 1d6605352df84..ef757abd139a4 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -314,8 +314,8 @@ if (isMacintosh) { }); } -MenuRegistry.appendMenuItem(MenuId.TitleMenu, { order: 1, command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, icon: Codicon.arrowLeft } }); -MenuRegistry.appendMenuItem(MenuId.TitleMenu, { order: 2, command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, icon: Codicon.arrowRight } }); +MenuRegistry.appendMenuItem(MenuId.CommandCenter, { order: 1, command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, icon: Codicon.arrowLeft } }); +MenuRegistry.appendMenuItem(MenuId.CommandCenter, { order: 2, command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, icon: Codicon.arrowRight } }); // Empty Editor Group Toolbar MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroup, { command: { id: UNLOCK_GROUP_COMMAND_ID, title: localize('unlockGroupAction', "Unlock Group"), icon: Codicon.lock }, group: 'navigation', order: 10, when: ActiveEditorGroupLockedContext }); diff --git a/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts similarity index 87% rename from src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts rename to src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts index 63dd190ea8298..c0728f1c334bf 100644 --- a/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts @@ -27,7 +27,7 @@ import { WindowTitle } from 'vs/workbench/browser/parts/titlebar/windowTitle'; import { MENUBAR_SELECTION_BACKGROUND, MENUBAR_SELECTION_FOREGROUND, TITLE_BAR_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; -export class TitleMenuControl { +export class CommandCenterControl { private readonly _disposables = new DisposableStore(); @@ -47,7 +47,7 @@ export class TitleMenuControl { @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService, ) { - this.element.classList.add('title-menu'); + this.element.classList.add('command-center'); const hoverDelegate = new class implements IHoverDelegate { @@ -134,16 +134,16 @@ export class TitleMenuControl { return createActionViewItem(instantiationService, action, { hoverDelegate }); } }); - const titleMenu = this._disposables.add(menuService.createMenu(MenuId.TitleMenu, contextKeyService)); - const titleMenuDisposables = this._disposables.add(new DisposableStore()); - const updateTitleMenu = () => { - titleMenuDisposables.clear(); + const menu = this._disposables.add(menuService.createMenu(MenuId.CommandCenter, contextKeyService)); + const menuDisposables = this._disposables.add(new DisposableStore()); + const menuUpdater = () => { + menuDisposables.clear(); const actions: IAction[] = []; - titleMenuDisposables.add(createAndFillInContextMenuActions(titleMenu, undefined, actions)); + menuDisposables.add(createAndFillInContextMenuActions(menu, undefined, actions)); titleToolbar.setActions(actions); }; - updateTitleMenu(); - this._disposables.add(titleMenu.onDidChange(updateTitleMenu)); + menuUpdater(); + this._disposables.add(menu.onDidChange(menuUpdater)); this._disposables.add(quickInputService.onShow(this._setVisibility.bind(this, false))); this._disposables.add(quickInputService.onHide(this._setVisibility.bind(this, true))); } @@ -162,34 +162,34 @@ export class TitleMenuControl { // foreground (inactive and active) colors.registerColor( - 'titleMenu.foreground', + 'commandCenter.foreground', { dark: TITLE_BAR_ACTIVE_FOREGROUND, hcDark: TITLE_BAR_ACTIVE_FOREGROUND, light: TITLE_BAR_ACTIVE_FOREGROUND, hcLight: TITLE_BAR_ACTIVE_FOREGROUND }, - localize('titleMenu-foreground', "Foreground color of the title menu"), + localize('commandCenter-foreground', "Foreground color of the command center"), false ); colors.registerColor( - 'titleMenu.activeForeground', + 'commandCenter.activeForeground', { dark: MENUBAR_SELECTION_FOREGROUND, hcDark: MENUBAR_SELECTION_FOREGROUND, light: MENUBAR_SELECTION_FOREGROUND, hcLight: MENUBAR_SELECTION_FOREGROUND }, - localize('titleMenu-activeForeground', "Active foreground color of the title menu"), + localize('commandCenter-activeForeground', "Active foreground color of the command center"), false ); // background (inactive and active) colors.registerColor( - 'titleMenu.background', + 'commandCenter.background', { dark: null, hcDark: null, light: null, hcLight: null }, - localize('titleMenu-background', "Background color of the title menu"), + localize('commandCenter-background', "Background color of the command center"), false ); const activeBackground = colors.registerColor( - 'titleMenu.activeBackground', + 'commandCenter.activeBackground', { dark: MENUBAR_SELECTION_BACKGROUND, hcDark: MENUBAR_SELECTION_BACKGROUND, light: MENUBAR_SELECTION_BACKGROUND, hcLight: MENUBAR_SELECTION_BACKGROUND }, - localize('titleMenu-activeBackground', "Active background color of the title menu"), + localize('commandCenter-activeBackground', "Active background color of the command center"), false ); // border: defaults to active background colors.registerColor( - 'titleMenu.border', + 'commandCenter.border', { dark: activeBackground, hcDark: colors.inputBorder, light: activeBackground, hcLight: colors.inputBorder }, - localize('titleMenu-border', "Border color of the title menu"), + localize('commandCenter-border', "Border color of the command center"), false ); diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 1a1d9490d5c06..77abc65361444 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -80,21 +80,21 @@ } /* Window Title Menu */ -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center { z-index: 2550; -webkit-app-region: no-drag; padding: 0 8px; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu.hide { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center.hide { display: none; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center .action-item.quickopen { display: flex; - color: var(--vscode-titleMenu-foreground); - background-color: var(--vscode-titleMenu-background); - border: 1px solid var(--vscode-titleMenu-border); + color: var(--vscode-commandCenter-foreground); + background-color: var(--vscode-commandCenter-background); + border: 1px solid var(--vscode-commandCenter-border); border-radius: 5px; height: 20px; line-height: 18px; @@ -106,18 +106,18 @@ overflow: hidden; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen:HOVER { - color: var(--vscode-titleMenu-activeForeground); - background-color: var(--vscode-titleMenu-activeBackground); +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center .action-item.quickopen:HOVER { + color: var(--vscode-commandCenter-activeForeground); + background-color: var(--vscode-commandCenter-activeBackground); line-height: 18px; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu:HOVER .quickopen .action-label { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center:HOVER .quickopen .action-label { background-color: transparent !important; outline-color: transparent !important; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen>.action-label.search { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center .action-item.quickopen>.action-label.search { display: inline-flex; text-align: center; font-size: 12px; @@ -127,18 +127,18 @@ width: calc(100% - 19px); } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen>.action-label.search>.search-icon { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center .action-item.quickopen>.action-label.search>.search-icon { font-size: 14px; opacity: .8; padding: 0 3px; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen>.action-label.search>.search-label { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center .action-item.quickopen>.action-label.search>.search-label { overflow: hidden; text-overflow: ellipsis; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen>.all-options>.action-label { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center .action-item.quickopen>.all-options>.action-label { text-align: center; font-size: 12px; width: 16px; @@ -147,8 +147,8 @@ padding-right: 0; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu:HOVER .action-item.quickopen>.all-options>.action-label { - border-color: var(--vscode-titleMenu-border); +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.command-center:HOVER .action-item.quickopen>.all-options>.action-label { + border-color: var(--vscode-commandCenter-border); } /* Menubar */ diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index e35c289cb8e5b..e549362ba2d3b 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -33,11 +33,11 @@ import { Codicon } from 'vs/base/common/codicons'; import { getIconRegistry } from 'vs/platform/theme/common/iconRegistry'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { WindowTitle } from 'vs/workbench/browser/parts/titlebar/windowTitle'; -import { TitleMenuControl } from 'vs/workbench/browser/parts/titlebar/titleMenuControl'; +import { CommandCenterControl } from 'vs/workbench/browser/parts/titlebar/commandCenterControl'; export class TitlebarPart extends Part implements ITitleService { - private static readonly configTitleMenu = 'window.experimental.titleMenu'; + private static readonly configCommandCenter = 'window.experimental.commandCenter'; declare readonly _serviceBrand: undefined; @@ -102,8 +102,8 @@ export class TitlebarPart extends Part implements ITitleService { this.windowTitle.updateProperties(properties); } - get titleMenuVisible() { - return this.configurationService.getValue(TitlebarPart.configTitleMenu); + get isCommandCenterVisible() { + return this.configurationService.getValue(TitlebarPart.configCommandCenter); } private registerListeners(): void { @@ -137,7 +137,7 @@ export class TitlebarPart extends Part implements ITitleService { this.layoutControls.classList.toggle('show-layout-control', this.layoutControlEnabled); } - if (event.affectsConfiguration(TitlebarPart.configTitleMenu)) { + if (event.affectsConfiguration(TitlebarPart.configCommandCenter)) { this.updateTitle(); this.adjustTitleMarginToCenter(); this._onDidChangeTitleMenuVisibility.fire(); @@ -183,7 +183,7 @@ export class TitlebarPart extends Part implements ITitleService { private updateTitle(): void { this.titleDisposables.clear(); - if (!this.titleMenuVisible) { + if (!this.isCommandCenterVisible) { // Text Title this.title.innerText = this.windowTitle.value; this.titleDisposables.add(this.windowTitle.onDidChange(() => { @@ -192,10 +192,10 @@ export class TitlebarPart extends Part implements ITitleService { })); } else { // Menu Title - const titleMenu = this.instantiationService.createInstance(TitleMenuControl, this.windowTitle); - reset(this.title, titleMenu.element); - this.titleDisposables.add(titleMenu); - this.titleDisposables.add(titleMenu.onDidChangeVisibility(this.adjustTitleMarginToCenter, this)); + const commandCenter = this.instantiationService.createInstance(CommandCenterControl, this.windowTitle); + reset(this.title, commandCenter.element); + this.titleDisposables.add(commandCenter); + this.titleDisposables.add(commandCenter.onDidChangeVisibility(this.adjustTitleMarginToCenter, this)); } } diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index a0adbe40cb241..1e654bb15a173 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -538,10 +538,10 @@ const registry = Registry.as(ConfigurationExtensions.Con 'default': isMacintosh ? ' \u2014 ' : ' - ', 'markdownDescription': localize("window.titleSeparator", "Separator used by `window.title`.") }, - 'window.experimental.titleMenu': { + 'window.experimental.commandCenter': { type: 'boolean', default: false, - description: localize('window.experimental.titleMenu', "Show window title as menu") + description: localize('window.experimental.commandCenter', "Show command launcher together with the window title.") }, 'window.menuBarVisibility': { 'type': 'string', diff --git a/src/vs/workbench/services/title/common/titleService.ts b/src/vs/workbench/services/title/common/titleService.ts index a89bb2f096acd..92aca9f92cd5a 100644 --- a/src/vs/workbench/services/title/common/titleService.ts +++ b/src/vs/workbench/services/title/common/titleService.ts @@ -26,7 +26,7 @@ export interface ITitleService { /** * Title menu is visible */ - readonly titleMenuVisible: boolean; + readonly isCommandCenterVisible: boolean; /** * An event when the title menu is enabled/disabled From 67c79ededdfe616c32509f6b7e46f4c0758b50bb Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 12:57:38 +0200 Subject: [PATCH 261/942] clarify when/how setting is effective --- src/vs/workbench/browser/workbench.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 1e654bb15a173..36dc82658061b 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -541,7 +541,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'window.experimental.commandCenter': { type: 'boolean', default: false, - description: localize('window.experimental.commandCenter', "Show command launcher together with the window title.") + markdownDescription: localize('window.experimental.commandCenter', "Show command launcher together with the window title. This setting only has an effect when `#window.titleBarStyle#` is set to `custom`.") }, 'window.menuBarVisibility': { 'type': 'string', From 753fc8a044784de7d5f36fe849a1ccfb26d417ed Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 24 May 2022 13:16:43 +0200 Subject: [PATCH 262/942] Fixes bracket fixing bug for inline completions. --- .../bracketPairsTextModelPart/bracketPairsTree/brackets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts b/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts index 280cb3b8deb57..2673aeacb66ae 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts +++ b/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts @@ -85,7 +85,7 @@ export class BracketTokens { findClosingTokenText(openingBracketIds: SmallImmutableSet): string | undefined { for (const [closingText, info] of this.map) { - if (info.bracketIds.intersects(openingBracketIds)) { + if (info.kind === TokenKind.ClosingBracket && info.bracketIds.intersects(openingBracketIds)) { return closingText; } } From 34a75f45d688ec88f1635670b783e805a1778db4 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 14:45:09 +0200 Subject: [PATCH 263/942] use CSS variables for colors --- .../contrib/mergeEditor/browser/media/mergeEditor.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css index 0b9caf7faf892..abae99ac16ca2 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css +++ b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css @@ -56,7 +56,7 @@ } .merge-accept-gutter-marker.multi-line .background { - border: 2px solid #323232FF; + border: 2px solid var(--vscode-checkbox-border); border-right: 0; left: 8px; width: 10px; @@ -74,5 +74,5 @@ .merge-accept-gutter-marker .checkbox .accept-conflict-group.monaco-custom-toggle.monaco-checkbox { margin: 0; padding: 0; - background-color: #3C3C3CFF; + background-color: var(--vscode-checkbox-border); } From 3ab95815b5b3adb4a7029e2095f6fa1d5720d5df Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 24 May 2022 14:57:08 +0200 Subject: [PATCH 264/942] Fixes nits. --- src/vscode-dts/vscode.d.ts | 301 +++++++++++++++++++------------------ 1 file changed, 151 insertions(+), 150 deletions(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 8b4e5197a2724..292d369a5577c 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -4498,6 +4498,144 @@ declare module 'vscode' { resolveCompletionItem?(item: T, token: CancellationToken): ProviderResult; } + + /** + * The inline completion item provider interface defines the contract between extensions and + * the inline completion feature. + * + * Providers are asked for completions either explicitly by a user gesture or implicitly when typing. + */ + export interface InlineCompletionItemProvider { + + /** + * Provides inline completion items for the given position and document. + * If inline completions are enabled, this method will be called whenever the user stopped typing. + * It will also be called when the user explicitly triggers inline completions or explicitly asks for the next or previous inline completion. + * In that case, all available inline completions should be returned. + * `context.triggerKind` can be used to distinguish between these scenarios. + * + * @param document The document inline completions are requested for. + * @param position The position inline completions are requested for. + * @param context A context object with additional information. + * @param token A cancellation token. + * @return An array of completion items or a thenable that resolves to an array of completion items. + */ + provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; + } + + /** + * Represents a collection of {@link InlineCompletionItem inline completion items} to be presented + * in the editor. + */ + export class InlineCompletionList { + /** + * The inline completion items. + */ + items: InlineCompletionItem[]; + + /** + * Creates a new list of inline completion items. + */ + constructor(items: InlineCompletionItem[]); + } + + /** + * Provides information about the context in which an inline completion was requested. + */ + export interface InlineCompletionContext { + /** + * Describes how the inline completion was triggered. + */ + readonly triggerKind: InlineCompletionTriggerKind; + + /** + * Provides information about the currently selected item in the autocomplete widget if it is visible. + * + * If set, provided inline completions must extend the text of the selected item + * and use the same range, otherwise they are not shown as preview. + * As an example, if the document text is `console.` and the selected item is `.log` replacing the `.` in the document, + * the inline completion must also replace `.` and start with `.log`, for example `.log()`. + * + * Inline completion providers are requested again whenever the selected item changes. + */ + readonly selectedCompletionInfo: SelectedCompletionInfo | undefined; + } + + /** + * Describes the currently selected completion item. + */ + export interface SelectedCompletionInfo { + /** + * The range that will be replaced if this completion item is accepted. + */ + readonly range: Range; + + /** + * The text the range will be replaced with if this completion is accepted. + */ + readonly text: string; + } + + /** + * Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. + */ + export enum InlineCompletionTriggerKind { + /** + * Completion was triggered explicitly by a user gesture. + * Return multiple completion items to enable cycling through them. + */ + Invoke = 0, + + /** + * Completion was triggered automatically while editing. + * It is sufficient to return a single completion item in this case. + */ + Automatic = 1, + } + + /** + * An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. + * + * @see {@link InlineCompletionItemProvider.provideInlineCompletionItems} + */ + export class InlineCompletionItem { + /** + * The text to replace the range with. Must be set. + * Is used both for the preview and the accept operation. + */ + insertText: string | SnippetString; + + /** + * A text that is used to decide if this inline completion should be shown. When `falsy` + * the {@link InlineCompletionItem.insertText} is used. + * + * An inline completion is shown if the text to replace is a prefix of the filter text. + */ + filterText?: string; + + /** + * The range to replace. + * Must begin and end on the same line. + * + * Prefer replacements over insertions to provide a better experience when the user deletes typed text. + */ + range?: Range; + + /** + * An optional {@link Command} that is executed *after* inserting this completion. + */ + command?: Command; + + /** + * Creates a new inline completion item. + * + * @param insertText The text to replace the range with. + * @param range The range to replace. If not set, the word at the requested position will be used. + * @param command An optional {@link Command} that is executed *after* inserting this completion. + */ + constructor(insertText: string | SnippetString, range?: Range, command?: Command); + } + /** * A document link is a range in a text document that links to an internal or external resource, like another * text document or a web site. @@ -12202,6 +12340,19 @@ declare module 'vscode' { */ export function registerCompletionItemProvider(selector: DocumentSelector, provider: CompletionItemProvider, ...triggerCharacters: string[]): Disposable; + /** + * Registers an inline completion provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An inline completion provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; + /** * Register a code action provider. * @@ -12587,19 +12738,6 @@ declare module 'vscode' { * @return A {@link Disposable} that unsets this configuration. */ export function setLanguageConfiguration(language: string, configuration: LanguageConfiguration): Disposable; - - /** - * Registers an inline completion provider. - * - * Multiple providers can be registered for a language. In that case providers are asked in - * parallel and the results are merged. A failing provider (rejected promise or exception) will - * not cause a failure of the whole operation. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider An inline completion provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; } /** @@ -15948,143 +16086,6 @@ declare module 'vscode' { */ close(tabGroup: TabGroup | readonly TabGroup[], preserveFocus?: boolean): Thenable; } - - /** - * The inline completion item provider interface defines the contract between extensions and - * the inline completion feature. - * - * Providers are asked for completions either explicitly by a user gesture or implicitly when typing. - */ - export interface InlineCompletionItemProvider { - - /** - * Provides inline completion items for the given position and document. - * If inline completions are enabled, this method will be called whenever the user stopped typing. - * It will also be called when the user explicitly triggers inline completions or explicitly asks for the next or previous inline completion. - * In that case, all available inline completions should be returned. - * `context.triggerKind` can be used to distinguish between these scenarios. - * - * @param document The document inline completions are requested for. - * @param position The position inline completions are requested for. - * @param context A context object with additional information. - * @param token A cancellation token. - * @return An array of completion items or a thenable that resolves to an array of completion items. - */ - provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; - } - - /** - * Represents a collection of {@link InlineCompletionItem inline completion items} to be presented - * in the editor. - */ - export class InlineCompletionList { - /** - * The inline completion items. - */ - items: InlineCompletionItem[]; - - /** - * Creates a new list of inline completion items. - */ - constructor(items: InlineCompletionItem[]); - } - - /** - * Provides information about the context in which an inline completion was requested. - */ - export interface InlineCompletionContext { - /** - * Describes how the inline completion was triggered. - */ - readonly triggerKind: InlineCompletionTriggerKind; - - /** - * Provides information about the currently selected item in the autocomplete widget if it is visible. - * - * If set, provided inline completions must extend the text of the selected item - * and use the same range, otherwise they are not shown as preview. - * As an example, if the document text is `console.` and the selected item is `.log` replacing the `.` in the document, - * the inline completion must also replace `.` and start with `.log`, for example `.log()`. - * - * Inline completion providers are requested again whenever the selected item changes. - */ - readonly selectedCompletionInfo: SelectedCompletionInfo | undefined; - } - - /** - * Describes the currently selected completion item. - */ - export interface SelectedCompletionInfo { - /** - * The range that will be replaced if this completion item is accepted. - */ - readonly range: Range; - - /** - * The text the range will be replaced with if this completion is accepted. - */ - readonly text: string; - } - - /** - * Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. - */ - export enum InlineCompletionTriggerKind { - /** - * Completion was triggered explicitly by a user gesture. - * Return multiple completion items to enable cycling through them. - */ - Invoke = 0, - - /** - * Completion was triggered automatically while editing. - * It is sufficient to return a single completion item in this case. - */ - Automatic = 1, - } - - /** - * An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. - * - * @see {@link InlineCompletionItemProvider.provideInlineCompletionItems} - */ - export class InlineCompletionItem { - /** - * The text to replace the range with. Must be set. - * Is used both for the preview and the accept operation. - */ - insertText: string | SnippetString; - - /** - * A text that is used to decide if this inline completion should be shown. When `falsy` - * the {@link InlineCompletionItem.insertText} is used. - * - * An inline completion is shown if the text to replace is a prefix of the filter text. - */ - filterText?: string; - - /** - * The range to replace. - * Must begin and end on the same line. - * - * Prefer replacements over insertions to provide a better experience when the user deletes typed text. - */ - range?: Range; - - /** - * An optional {@link Command} that is executed *after* inserting this completion. - */ - command?: Command; - - /** - * Creates a new inline completion item. - * - * @param insertText The text to replace the range with. - * @param range The range to replace. If not set, the word at the requested position will be used. - * @param command An optional {@link Command} that is executed *after* inserting this completion. - */ - constructor(insertText: string | SnippetString, range?: Range, command?: Command); - } } /** From 0e8b6a43b0deb31e8c23d0c3b1c23f218f2c593f Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 15:00:51 +0200 Subject: [PATCH 265/942] show correct description for input2 --- src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index f46dce24ff15b..0f0ef7c4cba35 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -184,7 +184,7 @@ export class MergeEditor extends EditorPane { const model = await input.resolve(); this.input1View.setModel(model, model.input1, localize('yours', 'Yours'), model.input1Detail, model.input1Description); - this.input2View.setModel(model, model.input2, localize('theirs', 'Theirs',), model.input2Detail, model.input1Description); + this.input2View.setModel(model, model.input2, localize('theirs', 'Theirs',), model.input2Detail, model.input2Description); this.inputResultView.setModel(model, model.result, localize('result', 'Result',), this._labelService.getUriLabel(model.result.uri, { relative: true }), undefined); // TODO: Update editor options! From 3e83fa95b06af67b9e0e3bbffda3f93e9fafe090 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 24 May 2022 15:06:34 +0200 Subject: [PATCH 266/942] Fix #150266 (#150267) --- extensions/git/src/repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index c7962fb1a62ff..d3626bc0de320 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -546,7 +546,7 @@ class DotGitWatcher implements IFileWatcher { // Ignore changes to the "index.lock" file, and watchman fsmonitor hook (https://git-scm.com/docs/githooks#_fsmonitor_watchman) cookie files. // Watchman creates a cookie file inside the git directory whenever a query is run (https://facebook.github.io/watchman/docs/cookies.html). - const filteredRootWatcher = filterEvent(rootWatcher.event, uri => !/\/\.git(\/index\.lock)?$|\/\.watchman-cookie-/.test(uri.path)); + const filteredRootWatcher = filterEvent(rootWatcher.event, uri => uri.scheme === 'file' && !/\/\.git(\/index\.lock)?$|\/\.watchman-cookie-/.test(uri.path)); this.event = anyEvent(filteredRootWatcher, this.emitter.event); repository.onDidRunGitStatus(this.updateTransientWatchers, this, this.disposables); From 8e84b5b0152cdfe5bacbf8649cae127c1094d925 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 15:32:00 +0200 Subject: [PATCH 267/942] increase title part height when CC shows * add 4px extra space to title bar part when CC is enabled * trigger relayout when toggling CC so that new height is taken into account * macos: manually position traffic lights so that they line-up with CC --- .../platform/windows/electron-main/window.ts | 19 +++++++++++++++++++ src/vs/workbench/browser/layout.ts | 2 +- .../browser/parts/titlebar/titlebarPart.ts | 5 ++++- .../parts/titlebar/titlebarPart.ts | 8 +++++++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 3b8ba525827d8..e181acbdfe0b9 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -271,6 +271,25 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._id = this._win.id; + // re-position traffic light if command center is visible + if (useCustomTitleStyle && isMacintosh) { + const ccConfigKey = 'window.experimental.commandCenter'; + const trafficLightUpdater = () => { + const on = this.configurationService.getValue(ccConfigKey); + if (on) { + this._win.setTrafficLightPosition({ x: 7, y: 8 }); + } else { + this._win.setTrafficLightPosition({ x: 7, y: 6 }); + } + }; + trafficLightUpdater(); + this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(ccConfigKey)) { + trafficLightUpdater(); + } + }); + } + // Open devtools if instructed from command line args if (this.environmentMainService.args['open-devtools'] === true) { this._win.webContents.openDevTools(); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 10a0a5e03ec14..43168e2f92731 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -269,7 +269,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // Title Menu changes - this._register(this.titleService.onDidChangeTitleMenuVisibility(() => this._onDidLayout.fire(this._dimension))); + this._register(this.titleService.onDidChangeTitleMenuVisibility(() => this.layout())); // Theme changes this._register(this.themeService.onDidColorThemeChange(() => this.updateStyles())); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index e549362ba2d3b..c1fbf2fd6bb08 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -45,7 +45,10 @@ export class TitlebarPart extends Part implements ITitleService { readonly minimumWidth: number = 0; readonly maximumWidth: number = Number.POSITIVE_INFINITY; - get minimumHeight(): number { return 30 / (this.currentMenubarVisibility === 'hidden' || getZoomFactor() < 1 ? getZoomFactor() : 1); } + get minimumHeight(): number { + const ccPadding = this.isCommandCenterVisible ? 4 : 0; + return (30 + ccPadding) / (this.currentMenubarVisibility === 'hidden' || getZoomFactor() < 1 ? getZoomFactor() : 1); + } get maximumHeight(): number { return this.minimumHeight; } //#endregion diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 75a1a5fa6bd6d..f5962e8f33d72 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -37,7 +37,13 @@ export class TitlebarPart extends BrowserTitleBarPart { return 22; } - override get minimumHeight(): number { return isMacintosh ? this.getMacTitlebarSize() / getZoomFactor() : super.minimumHeight; } + override get minimumHeight(): number { + if (!isMacintosh) { + return super.minimumHeight; + } + const ccPadding = this.isCommandCenterVisible ? 4 : 0; + return (this.getMacTitlebarSize() + ccPadding) / getZoomFactor(); + } override get maximumHeight(): number { return this.minimumHeight; } protected override readonly environmentService: INativeWorkbenchEnvironmentService; From 0075b0156cf38d471c84497679fb6815f3cb1192 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 15:35:46 +0200 Subject: [PATCH 268/942] add missing CC rename --- src/vs/workbench/browser/layout.ts | 2 +- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 6 +++--- src/vs/workbench/services/title/common/titleService.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 43168e2f92731..9d7adb44001bd 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -269,7 +269,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // Title Menu changes - this._register(this.titleService.onDidChangeTitleMenuVisibility(() => this.layout())); + this._register(this.titleService.onDidChangeCommandCenterVisibility(() => this.layout())); // Theme changes this._register(this.themeService.onDidColorThemeChange(() => this.updateStyles())); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index c1fbf2fd6bb08..b69113cd97e67 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -56,8 +56,8 @@ export class TitlebarPart extends Part implements ITitleService { private _onMenubarVisibilityChange = this._register(new Emitter()); readonly onMenubarVisibilityChange = this._onMenubarVisibilityChange.event; - private readonly _onDidChangeTitleMenuVisibility = new Emitter(); - readonly onDidChangeTitleMenuVisibility: Event = this._onDidChangeTitleMenuVisibility.event; + private readonly _onDidChangeCommandCenterVisibility = new Emitter(); + readonly onDidChangeCommandCenterVisibility: Event = this._onDidChangeCommandCenterVisibility.event; protected rootContainer!: HTMLElement; protected windowControls: HTMLElement | undefined; @@ -143,7 +143,7 @@ export class TitlebarPart extends Part implements ITitleService { if (event.affectsConfiguration(TitlebarPart.configCommandCenter)) { this.updateTitle(); this.adjustTitleMarginToCenter(); - this._onDidChangeTitleMenuVisibility.fire(); + this._onDidChangeCommandCenterVisibility.fire(); } } diff --git a/src/vs/workbench/services/title/common/titleService.ts b/src/vs/workbench/services/title/common/titleService.ts index 92aca9f92cd5a..57b7ae7430ef2 100644 --- a/src/vs/workbench/services/title/common/titleService.ts +++ b/src/vs/workbench/services/title/common/titleService.ts @@ -31,7 +31,7 @@ export interface ITitleService { /** * An event when the title menu is enabled/disabled */ - readonly onDidChangeTitleMenuVisibility: Event; + readonly onDidChangeCommandCenterVisibility: Event; /** * Update some environmental title properties. From 67cc6771cc09766911ce6783045c3ee733a64feb Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 15:50:34 +0200 Subject: [PATCH 269/942] center align menubar menu buttons, don't assign fixed height to titlebar container --- src/vs/base/browser/ui/menu/menubar.css | 1 + src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/menu/menubar.css b/src/vs/base/browser/ui/menu/menubar.css index b9dd13780e9db..59b16df8d13b6 100644 --- a/src/vs/base/browser/ui/menu/menubar.css +++ b/src/vs/base/browser/ui/menu/menubar.css @@ -30,6 +30,7 @@ zoom: 1; white-space: nowrap; outline: 0; + align-self: center; } .menubar.compact { diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 77abc65361444..a43cd721f2f2e 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -41,7 +41,7 @@ .monaco-workbench.web .part.titlebar>.titlebar-container, .monaco-workbench.windows .part.titlebar>.titlebar-container, .monaco-workbench.linux .part.titlebar>.titlebar-container { - height: 30px; + /* height: 30px; */ line-height: 22px; justify-content: left; } From 903c5c6cc9a85744e16580695a0b4994431bd39c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 24 May 2022 15:50:36 +0200 Subject: [PATCH 270/942] - Remove Show Extension button (#150274) - Add Open Alternative Extension or Configure Settings button --- .../extensions/browser/extensionsActions.ts | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index ee17618c9582c..86f03bfe8fb6f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -65,6 +65,7 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { ViewContainerLocation } from 'vs/workbench/common/views'; import { flatten } from 'vs/base/common/arrays'; import { fromNow } from 'vs/base/common/date'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; export class PromptExtensionInstallFailureAction extends Action { @@ -250,6 +251,7 @@ export abstract class AbstractInstallAction extends ExtensionAction { @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, @ILabelService private readonly labelService: ILabelService, @IDialogService private readonly dialogService: IDialogService, + @IPreferencesService private readonly preferencesService: IPreferencesService, ) { super(id, localize('install', "Install"), cssClass, false); this.update(); @@ -276,20 +278,33 @@ export abstract class AbstractInstallAction extends ExtensionAction { } if (this.extension.deprecationInfo) { + let detail = localize('deprecated message', "This extension is no longer being maintained and is deprecated."); + let action: () => Promise = async () => undefined; + const buttons = [ + localize('install anyway', "Install Anyway"), + localize('cancel', "Cancel"), + ]; + + if (this.extension.deprecationInfo.extension) { + detail = localize('deprecated with alternate extension message', "This extension has been deprecated. Use {0} instead.", this.extension.deprecationInfo.extension.displayName); + buttons.splice(1, 0, localize('Show alternate extension', "Open {0}", this.extension.deprecationInfo.extension.displayName)); + const alternateExtension = this.extension.deprecationInfo.extension; + action = () => this.extensionsWorkbenchService.getExtensions([{ id: alternateExtension.id, preRelease: alternateExtension.preRelease }], CancellationToken.None) + .then(([extension]) => this.extensionsWorkbenchService.open(extension)); + } else if (this.extension.deprecationInfo.settings) { + detail = localize('deprecated with alternate settings message', "This extension is deprecated and has become a native feature in VS Code."); + buttons.splice(1, 0, localize('configure in settings', "Configure Settings")); + const settings = this.extension.deprecationInfo.settings; + action = () => this.preferencesService.openSettings({ query: settings.map(setting => `@id:${setting}`).join(' ') }); + } + const result = await this.dialogService.show( Severity.Warning, localize('install confirmation', "Are you sure you want to install '{0}'?", this.extension.displayName), - [ - localize('install anyway', "Install Anyway"), - localize('open extension', "Open Extension"), - localize('cancel', "Cancel"), - ], - { - detail: localize('deprecated message', "This extension is no longer being maintained and is deprecated."), - cancelId: 2, - }); + buttons, + { detail, cancelId: buttons.length - 1 }); if (result.choice === 1) { - return this.extensionsWorkbenchService.open(this.extension, { showPreReleaseVersion: this.installPreReleaseVersion }); + return action(); } if (result.choice === 2) { return; @@ -396,12 +411,13 @@ export class InstallAction extends AbstractInstallAction { @IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService, @ILabelService labelService: ILabelService, @IDialogService dialogService: IDialogService, + @IPreferencesService preferencesService: IPreferencesService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IWorkbenchExtensionManagementService private readonly workbenchExtensioManagementService: IWorkbenchExtensionManagementService, @IUserDataSyncEnablementService protected readonly userDataSyncEnablementService: IUserDataSyncEnablementService, ) { super(`extensions.install`, installPreReleaseVersion, InstallAction.Class, - extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService, dialogService); + extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService, dialogService, preferencesService); this.updateLabel(); this._register(labelService.onDidChangeFormatters(() => this.updateLabel(), this)); this._register(Event.any(userDataSyncEnablementService.onDidChangeEnablement, @@ -462,11 +478,12 @@ export class InstallAndSyncAction extends AbstractInstallAction { @IWorkbenchThemeService workbenchThemeService: IWorkbenchThemeService, @ILabelService labelService: ILabelService, @IDialogService dialogService: IDialogService, + @IPreferencesService preferencesService: IPreferencesService, @IProductService productService: IProductService, @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService, ) { super('extensions.installAndSync', installPreReleaseVersion, AbstractInstallAction.Class, - extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService, dialogService); + extensionsWorkbenchService, instantiationService, runtimeExtensionService, workbenchThemeService, labelService, dialogService, preferencesService); this.tooltip = localize({ key: 'install everywhere tooltip', comment: ['Placeholder is the name of the product. Eg: Visual Studio Code or Visual Studio Code - Insiders'] }, "Install this extension in all your synced {0} instances", productService.nameLong); this._register(Event.any(userDataSyncEnablementService.onDidChangeEnablement, Event.filter(userDataSyncEnablementService.onDidChangeResourceEnablement, e => e[0] === SyncResource.Extensions))(() => this.update())); From 339c3a9b60594411c62ea3ed9123a0b26cf722e9 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 24 May 2022 15:51:23 +0200 Subject: [PATCH 271/942] Fixes #150176: Extract code related to encoded token attributes to a separate file (#150275) --- .../browser/controller/textAreaHandler.ts | 3 +- .../browser/viewParts/minimap/minimap.ts | 2 +- .../editor/common/encodedTokenAttributes.ts | 193 +++++++++++++++++ src/vs/editor/common/languages.ts | 197 +----------------- .../common/languages/languageConfiguration.ts | 2 +- .../editor/common/languages/nullTokenize.ts | 3 +- src/vs/editor/common/languages/supports.ts | 2 +- .../common/languages/supports/tokenization.ts | 2 +- .../common/languages/textToHtmlTokenizer.ts | 3 +- .../bracketPairsTree/tokenizer.ts | 2 +- src/vs/editor/common/model/textModelTokens.ts | 3 +- .../common/model/tokenizationTextModelPart.ts | 2 +- .../common/services/languagesRegistry.ts | 3 +- .../services/semanticTokensProviderStyling.ts | 3 +- src/vs/editor/common/tokenizationRegistry.ts | 3 +- .../common/tokenizationTextModelPart.ts | 2 +- .../common/tokens/contiguousTokensStore.ts | 3 +- src/vs/editor/common/tokens/lineTokens.ts | 3 +- .../editor/common/tokens/sparseTokensStore.ts | 3 +- .../viewModel/minimapTokensColorTracker.ts | 3 +- .../common/viewModel/viewModelDecorations.ts | 2 +- .../editor/common/viewModel/viewModelImpl.ts | 3 +- .../test/browser/lineCommentCommand.test.ts | 3 +- .../indentation/browser/indentation.ts | 3 +- .../editor/contrib/suggest/browser/suggest.ts | 7 +- .../suggest/test/browser/suggestModel.test.ts | 3 +- src/vs/editor/standalone/browser/colorizer.ts | 3 +- .../browser/inspectTokens/inspectTokens.ts | 3 +- .../browser/standaloneThemeService.ts | 3 +- .../standalone/common/monarch/monarchLexer.ts | 5 +- .../test/browser/standaloneLanguages.test.ts | 3 +- .../test/browser/controller/cursor.test.ts | 3 +- .../viewModel/modelLineProjection.test.ts | 3 +- .../test/common/core/lineTokens.test.ts | 2 +- .../editor/test/common/core/testLineToken.ts | 2 +- .../bracketPairColorizer/tokenizer.test.ts | 3 +- .../test/common/model/model.line.test.ts | 2 +- src/vs/editor/test/common/model/model.test.ts | 3 +- .../common/model/textModelWithTokens.test.ts | 3 +- .../test/common/model/tokensStore.test.ts | 2 +- .../modes/languageConfiguration.test.ts | 2 +- .../modes/supports/characterPair.test.ts | 2 +- .../modes/supports/electricCharacter.test.ts | 2 +- .../modes/supports/tokenization.test.ts | 2 +- .../common/modes/textToHtmlTokenizer.test.ts | 3 +- src/vs/editor/test/common/modesTestUtils.ts | 2 +- .../semanticTokensProviderStyling.test.ts | 2 +- .../viewLayout/viewLineRenderer.test.ts | 2 +- .../node/classification/typescript.test.ts | 2 +- .../api/browser/mainThreadLanguages.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 3 +- .../api/common/extHostTypeConverters.ts | 11 +- .../inspectEditorTokens.ts | 3 +- .../debug/browser/debugEditorContribution.ts | 3 +- .../view/cellParts/cellDragRenderer.ts | 5 +- .../browser/themes.test.contribution.ts | 3 +- .../browser/abstractTextMateService.ts | 3 +- .../textMate/browser/textMateWorker.ts | 2 +- .../textMate/common/TMScopeRegistry.ts | 2 +- .../textMate/common/TMTokenization.ts | 3 +- 60 files changed, 295 insertions(+), 262 deletions(-) create mode 100644 src/vs/editor/common/encodedTokenAttributes.ts diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index a8a0a6d985e3a..ffbe3b5588267 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -31,7 +31,8 @@ import * as viewEvents from 'vs/editor/common/viewEvents'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { IEditorAriaOptions } from 'vs/editor/browser/editorBrowser'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; -import { ColorId, ITokenPresentation, TokenizationRegistry } from 'vs/editor/common/languages'; +import { TokenizationRegistry } from 'vs/editor/common/languages'; +import { ColorId, ITokenPresentation } from 'vs/editor/common/encodedTokenAttributes'; import { Color } from 'vs/base/common/color'; export interface IVisibleRangeProvider { diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 212bd90a9290a..2edb031b356f2 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -18,7 +18,7 @@ import { Range } from 'vs/editor/common/core/range'; import { RGBA8 } from 'vs/editor/common/core/rgba'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { IEditorConfiguration } from 'vs/editor/common/config/editorConfiguration'; -import { ColorId } from 'vs/editor/common/languages'; +import { ColorId } from 'vs/editor/common/encodedTokenAttributes'; import { MinimapCharRenderer } from 'vs/editor/browser/viewParts/minimap/minimapCharRenderer'; import { Constants } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet'; import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTokensColorTracker'; diff --git a/src/vs/editor/common/encodedTokenAttributes.ts b/src/vs/editor/common/encodedTokenAttributes.ts new file mode 100644 index 0000000000000..c0f2fad70f133 --- /dev/null +++ b/src/vs/editor/common/encodedTokenAttributes.ts @@ -0,0 +1,193 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Open ended enum at runtime + */ +export const enum LanguageId { + Null = 0, + PlainText = 1 +} + +/** + * A font style. Values are 2^x such that a bit mask can be used. + */ +export const enum FontStyle { + NotSet = -1, + None = 0, + Italic = 1, + Bold = 2, + Underline = 4, + Strikethrough = 8, +} + +/** + * Open ended enum at runtime + */ +export const enum ColorId { + None = 0, + DefaultForeground = 1, + DefaultBackground = 2 +} + +/** + * A standard token type. + */ +export const enum StandardTokenType { + Other = 0, + Comment = 1, + String = 2, + RegEx = 3 +} + +/** + * Helpers to manage the "collapsed" metadata of an entire StackElement stack. + * The following assumptions have been made: + * - languageId < 256 => needs 8 bits + * - unique color count < 512 => needs 9 bits + * + * The binary format is: + * - ------------------------------------------- + * 3322 2222 2222 1111 1111 1100 0000 0000 + * 1098 7654 3210 9876 5432 1098 7654 3210 + * - ------------------------------------------- + * xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx + * bbbb bbbb ffff ffff fFFF FBTT LLLL LLLL + * - ------------------------------------------- + * - L = LanguageId (8 bits) + * - T = StandardTokenType (2 bits) + * - B = Balanced bracket (1 bit) + * - F = FontStyle (4 bits) + * - f = foreground color (9 bits) + * - b = background color (9 bits) + * + */ +export const enum MetadataConsts { + LANGUAGEID_MASK = 0b00000000000000000000000011111111, + TOKEN_TYPE_MASK = 0b00000000000000000000001100000000, + BALANCED_BRACKETS_MASK = 0b00000000000000000000010000000000, + FONT_STYLE_MASK = 0b00000000000000000111100000000000, + FOREGROUND_MASK = 0b00000000111111111000000000000000, + BACKGROUND_MASK = 0b11111111000000000000000000000000, + + ITALIC_MASK = 0b00000000000000000000100000000000, + BOLD_MASK = 0b00000000000000000001000000000000, + UNDERLINE_MASK = 0b00000000000000000010000000000000, + STRIKETHROUGH_MASK = 0b00000000000000000100000000000000, + + // Semantic tokens cannot set the language id, so we can + // use the first 8 bits for control purposes + SEMANTIC_USE_ITALIC = 0b00000000000000000000000000000001, + SEMANTIC_USE_BOLD = 0b00000000000000000000000000000010, + SEMANTIC_USE_UNDERLINE = 0b00000000000000000000000000000100, + SEMANTIC_USE_STRIKETHROUGH = 0b00000000000000000000000000001000, + SEMANTIC_USE_FOREGROUND = 0b00000000000000000000000000010000, + SEMANTIC_USE_BACKGROUND = 0b00000000000000000000000000100000, + + LANGUAGEID_OFFSET = 0, + TOKEN_TYPE_OFFSET = 8, + BALANCED_BRACKETS_OFFSET = 10, + FONT_STYLE_OFFSET = 11, + FOREGROUND_OFFSET = 15, + BACKGROUND_OFFSET = 24 +} + +/** + */ +export class TokenMetadata { + + public static getLanguageId(metadata: number): LanguageId { + return (metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET; + } + + public static getTokenType(metadata: number): StandardTokenType { + return (metadata & MetadataConsts.TOKEN_TYPE_MASK) >>> MetadataConsts.TOKEN_TYPE_OFFSET; + } + + public static containsBalancedBrackets(metadata: number): boolean { + return (metadata & MetadataConsts.BALANCED_BRACKETS_MASK) !== 0; + } + + public static getFontStyle(metadata: number): FontStyle { + return (metadata & MetadataConsts.FONT_STYLE_MASK) >>> MetadataConsts.FONT_STYLE_OFFSET; + } + + public static getForeground(metadata: number): ColorId { + return (metadata & MetadataConsts.FOREGROUND_MASK) >>> MetadataConsts.FOREGROUND_OFFSET; + } + + public static getBackground(metadata: number): ColorId { + return (metadata & MetadataConsts.BACKGROUND_MASK) >>> MetadataConsts.BACKGROUND_OFFSET; + } + + public static getClassNameFromMetadata(metadata: number): string { + const foreground = this.getForeground(metadata); + let className = 'mtk' + foreground; + + const fontStyle = this.getFontStyle(metadata); + if (fontStyle & FontStyle.Italic) { + className += ' mtki'; + } + if (fontStyle & FontStyle.Bold) { + className += ' mtkb'; + } + if (fontStyle & FontStyle.Underline) { + className += ' mtku'; + } + if (fontStyle & FontStyle.Strikethrough) { + className += ' mtks'; + } + + return className; + } + + public static getInlineStyleFromMetadata(metadata: number, colorMap: string[]): string { + const foreground = this.getForeground(metadata); + const fontStyle = this.getFontStyle(metadata); + + let result = `color: ${colorMap[foreground]};`; + if (fontStyle & FontStyle.Italic) { + result += 'font-style: italic;'; + } + if (fontStyle & FontStyle.Bold) { + result += 'font-weight: bold;'; + } + let textDecoration = ''; + if (fontStyle & FontStyle.Underline) { + textDecoration += ' underline'; + } + if (fontStyle & FontStyle.Strikethrough) { + textDecoration += ' line-through'; + } + if (textDecoration) { + result += `text-decoration:${textDecoration};`; + + } + return result; + } + + public static getPresentationFromMetadata(metadata: number): ITokenPresentation { + const foreground = this.getForeground(metadata); + const fontStyle = this.getFontStyle(metadata); + + return { + foreground: foreground, + italic: Boolean(fontStyle & FontStyle.Italic), + bold: Boolean(fontStyle & FontStyle.Bold), + underline: Boolean(fontStyle & FontStyle.Underline), + strikethrough: Boolean(fontStyle & FontStyle.Strikethrough), + }; + } +} + +/** + */ +export interface ITokenPresentation { + foreground: ColorId; + italic: boolean; + bold: boolean; + underline: boolean; + strikethrough: boolean; +} diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 1c366d79a5abe..647dc1fa131de 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -15,208 +15,13 @@ import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; +import { LanguageId } from 'vs/editor/common/encodedTokenAttributes'; import * as model from 'vs/editor/common/model'; import { TokenizationRegistry as TokenizationRegistryImpl } from 'vs/editor/common/tokenizationRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IMarkerData } from 'vs/platform/markers/common/markers'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -/** - * Open ended enum at runtime - * @internal - */ -export const enum LanguageId { - Null = 0, - PlainText = 1 -} - -/** - * A font style. Values are 2^x such that a bit mask can be used. - * @internal - */ -export const enum FontStyle { - NotSet = -1, - None = 0, - Italic = 1, - Bold = 2, - Underline = 4, - Strikethrough = 8, -} - -/** - * Open ended enum at runtime - * @internal - */ -export const enum ColorId { - None = 0, - DefaultForeground = 1, - DefaultBackground = 2 -} - -/** - * A standard token type. - * @internal - */ -export const enum StandardTokenType { - Other = 0, - Comment = 1, - String = 2, - RegEx = 3 -} - -/** - * Helpers to manage the "collapsed" metadata of an entire StackElement stack. - * The following assumptions have been made: - * - languageId < 256 => needs 8 bits - * - unique color count < 512 => needs 9 bits - * - * The binary format is: - * - ------------------------------------------- - * 3322 2222 2222 1111 1111 1100 0000 0000 - * 1098 7654 3210 9876 5432 1098 7654 3210 - * - ------------------------------------------- - * xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - * bbbb bbbb ffff ffff fFFF FBTT LLLL LLLL - * - ------------------------------------------- - * - L = LanguageId (8 bits) - * - T = StandardTokenType (2 bits) - * - B = Balanced bracket (1 bit) - * - F = FontStyle (4 bits) - * - f = foreground color (9 bits) - * - b = background color (9 bits) - * - * @internal - */ -export const enum MetadataConsts { - LANGUAGEID_MASK = 0b00000000000000000000000011111111, - TOKEN_TYPE_MASK = 0b00000000000000000000001100000000, - BALANCED_BRACKETS_MASK = 0b00000000000000000000010000000000, - FONT_STYLE_MASK = 0b00000000000000000111100000000000, - FOREGROUND_MASK = 0b00000000111111111000000000000000, - BACKGROUND_MASK = 0b11111111000000000000000000000000, - - ITALIC_MASK = 0b00000000000000000000100000000000, - BOLD_MASK = 0b00000000000000000001000000000000, - UNDERLINE_MASK = 0b00000000000000000010000000000000, - STRIKETHROUGH_MASK = 0b00000000000000000100000000000000, - - // Semantic tokens cannot set the language id, so we can - // use the first 8 bits for control purposes - SEMANTIC_USE_ITALIC = 0b00000000000000000000000000000001, - SEMANTIC_USE_BOLD = 0b00000000000000000000000000000010, - SEMANTIC_USE_UNDERLINE = 0b00000000000000000000000000000100, - SEMANTIC_USE_STRIKETHROUGH = 0b00000000000000000000000000001000, - SEMANTIC_USE_FOREGROUND = 0b00000000000000000000000000010000, - SEMANTIC_USE_BACKGROUND = 0b00000000000000000000000000100000, - - LANGUAGEID_OFFSET = 0, - TOKEN_TYPE_OFFSET = 8, - BALANCED_BRACKETS_OFFSET = 10, - FONT_STYLE_OFFSET = 11, - FOREGROUND_OFFSET = 15, - BACKGROUND_OFFSET = 24 -} - -/** - * @internal - */ -export class TokenMetadata { - - public static getLanguageId(metadata: number): LanguageId { - return (metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET; - } - - public static getTokenType(metadata: number): StandardTokenType { - return (metadata & MetadataConsts.TOKEN_TYPE_MASK) >>> MetadataConsts.TOKEN_TYPE_OFFSET; - } - - public static containsBalancedBrackets(metadata: number): boolean { - return (metadata & MetadataConsts.BALANCED_BRACKETS_MASK) !== 0; - } - - public static getFontStyle(metadata: number): FontStyle { - return (metadata & MetadataConsts.FONT_STYLE_MASK) >>> MetadataConsts.FONT_STYLE_OFFSET; - } - - public static getForeground(metadata: number): ColorId { - return (metadata & MetadataConsts.FOREGROUND_MASK) >>> MetadataConsts.FOREGROUND_OFFSET; - } - - public static getBackground(metadata: number): ColorId { - return (metadata & MetadataConsts.BACKGROUND_MASK) >>> MetadataConsts.BACKGROUND_OFFSET; - } - - public static getClassNameFromMetadata(metadata: number): string { - const foreground = this.getForeground(metadata); - let className = 'mtk' + foreground; - - const fontStyle = this.getFontStyle(metadata); - if (fontStyle & FontStyle.Italic) { - className += ' mtki'; - } - if (fontStyle & FontStyle.Bold) { - className += ' mtkb'; - } - if (fontStyle & FontStyle.Underline) { - className += ' mtku'; - } - if (fontStyle & FontStyle.Strikethrough) { - className += ' mtks'; - } - - return className; - } - - public static getInlineStyleFromMetadata(metadata: number, colorMap: string[]): string { - const foreground = this.getForeground(metadata); - const fontStyle = this.getFontStyle(metadata); - - let result = `color: ${colorMap[foreground]};`; - if (fontStyle & FontStyle.Italic) { - result += 'font-style: italic;'; - } - if (fontStyle & FontStyle.Bold) { - result += 'font-weight: bold;'; - } - let textDecoration = ''; - if (fontStyle & FontStyle.Underline) { - textDecoration += ' underline'; - } - if (fontStyle & FontStyle.Strikethrough) { - textDecoration += ' line-through'; - } - if (textDecoration) { - result += `text-decoration:${textDecoration};`; - - } - return result; - } - - public static getPresentationFromMetadata(metadata: number): ITokenPresentation { - const foreground = this.getForeground(metadata); - const fontStyle = this.getFontStyle(metadata); - - return { - foreground: foreground, - italic: Boolean(fontStyle & FontStyle.Italic), - bold: Boolean(fontStyle & FontStyle.Bold), - underline: Boolean(fontStyle & FontStyle.Underline), - strikethrough: Boolean(fontStyle & FontStyle.Strikethrough), - }; - } -} - -/** - * @internal - */ -export interface ITokenPresentation { - foreground: ColorId; - italic: boolean; - bold: boolean; - underline: boolean; - strikethrough: boolean; -} - /** * @internal */ diff --git a/src/vs/editor/common/languages/languageConfiguration.ts b/src/vs/editor/common/languages/languageConfiguration.ts index 1f4a458bc08c7..69b852e407c4f 100644 --- a/src/vs/editor/common/languages/languageConfiguration.ts +++ b/src/vs/editor/common/languages/languageConfiguration.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from 'vs/base/common/charCode'; -import { StandardTokenType } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { ScopedLineTokens } from 'vs/editor/common/languages/supports'; /** diff --git a/src/vs/editor/common/languages/nullTokenize.ts b/src/vs/editor/common/languages/nullTokenize.ts index ce7b605783d00..2bf77a6acd81c 100644 --- a/src/vs/editor/common/languages/nullTokenize.ts +++ b/src/vs/editor/common/languages/nullTokenize.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Token, TokenizationResult, EncodedTokenizationResult, ColorId, FontStyle, IState, LanguageId, MetadataConsts, StandardTokenType } from 'vs/editor/common/languages'; +import { Token, TokenizationResult, EncodedTokenizationResult, IState } from 'vs/editor/common/languages'; +import { LanguageId, FontStyle, ColorId, StandardTokenType, MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; export const NullState: IState = new class implements IState { public clone(): IState { diff --git a/src/vs/editor/common/languages/supports.ts b/src/vs/editor/common/languages/supports.ts index bef3fc120a92c..8fdfa17bb5132 100644 --- a/src/vs/editor/common/languages/supports.ts +++ b/src/vs/editor/common/languages/supports.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; -import { StandardTokenType } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; export function createScopedLineTokens(context: LineTokens, offset: number): ScopedLineTokens { const tokenCount = context.getCount(); diff --git a/src/vs/editor/common/languages/supports/tokenization.ts b/src/vs/editor/common/languages/supports/tokenization.ts index b3f686300dda7..6f7f777dcdbd2 100644 --- a/src/vs/editor/common/languages/supports/tokenization.ts +++ b/src/vs/editor/common/languages/supports/tokenization.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Color } from 'vs/base/common/color'; -import { ColorId, FontStyle, LanguageId, MetadataConsts, StandardTokenType } from 'vs/editor/common/languages'; +import { LanguageId, FontStyle, ColorId, StandardTokenType, MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; export interface ITokenThemeRule { token: string; diff --git a/src/vs/editor/common/languages/textToHtmlTokenizer.ts b/src/vs/editor/common/languages/textToHtmlTokenizer.ts index 64bf71febb11c..66651f254107f 100644 --- a/src/vs/editor/common/languages/textToHtmlTokenizer.ts +++ b/src/vs/editor/common/languages/textToHtmlTokenizer.ts @@ -6,7 +6,8 @@ import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { IViewLineTokens, LineTokens } from 'vs/editor/common/tokens/lineTokens'; -import { ILanguageIdCodec, IState, ITokenizationSupport, LanguageId, TokenizationRegistry } from 'vs/editor/common/languages'; +import { ILanguageIdCodec, IState, ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/languages'; +import { LanguageId } from 'vs/editor/common/encodedTokenAttributes'; import { NullState, nullTokenizeEncoded } from 'vs/editor/common/languages/nullTokenize'; import { ILanguageService } from 'vs/editor/common/languages/language'; diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts b/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts index c65a57b0175b7..0e7381b767787 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts +++ b/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { NotSupportedError } from 'vs/base/common/errors'; -import { StandardTokenType, TokenMetadata } from 'vs/editor/common/languages'; +import { StandardTokenType, TokenMetadata } from 'vs/editor/common/encodedTokenAttributes'; import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens'; import { BracketAstNode, TextAstNode } from './ast'; import { BracketTokens, LanguageAgnosticBracketTokens } from './brackets'; diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index a0aaf37769014..b504fc1436659 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -8,7 +8,8 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; import { Position } from 'vs/editor/common/core/position'; import { IRange } from 'vs/editor/common/core/range'; -import { EncodedTokenizationResult, ILanguageIdCodec, IState, ITokenizationSupport, StandardTokenType, TokenizationRegistry } from 'vs/editor/common/languages'; +import { EncodedTokenizationResult, ILanguageIdCodec, IState, ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { nullTokenizeEncoded } from 'vs/editor/common/languages/nullTokenize'; import { TextModel } from 'vs/editor/common/model/textModel'; import { Disposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/editor/common/model/tokenizationTextModelPart.ts b/src/vs/editor/common/model/tokenizationTextModelPart.ts index df1000db4cdfe..cc2cf81f02bbd 100644 --- a/src/vs/editor/common/model/tokenizationTextModelPart.ts +++ b/src/vs/editor/common/model/tokenizationTextModelPart.ts @@ -9,7 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { getWordAtText, IWordAtPosition } from 'vs/editor/common/core/wordHelper'; -import { StandardTokenType } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { ILanguageConfigurationService, ResolvedLanguageConfiguration } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { TextModel } from 'vs/editor/common/model/textModel'; diff --git a/src/vs/editor/common/services/languagesRegistry.ts b/src/vs/editor/common/services/languagesRegistry.ts index dba3c0b9bb655..cf7a5f5ea35a8 100644 --- a/src/vs/editor/common/services/languagesRegistry.ts +++ b/src/vs/editor/common/services/languagesRegistry.ts @@ -9,7 +9,8 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { compareIgnoreCase, regExpLeadsToEndlessLoop } from 'vs/base/common/strings'; import { clearPlatformLanguageAssociations, getLanguageIds, registerPlatformLanguageAssociation } from 'vs/editor/common/services/languagesAssociations'; import { URI } from 'vs/base/common/uri'; -import { ILanguageIdCodec, LanguageId } from 'vs/editor/common/languages'; +import { ILanguageIdCodec } from 'vs/editor/common/languages'; +import { LanguageId } from 'vs/editor/common/encodedTokenAttributes'; import { ModesRegistry, PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; import { ILanguageExtensionPoint, ILanguageNameIdPair, ILanguageIcon } from 'vs/editor/common/languages/language'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; diff --git a/src/vs/editor/common/services/semanticTokensProviderStyling.ts b/src/vs/editor/common/services/semanticTokensProviderStyling.ts index e804a241d4eab..019c5d017267b 100644 --- a/src/vs/editor/common/services/semanticTokensProviderStyling.ts +++ b/src/vs/editor/common/services/semanticTokensProviderStyling.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SemanticTokensLegend, TokenMetadata, FontStyle, MetadataConsts, SemanticTokens } from 'vs/editor/common/languages'; +import { SemanticTokensLegend, SemanticTokens } from 'vs/editor/common/languages'; +import { FontStyle, MetadataConsts, TokenMetadata } from 'vs/editor/common/encodedTokenAttributes'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { SparseMultilineTokens } from 'vs/editor/common/tokens/sparseMultilineTokens'; diff --git a/src/vs/editor/common/tokenizationRegistry.ts b/src/vs/editor/common/tokenizationRegistry.ts index 223eb58996b0b..4f1876bbb9a69 100644 --- a/src/vs/editor/common/tokenizationRegistry.ts +++ b/src/vs/editor/common/tokenizationRegistry.ts @@ -6,7 +6,8 @@ import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { ColorId, ITokenizationRegistry, ITokenizationSupport, ITokenizationSupportChangedEvent, ITokenizationSupportFactory } from 'vs/editor/common/languages'; +import { ITokenizationRegistry, ITokenizationSupport, ITokenizationSupportChangedEvent, ITokenizationSupportFactory } from 'vs/editor/common/languages'; +import { ColorId } from 'vs/editor/common/encodedTokenAttributes'; export class TokenizationRegistry implements ITokenizationRegistry { diff --git a/src/vs/editor/common/tokenizationTextModelPart.ts b/src/vs/editor/common/tokenizationTextModelPart.ts index b68ce485850eb..6c8e7c322ed3a 100644 --- a/src/vs/editor/common/tokenizationTextModelPart.ts +++ b/src/vs/editor/common/tokenizationTextModelPart.ts @@ -6,7 +6,7 @@ import { Event } from 'vs/base/common/event'; import { IPosition } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { StandardTokenType } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { ContiguousMultilineTokens } from 'vs/editor/common/tokens/contiguousMultilineTokens'; import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; import { SparseMultilineTokens } from 'vs/editor/common/tokens/sparseMultilineTokens'; diff --git a/src/vs/editor/common/tokens/contiguousTokensStore.ts b/src/vs/editor/common/tokens/contiguousTokensStore.ts index b1aac9f82327f..aa2c14214913f 100644 --- a/src/vs/editor/common/tokens/contiguousTokensStore.ts +++ b/src/vs/editor/common/tokens/contiguousTokensStore.ts @@ -8,7 +8,8 @@ import { Position } from 'vs/editor/common/core/position'; import { IRange } from 'vs/editor/common/core/range'; import { ContiguousTokensEditing, EMPTY_LINE_TOKENS, toUint32Array } from 'vs/editor/common/tokens/contiguousTokensEditing'; import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; -import { ColorId, FontStyle, ILanguageIdCodec, LanguageId, MetadataConsts, StandardTokenType, TokenMetadata } from 'vs/editor/common/languages'; +import { ILanguageIdCodec } from 'vs/editor/common/languages'; +import { LanguageId, FontStyle, ColorId, StandardTokenType, MetadataConsts, TokenMetadata } from 'vs/editor/common/encodedTokenAttributes'; /** * Represents contiguous tokens in a text model. diff --git a/src/vs/editor/common/tokens/lineTokens.ts b/src/vs/editor/common/tokens/lineTokens.ts index b950acf47685d..bdcda7cb180c2 100644 --- a/src/vs/editor/common/tokens/lineTokens.ts +++ b/src/vs/editor/common/tokens/lineTokens.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ColorId, FontStyle, ILanguageIdCodec, ITokenPresentation, MetadataConsts, StandardTokenType, TokenMetadata } from 'vs/editor/common/languages'; +import { ILanguageIdCodec } from 'vs/editor/common/languages'; +import { FontStyle, ColorId, StandardTokenType, MetadataConsts, TokenMetadata, ITokenPresentation } from 'vs/editor/common/encodedTokenAttributes'; export interface IViewLineTokens { equals(other: IViewLineTokens): boolean; diff --git a/src/vs/editor/common/tokens/sparseTokensStore.ts b/src/vs/editor/common/tokens/sparseTokensStore.ts index 387adb9b51800..90a1f3ab81840 100644 --- a/src/vs/editor/common/tokens/sparseTokensStore.ts +++ b/src/vs/editor/common/tokens/sparseTokensStore.ts @@ -7,7 +7,8 @@ import * as arrays from 'vs/base/common/arrays'; import { IRange, Range } from 'vs/editor/common/core/range'; import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; import { SparseMultilineTokens } from 'vs/editor/common/tokens/sparseMultilineTokens'; -import { ILanguageIdCodec, MetadataConsts } from 'vs/editor/common/languages'; +import { ILanguageIdCodec } from 'vs/editor/common/languages'; +import { MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; /** * Represents sparse tokens in a text model. diff --git a/src/vs/editor/common/viewModel/minimapTokensColorTracker.ts b/src/vs/editor/common/viewModel/minimapTokensColorTracker.ts index 16cc6154be9c7..a063d37a0ce06 100644 --- a/src/vs/editor/common/viewModel/minimapTokensColorTracker.ts +++ b/src/vs/editor/common/viewModel/minimapTokensColorTracker.ts @@ -6,7 +6,8 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, markAsSingleton } from 'vs/base/common/lifecycle'; import { RGBA8 } from 'vs/editor/common/core/rgba'; -import { ColorId, TokenizationRegistry } from 'vs/editor/common/languages'; +import { TokenizationRegistry } from 'vs/editor/common/languages'; +import { ColorId } from 'vs/editor/common/encodedTokenAttributes'; export class MinimapTokensColorTracker extends Disposable { private static _INSTANCE: MinimapTokensColorTracker | null = null; diff --git a/src/vs/editor/common/viewModel/viewModelDecorations.ts b/src/vs/editor/common/viewModel/viewModelDecorations.ts index e9db60465a0c7..909d9f19bc335 100644 --- a/src/vs/editor/common/viewModel/viewModelDecorations.ts +++ b/src/vs/editor/common/viewModel/viewModelDecorations.ts @@ -11,7 +11,7 @@ import { IModelDecoration, ITextModel, PositionAffinity } from 'vs/editor/common import { IViewModelLines } from 'vs/editor/common/viewModel/viewModelLines'; import { ICoordinatesConverter, InlineDecoration, InlineDecorationType, ViewModelDecoration } from 'vs/editor/common/viewModel'; import { filterValidationDecorations } from 'vs/editor/common/config/editorOptions'; -import { StandardTokenType } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; export interface IDecorationsViewportData { /** diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 9d2738c3b9f56..c55cf3d02d15e 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -23,7 +23,8 @@ import { EndOfLinePreference, ICursorStateComputer, IIdentifiedSingleEditOperati import { IActiveIndentGuideInfo, BracketGuideOptions, IndentGuide } from 'vs/editor/common/textModelGuides'; import { ModelDecorationMinimapOptions, ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel'; import * as textModelEvents from 'vs/editor/common/textModelEvents'; -import { ColorId, TokenizationRegistry } from 'vs/editor/common/languages'; +import { TokenizationRegistry } from 'vs/editor/common/languages'; +import { ColorId } from 'vs/editor/common/encodedTokenAttributes'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; import { tokenizeLineToHTML } from 'vs/editor/common/languages/textToHtmlTokenizer'; diff --git a/src/vs/editor/contrib/comment/test/browser/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/browser/lineCommentCommand.test.ts index a451d61ecacd9..7f9bea14db941 100644 --- a/src/vs/editor/contrib/comment/test/browser/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/browser/lineCommentCommand.test.ts @@ -7,7 +7,8 @@ import * as assert from 'assert'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Selection } from 'vs/editor/common/core/selection'; import { ICommand } from 'vs/editor/common/editorCommon'; -import { EncodedTokenizationResult, ColorId, IState, MetadataConsts, TokenizationRegistry } from 'vs/editor/common/languages'; +import { EncodedTokenizationResult, IState, TokenizationRegistry } from 'vs/editor/common/languages'; +import { ColorId, MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { CommentRule } from 'vs/editor/common/languages/languageConfiguration'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { NullState } from 'vs/editor/common/languages/nullTokenize'; diff --git a/src/vs/editor/contrib/indentation/browser/indentation.ts b/src/vs/editor/contrib/indentation/browser/indentation.ts index 2f7bcfd1eae45..8bc18b1d8c1ef 100644 --- a/src/vs/editor/contrib/indentation/browser/indentation.ts +++ b/src/vs/editor/contrib/indentation/browser/indentation.ts @@ -15,7 +15,8 @@ import { Selection } from 'vs/editor/common/core/selection'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder, IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; -import { StandardTokenType, TextEdit } from 'vs/editor/common/languages'; +import { TextEdit } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { IndentConsts } from 'vs/editor/common/languages/supports/indentRules'; import { IModelService } from 'vs/editor/common/services/model'; diff --git a/src/vs/editor/contrib/suggest/browser/suggest.ts b/src/vs/editor/contrib/suggest/browser/suggest.ts index 33b339aea720b..99a61d27d8af3 100644 --- a/src/vs/editor/contrib/suggest/browser/suggest.ts +++ b/src/vs/editor/contrib/suggest/browser/suggest.ts @@ -27,6 +27,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat import { historyNavigationVisible } from 'vs/platform/history/browser/contextScopedHistoryWidget'; import { InternalQuickSuggestionsOptions, QuickSuggestionsValue } from 'vs/editor/common/config/editorOptions'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; export const Context = { Visible: historyNavigationVisible, @@ -436,10 +437,10 @@ export abstract class QuickSuggestionsOptions { return config.other === 'on' && config.comments === 'on' && config.strings === 'on'; } - static valueFor(config: InternalQuickSuggestionsOptions, tokenType: languages.StandardTokenType): QuickSuggestionsValue { + static valueFor(config: InternalQuickSuggestionsOptions, tokenType: StandardTokenType): QuickSuggestionsValue { switch (tokenType) { - case languages.StandardTokenType.Comment: return config.comments; - case languages.StandardTokenType.String: return config.strings; + case StandardTokenType.Comment: return config.comments; + case StandardTokenType.String: return config.strings; default: return config.other; } } diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts index 140697730081d..0f5af3b638a6d 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts @@ -15,7 +15,8 @@ import { Selection } from 'vs/editor/common/core/selection'; import { Handler } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { CompletionItemKind, CompletionItemProvider, CompletionList, CompletionTriggerKind, EncodedTokenizationResult, IState, MetadataConsts, TokenizationRegistry } from 'vs/editor/common/languages'; +import { CompletionItemKind, CompletionItemProvider, CompletionList, CompletionTriggerKind, EncodedTokenizationResult, IState, TokenizationRegistry } from 'vs/editor/common/languages'; +import { MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { NullState } from 'vs/editor/common/languages/nullTokenize'; import { ILanguageService } from 'vs/editor/common/languages/language'; diff --git a/src/vs/editor/standalone/browser/colorizer.ts b/src/vs/editor/standalone/browser/colorizer.ts index 7adcf0cea76e0..2e38febe77428 100644 --- a/src/vs/editor/standalone/browser/colorizer.ts +++ b/src/vs/editor/standalone/browser/colorizer.ts @@ -6,7 +6,8 @@ import * as strings from 'vs/base/common/strings'; import { IViewLineTokens, LineTokens } from 'vs/editor/common/tokens/lineTokens'; import { ITextModel } from 'vs/editor/common/model'; -import { ColorId, FontStyle, ILanguageIdCodec, ITokenizationSupport, MetadataConsts, TokenizationRegistry } from 'vs/editor/common/languages'; +import { ILanguageIdCodec, ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/languages'; +import { FontStyle, ColorId, MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { RenderLineInput, renderViewLine2 as renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { ViewLineRenderingData } from 'vs/editor/common/viewModel'; diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts index 8ed87552447c7..6ac269a0f8ce7 100644 --- a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts @@ -14,7 +14,8 @@ import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorCon import { Position } from 'vs/editor/common/core/position'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { FontStyle, IState, ITokenizationSupport, StandardTokenType, TokenMetadata, TokenizationRegistry, ILanguageIdCodec, Token } from 'vs/editor/common/languages'; +import { IState, ITokenizationSupport, TokenizationRegistry, ILanguageIdCodec, Token } from 'vs/editor/common/languages'; +import { FontStyle, StandardTokenType, TokenMetadata } from 'vs/editor/common/encodedTokenAttributes'; import { NullState, nullTokenize, nullTokenizeEncoded } from 'vs/editor/common/languages/nullTokenize'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneTheme'; diff --git a/src/vs/editor/standalone/browser/standaloneThemeService.ts b/src/vs/editor/standalone/browser/standaloneThemeService.ts index cafca9f55a785..e6019720020cd 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeService.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeService.ts @@ -7,7 +7,8 @@ import * as dom from 'vs/base/browser/dom'; import { addMatchMediaChangeListener } from 'vs/base/browser/browser'; import { Color } from 'vs/base/common/color'; import { Emitter } from 'vs/base/common/event'; -import { FontStyle, TokenizationRegistry, TokenMetadata } from 'vs/editor/common/languages'; +import { TokenizationRegistry } from 'vs/editor/common/languages'; +import { FontStyle, TokenMetadata } from 'vs/editor/common/encodedTokenAttributes'; import { ITokenThemeRule, TokenTheme, generateTokensCSSForColorMap } from 'vs/editor/common/languages/supports/tokenization'; import { BuiltinTheme, IStandaloneTheme, IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneTheme'; import { hc_black, hc_light, vs, vs_dark } from 'vs/editor/standalone/common/themes'; diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts index bdc490fdb41de..7ef5b725f4891 100644 --- a/src/vs/editor/standalone/common/monarch/monarchLexer.ts +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -15,6 +15,7 @@ import { TokenTheme } from 'vs/editor/common/languages/supports/tokenization'; import { ILanguageService } from 'vs/editor/common/languages/language'; import * as monarchCommon from 'vs/editor/standalone/common/monarch/monarchCommon'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneTheme'; +import { LanguageId } from 'vs/editor/common/encodedTokenAttributes'; const CACHE_STACK_DEPTH = 5; @@ -296,7 +297,7 @@ class MonarchModernTokensCollector implements IMonarchTokensCollector { private readonly _theme: TokenTheme; private _prependTokens: Uint32Array | null; private _tokens: number[]; - private _currentLanguageId: languages.LanguageId; + private _currentLanguageId: LanguageId; private _lastTokenMetadata: number; constructor(languageService: ILanguageService, theme: TokenTheme) { @@ -304,7 +305,7 @@ class MonarchModernTokensCollector implements IMonarchTokensCollector { this._theme = theme; this._prependTokens = null; this._tokens = []; - this._currentLanguageId = languages.LanguageId.Null; + this._currentLanguageId = LanguageId.Null; this._lastTokenMetadata = 0; } diff --git a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts index 50d960dd0fb81..dbb0ee675a8c3 100644 --- a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts +++ b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts @@ -7,7 +7,8 @@ import * as assert from 'assert'; import { Color } from 'vs/base/common/color'; import { Emitter } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { Token, IState, LanguageId, MetadataConsts } from 'vs/editor/common/languages'; +import { Token, IState } from 'vs/editor/common/languages'; +import { LanguageId, MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { TokenTheme } from 'vs/editor/common/languages/supports/tokenization'; import { LanguageService } from 'vs/editor/common/services/languageService'; import { ILineTokens, IToken, TokenizationSupportAdapter, TokensProvider } from 'vs/editor/standalone/browser/standaloneLanguages'; diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 745e7388b60c0..14348ef8d378b 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -13,7 +13,8 @@ import { Selection } from 'vs/editor/common/core/selection'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference, EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { EncodedTokenizationResult, IState, ITokenizationSupport, MetadataConsts, StandardTokenType, TokenizationRegistry } from 'vs/editor/common/languages'; +import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/languages'; +import { StandardTokenType, MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { IndentAction, IndentationRule } from 'vs/editor/common/languages/languageConfiguration'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { NullState } from 'vs/editor/common/languages/nullTokenize'; diff --git a/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts b/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts index 2fe1d7799192b..790442706c49a 100644 --- a/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts +++ b/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts @@ -20,6 +20,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { ISimpleModel, IModelLineProjection, createModelLineProjection } from 'vs/editor/common/viewModel/modelLineProjection'; import { ModelLineProjectionData } from 'vs/editor/common/modelLineProjectionData'; +import { MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; suite('Editor ViewModel - SplitLinesCollection', () => { test('SplitLine', () => { @@ -342,7 +343,7 @@ suite('SplitLinesCollection', () => { for (let i = 0; i < tokens.length; i++) { result[2 * i] = tokens[i].startIndex; result[2 * i + 1] = ( - tokens[i].value << languages.MetadataConsts.FOREGROUND_OFFSET + tokens[i].value << MetadataConsts.FOREGROUND_OFFSET ); } return new languages.EncodedTokenizationResult(result, state); diff --git a/src/vs/editor/test/common/core/lineTokens.test.ts b/src/vs/editor/test/common/core/lineTokens.test.ts index f26d8930a5657..a6b8f18a88dfa 100644 --- a/src/vs/editor/test/common/core/lineTokens.test.ts +++ b/src/vs/editor/test/common/core/lineTokens.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { IViewLineTokens, LineTokens } from 'vs/editor/common/tokens/lineTokens'; -import { MetadataConsts } from 'vs/editor/common/languages'; +import { MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { LanguageIdCodec } from 'vs/editor/common/services/languagesRegistry'; suite('LineTokens', () => { diff --git a/src/vs/editor/test/common/core/testLineToken.ts b/src/vs/editor/test/common/core/testLineToken.ts index 0c6b236fbc799..ed9876b6a2818 100644 --- a/src/vs/editor/test/common/core/testLineToken.ts +++ b/src/vs/editor/test/common/core/testLineToken.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens'; -import { ColorId, ITokenPresentation, TokenMetadata } from 'vs/editor/common/languages'; +import { ColorId, TokenMetadata, ITokenPresentation } from 'vs/editor/common/encodedTokenAttributes'; /** * A token on a line. diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts index c88eca52ef2c5..2615475d9f0a0 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts @@ -10,7 +10,8 @@ import { Length, lengthAdd, lengthsToRange, lengthZero } from 'vs/editor/common/ import { DenseKeyProvider } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet'; import { TextBufferTokenizer, Token, Tokenizer, TokenKind } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { EncodedTokenizationResult, IState, ITokenizationSupport, LanguageId, MetadataConsts, StandardTokenType, TokenizationRegistry } from 'vs/editor/common/languages'; +import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/languages'; +import { LanguageId, StandardTokenType, MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { createModelServices, instantiateTextModel } from 'vs/editor/test/common/testTextModel'; diff --git a/src/vs/editor/test/common/model/model.line.test.ts b/src/vs/editor/test/common/model/model.line.test.ts index 7424e5f573ebc..583e4f5ab60d8 100644 --- a/src/vs/editor/test/common/model/model.line.test.ts +++ b/src/vs/editor/test/common/model/model.line.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; import { Range } from 'vs/editor/common/core/range'; import { computeIndentLevel } from 'vs/editor/common/model/utils'; -import { MetadataConsts } from 'vs/editor/common/languages'; +import { MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { TestLineToken, TestLineTokenFactory } from 'vs/editor/test/common/core/testLineToken'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; diff --git a/src/vs/editor/test/common/model/model.test.ts b/src/vs/editor/test/common/model/model.test.ts index 96fe698cb1b70..e9e4d63647d0e 100644 --- a/src/vs/editor/test/common/model/model.test.ts +++ b/src/vs/editor/test/common/model/model.test.ts @@ -10,7 +10,8 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { TextModel } from 'vs/editor/common/model/textModel'; import { InternalModelContentChangeEvent, ModelRawContentChangedEvent, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from 'vs/editor/common/textModelEvents'; -import { EncodedTokenizationResult, IState, MetadataConsts, TokenizationRegistry } from 'vs/editor/common/languages'; +import { EncodedTokenizationResult, IState, TokenizationRegistry } from 'vs/editor/common/languages'; +import { MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { NullState } from 'vs/editor/common/languages/nullTokenize'; import { createModelServices, createTextModel, instantiateTextModel } from 'vs/editor/test/common/testTextModel'; diff --git a/src/vs/editor/test/common/model/textModelWithTokens.test.ts b/src/vs/editor/test/common/model/textModelWithTokens.test.ts index ed66c94516d77..c20c35b3ff8bb 100644 --- a/src/vs/editor/test/common/model/textModelWithTokens.test.ts +++ b/src/vs/editor/test/common/model/textModelWithTokens.test.ts @@ -9,7 +9,8 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IFoundBracket } from 'vs/editor/common/textModelBracketPairs'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { ITokenizationSupport, MetadataConsts, TokenizationRegistry, StandardTokenType, EncodedTokenizationResult } from 'vs/editor/common/languages'; +import { ITokenizationSupport, TokenizationRegistry, EncodedTokenizationResult } from 'vs/editor/common/languages'; +import { StandardTokenType, MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { CharacterPair } from 'vs/editor/common/languages/languageConfiguration'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { NullState } from 'vs/editor/common/languages/nullTokenize'; diff --git a/src/vs/editor/test/common/model/tokensStore.test.ts b/src/vs/editor/test/common/model/tokensStore.test.ts index 0dcbea92aa76b..632a5375d257f 100644 --- a/src/vs/editor/test/common/model/tokensStore.test.ts +++ b/src/vs/editor/test/common/model/tokensStore.test.ts @@ -9,7 +9,7 @@ import { SparseTokensStore } from 'vs/editor/common/tokens/sparseTokensStore'; import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { MetadataConsts, TokenMetadata, FontStyle, ColorId } from 'vs/editor/common/languages'; +import { FontStyle, ColorId, MetadataConsts, TokenMetadata } from 'vs/editor/common/encodedTokenAttributes'; import { createModelServices, createTextModel, instantiateTextModel } from 'vs/editor/test/common/testTextModel'; import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; import { LanguageIdCodec } from 'vs/editor/common/services/languagesRegistry'; diff --git a/src/vs/editor/test/common/modes/languageConfiguration.test.ts b/src/vs/editor/test/common/modes/languageConfiguration.test.ts index 49f076e217e03..2f47dfd04dec1 100644 --- a/src/vs/editor/test/common/modes/languageConfiguration.test.ts +++ b/src/vs/editor/test/common/modes/languageConfiguration.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { StandardTokenType } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { StandardAutoClosingPairConditional } from 'vs/editor/common/languages/languageConfiguration'; import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService'; diff --git a/src/vs/editor/test/common/modes/supports/characterPair.test.ts b/src/vs/editor/test/common/modes/supports/characterPair.test.ts index d3f78fc15a465..1bfeaa4b56f0f 100644 --- a/src/vs/editor/test/common/modes/supports/characterPair.test.ts +++ b/src/vs/editor/test/common/modes/supports/characterPair.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { StandardTokenType } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { CharacterPairSupport } from 'vs/editor/common/languages/supports/characterPair'; import { TokenText, createFakeScopedLineTokens } from 'vs/editor/test/common/modesTestUtils'; import { StandardAutoClosingPairConditional } from 'vs/editor/common/languages/languageConfiguration'; diff --git a/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts index 5e3a3bf276bed..5a4dc78ec43c4 100644 --- a/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts +++ b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { StandardTokenType } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/languages/supports/electricCharacter'; import { RichEditBrackets } from 'vs/editor/common/languages/supports/richEditBrackets'; import { TokenText, createFakeScopedLineTokens } from 'vs/editor/test/common/modesTestUtils'; diff --git a/src/vs/editor/test/common/modes/supports/tokenization.test.ts b/src/vs/editor/test/common/modes/supports/tokenization.test.ts index a38cec767f987..1bb5f9bbc761b 100644 --- a/src/vs/editor/test/common/modes/supports/tokenization.test.ts +++ b/src/vs/editor/test/common/modes/supports/tokenization.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { FontStyle } from 'vs/editor/common/languages'; +import { FontStyle } from 'vs/editor/common/encodedTokenAttributes'; import { ColorMap, ExternalThemeTrieElement, ParsedTokenThemeRule, ThemeTrieElementRule, TokenTheme, parseTokenTheme, strcmp } from 'vs/editor/common/languages/supports/tokenization'; suite('Token theme matching', () => { diff --git a/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts b/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts index e49e06d804b6e..a05fbb7e86531 100644 --- a/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts +++ b/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts @@ -5,7 +5,8 @@ import * as assert from 'assert'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { EncodedTokenizationResult, ColorId, FontStyle, IState, MetadataConsts, TokenizationRegistry } from 'vs/editor/common/languages'; +import { EncodedTokenizationResult, IState, TokenizationRegistry } from 'vs/editor/common/languages'; +import { FontStyle, ColorId, MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { tokenizeLineToHTML, _tokenizeToString } from 'vs/editor/common/languages/textToHtmlTokenizer'; import { LanguageIdCodec } from 'vs/editor/common/services/languagesRegistry'; diff --git a/src/vs/editor/test/common/modesTestUtils.ts b/src/vs/editor/test/common/modesTestUtils.ts index d198101edcaa0..21bc380e2c247 100644 --- a/src/vs/editor/test/common/modesTestUtils.ts +++ b/src/vs/editor/test/common/modesTestUtils.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; -import { MetadataConsts, StandardTokenType } from 'vs/editor/common/languages'; +import { StandardTokenType, MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { ScopedLineTokens, createScopedLineTokens } from 'vs/editor/common/languages/supports'; import { LanguageIdCodec } from 'vs/editor/common/services/languagesRegistry'; diff --git a/src/vs/editor/test/common/services/semanticTokensProviderStyling.test.ts b/src/vs/editor/test/common/services/semanticTokensProviderStyling.test.ts index f512a58a9f296..b6660b3da97f7 100644 --- a/src/vs/editor/test/common/services/semanticTokensProviderStyling.test.ts +++ b/src/vs/editor/test/common/services/semanticTokensProviderStyling.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { SparseMultilineTokens } from 'vs/editor/common/tokens/sparseMultilineTokens'; -import { MetadataConsts } from 'vs/editor/common/languages'; +import { MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { SemanticTokensProviderStyling, toMultilineTokens2 } from 'vs/editor/common/services/semanticTokensProviderStyling'; import { createModelServices } from 'vs/editor/test/common/testTextModel'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; diff --git a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts index 4264f60e47ef0..19e225ab4f724 100644 --- a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts +++ b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens'; -import { MetadataConsts } from 'vs/editor/common/languages'; +import { MetadataConsts } from 'vs/editor/common/encodedTokenAttributes'; import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; import { CharacterMapping, RenderLineInput, renderViewLine2 as renderViewLine, LineRange, DomPosition } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { InlineDecorationType } from 'vs/editor/common/viewModel'; diff --git a/src/vs/editor/test/node/classification/typescript.test.ts b/src/vs/editor/test/node/classification/typescript.test.ts index 279801f4c7e56..e99cfa28d943a 100644 --- a/src/vs/editor/test/node/classification/typescript.test.ts +++ b/src/vs/editor/test/node/classification/typescript.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { StandardTokenType } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import * as fs from 'fs'; // import { getPathFromAmdModule } from 'vs/base/test/node/testUtils'; // import { parse } from 'vs/editor/common/modes/tokenization/typescript'; diff --git a/src/vs/workbench/api/browser/mainThreadLanguages.ts b/src/vs/workbench/api/browser/mainThreadLanguages.ts index e8316e42dfbaa..fffdbc7f4dbb2 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguages.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguages.ts @@ -10,7 +10,7 @@ import { MainThreadLanguagesShape, MainContext, ExtHostContext, ExtHostLanguages import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { StandardTokenType } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ILanguageStatus, ILanguageStatusService } from 'vs/workbench/services/languageStatus/common/languageStatusService'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 2a7023db7a064..33fcc3fb351d8 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -22,6 +22,7 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { ILineChange } from 'vs/editor/common/diff/diffComputer'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as languages from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/languages/languageConfiguration'; import { EndOfLineSequence } from 'vs/editor/common/model'; import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel'; @@ -398,7 +399,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { export interface MainThreadLanguagesShape extends IDisposable { $changeLanguage(resource: UriComponents, languageId: string): Promise; - $tokensAtPosition(resource: UriComponents, position: IPosition): Promise; + $tokensAtPosition(resource: UriComponents, position: IPosition): Promise; $setLanguageStatus(handle: number, status: ILanguageStatus): void; $removeLanguageStatus(handle: number): void; } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 94226d271f2a6..95e24e8692aa8 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -20,6 +20,7 @@ import * as editorRange from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; import { IContentDecorationRenderOptions, IDecorationOptions, IDecorationRenderOptions, IThemeDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import * as languages from 'vs/editor/common/languages'; +import * as encodedTokenAttributes from 'vs/editor/common/encodedTokenAttributes'; import * as languageSelector from 'vs/editor/common/languageSelector'; import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model'; import { EditorResolution, ITextEditorOptions } from 'vs/platform/editor/common/editor'; @@ -112,12 +113,12 @@ export namespace Range { } export namespace TokenType { - export function to(type: languages.StandardTokenType): types.StandardTokenType { + export function to(type: encodedTokenAttributes.StandardTokenType): types.StandardTokenType { switch (type) { - case languages.StandardTokenType.Comment: return types.StandardTokenType.Comment; - case languages.StandardTokenType.Other: return types.StandardTokenType.Other; - case languages.StandardTokenType.RegEx: return types.StandardTokenType.RegEx; - case languages.StandardTokenType.String: return types.StandardTokenType.String; + case encodedTokenAttributes.StandardTokenType.Comment: return types.StandardTokenType.Comment; + case encodedTokenAttributes.StandardTokenType.Other: return types.StandardTokenType.Other; + case encodedTokenAttributes.StandardTokenType.RegEx: return types.StandardTokenType.RegEx; + case encodedTokenAttributes.StandardTokenType.String: return types.StandardTokenType.String; } } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts index e3111f08d6cbd..ed70650c31ee2 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts @@ -16,7 +16,8 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { FontStyle, StandardTokenType, TokenMetadata, SemanticTokensLegend, SemanticTokens, ColorId } from 'vs/editor/common/languages'; +import { SemanticTokensLegend, SemanticTokens } from 'vs/editor/common/languages'; +import { FontStyle, ColorId, StandardTokenType, TokenMetadata } from 'vs/editor/common/encodedTokenAttributes'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { editorHoverBackground, editorHoverBorder } from 'vs/platform/theme/common/colorRegistry'; diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 98bcc2d1d6fc9..48a002966c607 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -12,7 +12,8 @@ import { setProperty } from 'vs/base/common/jsonEdit'; import { Constants } from 'vs/base/common/uint'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { InlineValueContext, StandardTokenType } from 'vs/editor/common/languages'; +import { InlineValueContext } from 'vs/editor/common/languages'; +import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { distinct, flatten } from 'vs/base/common/arrays'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDragRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDragRenderer.ts index 43c5852753ec5..5efe69fbc3d25 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDragRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDragRenderer.ts @@ -10,6 +10,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import * as languages from 'vs/editor/common/languages'; +import { ColorId } from 'vs/editor/common/encodedTokenAttributes'; import { tokenizeLineToHTML } from 'vs/editor/common/languages/textToHtmlTokenizer'; import { ITextModel } from 'vs/editor/common/model'; import { BaseCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; @@ -33,8 +34,8 @@ class EditorTextRenderer { const fontWeightVar = '--notebook-editor-font-weight'; const style = `` - + `color: ${colorMap[languages.ColorId.DefaultForeground]};` - + `background-color: ${colorMap[languages.ColorId.DefaultBackground]};` + + `color: ${colorMap[ColorId.DefaultForeground]};` + + `background-color: ${colorMap[ColorId.DefaultBackground]};` + `font-family: var(${fontFamilyVar});` + `font-weight: var(${fontWeightVar});` + `font-size: var(${fontSizeVar});` diff --git a/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts index 2fbc546da955a..5adb077e4cd51 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts @@ -12,7 +12,8 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { EditorResourceAccessor } from 'vs/workbench/common/editor'; import { ITextMateService } from 'vs/workbench/services/textMate/browser/textMate'; import type { IGrammar, StackElement } from 'vscode-textmate'; -import { TokenizationRegistry, TokenMetadata } from 'vs/editor/common/languages'; +import { TokenizationRegistry } from 'vs/editor/common/languages'; +import { TokenMetadata } from 'vs/editor/common/encodedTokenAttributes'; import { ThemeRule, findMatchingThemeRule } from 'vs/workbench/services/textMate/common/TMHelper'; import { Color } from 'vs/base/common/color'; import { IFileService } from 'vs/platform/files/common/files'; diff --git a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts index 0df6157e1bd6d..296cf73f0bcb5 100644 --- a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts +++ b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts @@ -12,7 +12,8 @@ import * as resources from 'vs/base/common/resources'; import * as types from 'vs/base/common/types'; import { equals as equalArray } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; -import { IState, ITokenizationSupport, LanguageId, TokenizationRegistry, StandardTokenType, ITokenizationSupportFactory, TokenizationResult, EncodedTokenizationResult } from 'vs/editor/common/languages'; +import { IState, ITokenizationSupport, TokenizationRegistry, ITokenizationSupportFactory, TokenizationResult, EncodedTokenizationResult } from 'vs/editor/common/languages'; +import { LanguageId, StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { nullTokenizeEncoded } from 'vs/editor/common/languages/nullTokenize'; import { generateTokensCSSForColorMap } from 'vs/editor/common/languages/supports/tokenization'; import { ILanguageService } from 'vs/editor/common/languages/language'; diff --git a/src/vs/workbench/services/textMate/browser/textMateWorker.ts b/src/vs/workbench/services/textMate/browser/textMateWorker.ts index 6b848d33c24ef..c4d6299742dd3 100644 --- a/src/vs/workbench/services/textMate/browser/textMateWorker.ts +++ b/src/vs/workbench/services/textMate/browser/textMateWorker.ts @@ -5,7 +5,7 @@ import { IWorkerContext } from 'vs/editor/common/services/editorSimpleWorker'; import { UriComponents, URI } from 'vs/base/common/uri'; -import { LanguageId } from 'vs/editor/common/languages'; +import { LanguageId } from 'vs/editor/common/encodedTokenAttributes'; import { IValidEmbeddedLanguagesMap, IValidTokenTypeMap, IValidGrammarDefinition } from 'vs/workbench/services/textMate/common/TMScopeRegistry'; import { TMGrammarFactory, ICreateGrammarResult } from 'vs/workbench/services/textMate/common/TMGrammarFactory'; import { IModelChangedEvent, MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; diff --git a/src/vs/workbench/services/textMate/common/TMScopeRegistry.ts b/src/vs/workbench/services/textMate/common/TMScopeRegistry.ts index 83b041e7823b9..5d88a28f4f47f 100644 --- a/src/vs/workbench/services/textMate/common/TMScopeRegistry.ts +++ b/src/vs/workbench/services/textMate/common/TMScopeRegistry.ts @@ -6,7 +6,7 @@ import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; -import { StandardTokenType, LanguageId } from 'vs/editor/common/languages'; +import { LanguageId, StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; export interface IValidGrammarDefinition { location: URI; diff --git a/src/vs/workbench/services/textMate/common/TMTokenization.ts b/src/vs/workbench/services/textMate/common/TMTokenization.ts index 64816e5d91238..058115fb065c7 100644 --- a/src/vs/workbench/services/textMate/common/TMTokenization.ts +++ b/src/vs/workbench/services/textMate/common/TMTokenization.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; -import { IState, ITokenizationSupport, LanguageId, TokenMetadata, TokenizationResult, EncodedTokenizationResult } from 'vs/editor/common/languages'; +import { IState, ITokenizationSupport, TokenizationResult, EncodedTokenizationResult } from 'vs/editor/common/languages'; +import { LanguageId, TokenMetadata } from 'vs/editor/common/encodedTokenAttributes'; import type { IGrammar, StackElement } from 'vscode-textmate'; import { Disposable } from 'vs/base/common/lifecycle'; From 6c12a03285d987979e3fe95d7900e0f5878f286f Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 16:05:38 +0200 Subject: [PATCH 272/942] make sure config listener is getting disposed --- src/vs/platform/windows/electron-main/window.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index e181acbdfe0b9..ced1af3fa1d16 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -287,7 +287,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { if (e.affectsConfiguration(ccConfigKey)) { trafficLightUpdater(); } - }); + }, undefined, this._store); } // Open devtools if instructed from command line args From ffb503f65a8f957fdbce6b2e7cddcc6922c1314c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 24 May 2022 16:11:47 +0200 Subject: [PATCH 273/942] Fix #149632 (#150278) --- src/vs/workbench/contrib/extensions/browser/extensionEditor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index eb5589276f25e..f2da4fedd3460 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -644,6 +644,7 @@ export class ExtensionEditor extends EditorPane { private setRecommendationText(extension: IExtension, template: IExtensionEditorTemplate): void { const updateRecommendationText = (layout: boolean) => { + reset(template.recommendation); const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); if (extRecommendations[extension.identifier.id.toLowerCase()]) { const reasonText = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; @@ -658,8 +659,8 @@ export class ExtensionEditor extends EditorPane { this.layout(this.dimension); } }; - reset(template.recommendation); if (extension.deprecationInfo || extension.state === ExtensionState.Installed) { + reset(template.recommendation); return; } updateRecommendationText(false); From 0844d7c996d6b0c3e4b1196a1d7d426356bdc9c8 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 16:13:37 +0200 Subject: [PATCH 274/942] move `USER_TASKS_GROUP_KEY` into tasks.ts, out of taskService.ts, and prevent cyclic dependency fixes https://github.com/microsoft/vscode/issues/150178 --- src/vs/workbench/api/common/extHostTask.ts | 2 +- .../workbench/contrib/tasks/browser/abstractTaskService.ts | 5 +++-- src/vs/workbench/contrib/tasks/common/taskConfiguration.ts | 5 ++--- src/vs/workbench/contrib/tasks/common/taskService.ts | 2 -- src/vs/workbench/contrib/tasks/common/tasks.ts | 4 +++- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index 00d03d0b6ee3e..c05d65eee9ede 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -24,7 +24,7 @@ import { Schemas } from 'vs/base/common/network'; import * as Platform from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; -import { USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/taskService'; +import { USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/tasks'; import { NotSupportedError } from 'vs/base/common/errors'; export interface IExtHostTask extends ExtHostTaskShape { diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 7e4c505c69cf9..dbd096e791cfa 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -52,9 +52,10 @@ import { Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskEvent, TaskSet, TaskGroup, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE, TaskRunSource, - KeyedTaskIdentifier as NKeyedTaskIdentifier, TaskDefinition, RuntimeType + KeyedTaskIdentifier as NKeyedTaskIdentifier, TaskDefinition, RuntimeType, + USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/tasks'; -import { ITaskService, ITaskProvider, ProblemMatcherRunOptions, CustomizationProperties, TaskFilter, WorkspaceFolderTaskResult, USER_TASKS_GROUP_KEY, CustomExecutionSupportedContext, ShellExecutionSupportedContext, ProcessExecutionSupportedContext } from 'vs/workbench/contrib/tasks/common/taskService'; +import { ITaskService, ITaskProvider, ProblemMatcherRunOptions, CustomizationProperties, TaskFilter, WorkspaceFolderTaskResult, CustomExecutionSupportedContext, ShellExecutionSupportedContext, ProcessExecutionSupportedContext } from 'vs/workbench/contrib/tasks/common/taskService'; import { getTemplates as getTaskTemplates } from 'vs/workbench/contrib/tasks/common/taskTemplates'; import * as TaskConfig from '../common/taskConfiguration'; diff --git a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index 9c6f0ba9e3455..3f567b6320af9 100644 --- a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -23,7 +23,7 @@ import * as Tasks from './tasks'; import { TaskDefinitionRegistry } from './taskDefinitionRegistry'; import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { URI } from 'vs/base/common/uri'; -import { USER_TASKS_GROUP_KEY, ShellExecutionSupportedContext, ProcessExecutionSupportedContext } from 'vs/workbench/contrib/tasks/common/taskService'; +import { ShellExecutionSupportedContext, ProcessExecutionSupportedContext } from 'vs/workbench/contrib/tasks/common/taskService'; import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; export const enum ShellQuoting { @@ -1268,7 +1268,7 @@ export namespace GroupKind { namespace TaskDependency { function uriFromSource(context: ParseContext, source: TaskConfigSource): URI | string { switch (source) { - case TaskConfigSource.User: return USER_TASKS_GROUP_KEY; + case TaskConfigSource.User: return Tasks.USER_TASKS_GROUP_KEY; case TaskConfigSource.TasksJson: return context.workspaceFolder.uri; default: return context.workspace && context.workspace.configuration ? context.workspace.configuration : context.workspaceFolder.uri; } @@ -2146,4 +2146,3 @@ export function parse(workspaceFolder: IWorkspaceFolder, workspace: IWorkspace | export function createCustomTask(contributedTask: Tasks.ContributedTask, configuredProps: Tasks.ConfiguringTask | Tasks.CustomTask): Tasks.CustomTask { return CustomTask.createCustomTask(contributedTask, configuredProps); } - diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index 68a2394a8801e..b75cd445666d2 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -55,8 +55,6 @@ export interface WorkspaceFolderTaskResult extends WorkspaceTaskResult { workspaceFolder: IWorkspaceFolder; } -export const USER_TASKS_GROUP_KEY = 'settings'; - export interface ITaskService { readonly _serviceBrand: undefined; onDidStateChange: Event; diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 1e7e42b6c81fc..e26ad2051e5a1 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -16,7 +16,9 @@ import { RawContextKey, ContextKeyExpression } from 'vs/platform/contextkey/comm import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/taskService'; + + +export const USER_TASKS_GROUP_KEY = 'settings'; export const TASK_RUNNING_STATE = new RawContextKey('taskRunning', false, nls.localize('tasks.taskRunningContext', "Whether a task is currently running.")); export const TASKS_CATEGORY = { value: nls.localize('tasksCategory', "Tasks"), original: 'Tasks' }; From d097ca3f0c13c39ffa01167d1a30fe69b41ff3af Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 24 May 2022 10:19:03 -0400 Subject: [PATCH 275/942] Don't run code review bot on draft PR (#150280) --- .github/workflows/pr-chat.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr-chat.yml b/.github/workflows/pr-chat.yml index 0c8d6bab79388..13803fda7784a 100644 --- a/.github/workflows/pr-chat.yml +++ b/.github/workflows/pr-chat.yml @@ -6,6 +6,7 @@ on: jobs: main: runs-on: ubuntu-latest + if: ${{ !github.event.pull_request.draft }} steps: - name: Checkout Actions uses: actions/checkout@v2 From dfa06b84f820790649f7712dffbfac964b6e0d89 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 24 May 2022 16:42:47 +0200 Subject: [PATCH 276/942] Use an `UtilityProcess` for the extension host (#150167) --- src/bootstrap-fork.js | 18 + .../extensions/common/extensionHostStarter.ts | 6 +- .../electron-main/extensionHostStarter.ts | 166 +++++- .../api/node/extensionHostProcess.ts | 40 +- .../localProcessExtensionHost.ts | 541 ++++++++++-------- 5 files changed, 507 insertions(+), 264 deletions(-) diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js index 06a95927644b7..c035cb6f262a4 100644 --- a/src/bootstrap-fork.js +++ b/src/bootstrap-fork.js @@ -37,6 +37,11 @@ if (process.env['VSCODE_PARENT_PID']) { terminateWhenParentTerminates(); } +// Listen for message ports +if (process.env['VSCODE_WILL_SEND_MESSAGE_PORT']) { + listenForMessagePort(); +} + // Load AMD entry point require('./bootstrap-amd').load(process.env['VSCODE_AMD_ENTRYPOINT']); @@ -264,4 +269,17 @@ function terminateWhenParentTerminates() { } } +function listenForMessagePort() { + // We need to listen for the 'port' event as soon as possible, + // otherwise we might miss the event. But we should also be + // prepared in case the event arrives late. + process.on('port', (e) => { + if (global.vscodePortsCallback) { + global.vscodePortsCallback(e.ports); + } else { + global.vscodePorts = e.ports; + } + }); +} + //#endregion diff --git a/src/vs/platform/extensions/common/extensionHostStarter.ts b/src/vs/platform/extensions/common/extensionHostStarter.ts index d4fc6b48112f5..33f3192f2061f 100644 --- a/src/vs/platform/extensions/common/extensionHostStarter.ts +++ b/src/vs/platform/extensions/common/extensionHostStarter.ts @@ -12,6 +12,9 @@ export const IExtensionHostStarter = createDecorator('ext export const ipcExtensionHostStarterChannelName = 'extensionHostStarter'; export interface IExtensionHostProcessOptions { + responseWindowId: number; + responseChannel: string; + responseNonce: string; env: { [key: string]: string | undefined }; detached: boolean; execArgv: string[] | undefined; @@ -27,8 +30,9 @@ export interface IExtensionHostStarter { onDynamicError(id: string): Event<{ error: SerializedError }>; onDynamicExit(id: string): Event<{ code: number; signal: string }>; + usesUtilityProcess(): Promise; createExtensionHost(): Promise<{ id: string }>; - start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number }>; + start(id: string, opts: IExtensionHostProcessOptions): Promise; enableInspectPort(id: string): Promise; kill(id: string): Promise; diff --git a/src/vs/platform/extensions/electron-main/extensionHostStarter.ts b/src/vs/platform/extensions/electron-main/extensionHostStarter.ts index 493168507e84f..8ef9c5a0da7ab 100644 --- a/src/vs/platform/extensions/electron-main/extensionHostStarter.ts +++ b/src/vs/platform/extensions/electron-main/extensionHostStarter.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { canceled, SerializedError, transformErrorForSerialization } from 'vs/base/common/errors'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter'; import { Emitter, Event } from 'vs/base/common/event'; import { ILogService } from 'vs/platform/log/common/log'; @@ -17,20 +17,40 @@ import { FileAccess } from 'vs/base/common/network'; import { mixin } from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { cwd } from 'vs/base/common/process'; +import type { EventEmitter } from 'events'; +import * as electron from 'electron'; + +declare namespace UtilityProcessProposedApi { + interface UtilityProcessOptions { + serviceName?: string | undefined; + execArgv?: string[] | undefined; + env?: NodeJS.ProcessEnv | undefined; + } + export class UtilityProcess extends EventEmitter { + readonly pid?: number | undefined; + constructor(modulePath: string, args?: string[] | undefined, options?: UtilityProcessOptions); + postMessage(channel: string, message: any, transfer?: Electron.MessagePortMain[]): void; + kill(signal?: number | string): boolean; + on(event: 'exit', listener: (code: number) => void): this; + on(event: 'spawn', listener: () => void): this; + } +} +const UtilityProcess = ((electron as any).UtilityProcess); +const canUseUtilityProcess = (typeof UtilityProcess !== 'undefined') && !!process.env['VSCODE_USE_UTILITY_PROCESS']; export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter { _serviceBrand: undefined; private static _lastId: number = 0; - protected readonly _extHosts: Map; + protected readonly _extHosts: Map; private _shutdown = false; constructor( @ILogService private readonly _logService: ILogService, @ILifecycleMainService lifecycleMainService: ILifecycleMainService ) { - this._extHosts = new Map(); + this._extHosts = new Map(); // On shutdown: gracefully await extension host shutdowns lifecycleMainService.onWillShutdown((e) => { @@ -43,7 +63,7 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter // Intentionally not killing the extension host processes } - private _getExtHost(id: string): ExtensionHostProcess { + private _getExtHost(id: string): ExtensionHostProcess | UtilityExtensionHostProcess { const extHostProcess = this._extHosts.get(id); if (!extHostProcess) { throw new Error(`Unknown extension host!`); @@ -71,12 +91,20 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter return this._getExtHost(id).onExit; } + async usesUtilityProcess(): Promise { + return canUseUtilityProcess; + } + async createExtensionHost(): Promise<{ id: string }> { if (this._shutdown) { throw canceled(); } const id = String(++ExtensionHostStarter._lastId); - const extHost = new ExtensionHostProcess(id, this._logService); + const extHost = ( + canUseUtilityProcess + ? new UtilityExtensionHostProcess(id, this._logService) + : new ExtensionHostProcess(id, this._logService) + ); this._extHosts.set(id, extHost); extHost.onExit(({ pid, code, signal }) => { this._logService.info(`Extension host with pid ${pid} exited with code: ${code}, signal: ${signal}.`); @@ -88,7 +116,7 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter return { id }; } - async start(id: string, opts: IExtensionHostProcessOptions): Promise<{ pid: number }> { + async start(id: string, opts: IExtensionHostProcessOptions): Promise { if (this._shutdown) { throw canceled(); } @@ -160,7 +188,7 @@ class ExtensionHostProcess extends Disposable { super(); } - start(opts: IExtensionHostProcessOptions): { pid: number } { + start(opts: IExtensionHostProcessOptions): void { if (platform.isCI) { this._logService.info(`Calling fork to start extension host...`); } @@ -199,8 +227,130 @@ class ExtensionHostProcess extends Disposable { this._hasExited = true; this._onExit.fire({ pid, code, signal }); }); + } + + enableInspectPort(): boolean { + if (!this._process) { + return false; + } + + this._logService.info(`Enabling inspect port on extension host with pid ${this._process.pid}.`); + + interface ProcessExt { + _debugProcess?(n: number): any; + } + + if (typeof (process)._debugProcess === 'function') { + // use (undocumented) _debugProcess feature of node + (process)._debugProcess!(this._process.pid!); + return true; + } else if (!platform.isWindows) { + // use KILL USR1 on non-windows platforms (fallback) + this._process.kill('SIGUSR1'); + return true; + } else { + // not supported... + return false; + } + } + + kill(): void { + if (!this._process) { + return; + } + this._logService.info(`Killing extension host with pid ${this._process.pid}.`); + this._process.kill(); + } + + async waitForExit(maxWaitTimeMs: number): Promise { + if (!this._process) { + return; + } + const pid = this._process.pid; + this._logService.info(`Waiting for extension host with pid ${pid} to exit.`); + await Promise.race([Event.toPromise(this.onExit), timeout(maxWaitTimeMs)]); + + if (!this._hasExited) { + // looks like we timed out + this._logService.info(`Extension host with pid ${pid} did not exit within ${maxWaitTimeMs}ms.`); + this._process.kill(); + } + } +} + +class UtilityExtensionHostProcess extends Disposable { + + readonly onStdout = Event.None; + readonly onStderr = Event.None; + readonly onError = Event.None; + + readonly _onMessage = this._register(new Emitter()); + readonly onMessage = this._onMessage.event; + + readonly _onExit = this._register(new Emitter<{ pid: number; code: number; signal: string }>()); + readonly onExit = this._onExit.event; + + private _process: UtilityProcessProposedApi.UtilityProcess | null = null; + private _hasExited: boolean = false; - return { pid }; + constructor( + public readonly id: string, + @ILogService private readonly _logService: ILogService, + ) { + super(); + } + + start(opts: IExtensionHostProcessOptions): void { + const responseWindow = electron.BrowserWindow.fromId(opts.responseWindowId); + if (!responseWindow || responseWindow.isDestroyed() || responseWindow.webContents.isDestroyed()) { + this._logService.info(`Refusing to create new Extension Host UtilityProcess because requesting window cannot be found...`); + return; + } + + const serviceName = `extensionHost${this.id}`; + const modulePath = FileAccess.asFileUri('bootstrap-fork.js', require).fsPath; + const args: string[] = ['--type=extensionHost', '--skipWorkspaceStorageLock']; + const execArgv: string[] = opts.execArgv || []; + const env: { [key: string]: any } = { ...opts.env }; + + // Make sure all values are strings, otherwise the process will not start + for (const key of Object.keys(env)) { + env[key] = String(env[key]); + } + + this._logService.info(`Creating new UtilityProcess to start extension host...`); + + this._process = new UtilityProcess(modulePath, args, { serviceName, env, execArgv }); + + this._process.on('spawn', () => { + this._logService.info(`Utility process emits spawn!`); + }); + this._process.on('exit', (code: number) => { + this._logService.info(`Utility process emits exit!`); + this._hasExited = true; + this._onExit.fire({ pid: this._process!.pid!, code, signal: '' }); + }); + const listener = (event: electron.Event, details: electron.Details) => { + if (details.type !== 'Utility') { + return; + } + // Despite the fact that we pass the argument `seviceName`, + // the details have a field called `name` where this value appears + if (details.name === serviceName) { + this._logService.info(`Utility process emits exit!`); + this._hasExited = true; + this._onExit.fire({ pid: this._process!.pid!, code: details.exitCode, signal: '' }); + } + }; + electron.app.on('child-process-gone', listener); + this._register(toDisposable(() => { + electron.app.off('child-process-gone', listener); + })); + + const { port1, port2 } = new electron.MessageChannelMain(); + + this._process.postMessage('port', null, [port2]); + responseWindow.webContents.postMessage(opts.responseChannel, opts.responseNonce, [port1]); } enableInspectPort(): boolean { diff --git a/src/vs/workbench/api/node/extensionHostProcess.ts b/src/vs/workbench/api/node/extensionHostProcess.ts index 2707ecf73d3c5..101afcd8e7611 100644 --- a/src/vs/workbench/api/node/extensionHostProcess.ts +++ b/src/vs/workbench/api/node/extensionHostProcess.ts @@ -23,6 +23,7 @@ import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService'; import { ProcessTimeRunOnceScheduler } from 'vs/base/common/async'; import { boolean } from 'vs/editor/common/config/editorOptions'; import { createURITransformer } from 'vs/workbench/api/node/uriTransformer'; +import { MessagePortMain } from 'electron'; import 'vs/workbench/api/common/extHost.common.services'; import 'vs/workbench/api/node/extHost.node.services'; @@ -102,8 +103,37 @@ let onTerminate = function (reason: string) { nativeExit(); }; -function _createExtHostProtocol(): Promise { - if (process.env.VSCODE_EXTHOST_WILL_SEND_SOCKET) { +function _createExtHostProtocol(): Promise { + if (process.env.VSCODE_WILL_SEND_MESSAGE_PORT) { + + return new Promise((resolve, reject) => { + + const withPorts = (ports: MessagePortMain[]) => { + const port = ports[0]; + const onMessage = new BufferedEmitter(); + port.on('message', (e) => onMessage.fire(VSBuffer.wrap(e.data))); + port.on('close', () => { + onTerminate('renderer closed the MessagePort'); + }); + port.start(); + + resolve({ + onMessage: onMessage.event, + send: message => port.postMessage(message.buffer) + }); + }; + + if ((global).vscodePorts) { + const ports = (global).vscodePorts; + delete (global).vscodePorts; + withPorts(ports); + } else { + (global).vscodePortsCallback = withPorts; + } + + }); + + } else if (process.env.VSCODE_EXTHOST_WILL_SEND_SOCKET) { return new Promise((resolve, reject) => { @@ -220,8 +250,10 @@ async function createExtHostProtocol(): Promise { } } - drain(): Promise { - return protocol.drain(); + async drain(): Promise { + if (protocol.drain) { + return protocol.drain(); + } } }; } diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index 0f7f60bbc7de9..87be62fa31f6e 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -3,21 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Server, Socket, createServer } from 'net'; +import { Server, createServer } from 'net'; import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import * as nls from 'vs/nls'; import { timeout } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter, Event } from 'vs/base/common/event'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { IRemoteConsoleLog, log } from 'vs/base/common/console'; import { logRemoteEntry, logRemoteEntryIfError } from 'vs/workbench/services/extensions/common/remoteConsoleUtil'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; -import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; +import { BufferedEmitter, PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILifecycleService, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -44,6 +44,8 @@ import { SerializedError } from 'vs/base/common/errors'; import { removeDangerousEnvVariables } from 'vs/base/common/processes'; import { StopWatch } from 'vs/base/common/stopwatch'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import { generateUuid } from 'vs/base/common/uuid'; +import { acquirePort } from 'vs/base/parts/ipc/electron-sandbox/ipc.mp'; export interface ILocalProcessExtensionHostInitData { readonly autoStart: boolean; @@ -91,7 +93,7 @@ class ExtensionHostProcess { this._id = id; } - public start(opts: IExtensionHostProcessOptions): Promise<{ pid: number }> { + public start(opts: IExtensionHostProcessOptions): Promise { return this._extensionHostStarter.start(this._id, opts); } @@ -127,11 +129,9 @@ export class LocalProcessExtensionHost implements IExtensionHost { private _terminating: boolean; // Resources, in order they get acquired/created when .start() is called: - private _namedPipeServer: Server | null; private _inspectPort: number | null; private _extensionHostProcess: ExtensionHostProcess | null; - private _extensionHostConnection: Socket | null; - private _messageProtocol: Promise | null; + private _messageProtocol: Promise | null; private readonly _extensionHostLogFile: URI; @@ -161,17 +161,15 @@ export class LocalProcessExtensionHost implements IExtensionHost { this._lastExtensionHostError = null; this._terminating = false; - this._namedPipeServer = null; this._inspectPort = null; this._extensionHostProcess = null; - this._extensionHostConnection = null; this._messageProtocol = null; this._extensionHostLogFile = joinPath(this._environmentService.extHostLogsPath, `${ExtensionHostLogFileName}.log`); this._toDispose.add(this._onExit); this._toDispose.add(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e))); - this._toDispose.add(this._lifecycleService.onDidShutdown(() => this.terminate())); + this._toDispose.add(this._lifecycleService.onDidShutdown(() => this._terminate())); this._toDispose.add(this._extensionHostDebugService.onClose(event => { if (this._isExtensionDevHost && this._environmentService.debugExtensionHost.debugId === event.sessionId) { this._nativeHostService.closeWindow(); @@ -185,7 +183,16 @@ export class LocalProcessExtensionHost implements IExtensionHost { } public dispose(): void { - this.terminate(); + this._terminate(); + } + + private _terminate(): void { + if (this._terminating) { + return; + } + this._terminating = true; + + this._toDispose.dispose(); } public start(): Promise | null { @@ -195,172 +202,204 @@ export class LocalProcessExtensionHost implements IExtensionHost { } if (!this._messageProtocol) { - this._messageProtocol = Promise.all([ + this._messageProtocol = this._start(); + } + + return this._messageProtocol; + } + + private async _start(): Promise { + const usesUtilityProcess = await this._extensionHostStarter.usesUtilityProcess(); + + let extensionHostCreationResult: { id: string }; + let listenOnPipeResult: { pipeName: string; namedPipeServer: Server } | undefined; + let portNumber: number; + let processEnv: platform.IProcessEnvironment; + + if (usesUtilityProcess) { + // We will receive a message port + [extensionHostCreationResult, portNumber, processEnv] = await Promise.all([ + this._extensionHostStarter.createExtensionHost(), + this._tryFindDebugPort(), + this._shellEnvironmentService.getShellEnv(), + ]); + } else { + // We will listen on a named pipe + [extensionHostCreationResult, listenOnPipeResult, portNumber, processEnv] = await Promise.all([ this._extensionHostStarter.createExtensionHost(), this._tryListenOnPipe(), this._tryFindDebugPort(), this._shellEnvironmentService.getShellEnv(), - ]).then(([extensionHostCreationResult, pipeName, portNumber, processEnv]) => { - - this._extensionHostProcess = new ExtensionHostProcess(extensionHostCreationResult.id, this._extensionHostStarter); - - const env = objects.mixin(processEnv, { - VSCODE_AMD_ENTRYPOINT: 'vs/workbench/api/node/extensionHostProcess', - VSCODE_PIPE_LOGGING: 'true', - VSCODE_VERBOSE_LOGGING: true, - VSCODE_LOG_NATIVE: this._isExtensionDevHost, - VSCODE_IPC_HOOK_EXTHOST: pipeName, - VSCODE_HANDLES_UNCAUGHT_ERRORS: true, - VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || this._productService.quality !== 'stable' || this._environmentService.verbose) - }); + ]); + } - if (this._environmentService.debugExtensionHost.env) { - objects.mixin(env, this._environmentService.debugExtensionHost.env); - } + this._extensionHostProcess = new ExtensionHostProcess(extensionHostCreationResult.id, this._extensionHostStarter); + + const env = objects.mixin(processEnv, { + VSCODE_AMD_ENTRYPOINT: 'vs/workbench/api/node/extensionHostProcess', + VSCODE_PIPE_LOGGING: 'true', + VSCODE_VERBOSE_LOGGING: true, + VSCODE_LOG_NATIVE: this._isExtensionDevHost, + VSCODE_WILL_SEND_MESSAGE_PORT: listenOnPipeResult ? undefined : 'true', + VSCODE_IPC_HOOK_EXTHOST: listenOnPipeResult ? listenOnPipeResult.pipeName : undefined, + VSCODE_HANDLES_UNCAUGHT_ERRORS: true, + VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || this._productService.quality !== 'stable' || this._environmentService.verbose) + }); - removeDangerousEnvVariables(env); + if (this._environmentService.debugExtensionHost.env) { + objects.mixin(env, this._environmentService.debugExtensionHost.env); + } - if (this._isExtensionDevHost) { - // Unset `VSCODE_CODE_CACHE_PATH` when developing extensions because it might - // be that dependencies, that otherwise would be cached, get modified. - delete env['VSCODE_CODE_CACHE_PATH']; - } + removeDangerousEnvVariables(env); - const opts = { - env, - // We only detach the extension host on windows. Linux and Mac orphan by default - // and detach under Linux and Mac create another process group. - // We detach because we have noticed that when the renderer exits, its child processes - // (i.e. extension host) are taken down in a brutal fashion by the OS - detached: !!platform.isWindows, - execArgv: undefined as string[] | undefined, - silent: true - }; - - if (portNumber !== 0) { - opts.execArgv = [ - '--nolazy', - (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + portNumber - ]; - } else { - opts.execArgv = ['--inspect-port=0']; - } + if (this._isExtensionDevHost) { + // Unset `VSCODE_CODE_CACHE_PATH` when developing extensions because it might + // be that dependencies, that otherwise would be cached, get modified. + delete env['VSCODE_CODE_CACHE_PATH']; + } - if (this._environmentService.extensionTestsLocationURI) { - opts.execArgv.unshift('--expose-gc'); - } + const opts: IExtensionHostProcessOptions = { + responseWindowId: this._environmentService.window.id, + responseChannel: 'vscode:startExtensionHostMessagePortResult', + responseNonce: generateUuid(), + env, + // We only detach the extension host on windows. Linux and Mac orphan by default + // and detach under Linux and Mac create another process group. + // We detach because we have noticed that when the renderer exits, its child processes + // (i.e. extension host) are taken down in a brutal fashion by the OS + detached: !!platform.isWindows, + execArgv: undefined as string[] | undefined, + silent: true + }; - if (this._environmentService.args['prof-v8-extensions']) { - opts.execArgv.unshift('--prof'); - } + if (portNumber !== 0) { + opts.execArgv = [ + '--nolazy', + (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + portNumber + ]; + } else { + opts.execArgv = ['--inspect-port=0']; + } - if (this._environmentService.args['max-memory']) { - opts.execArgv.unshift(`--max-old-space-size=${this._environmentService.args['max-memory']}`); - } + if (this._environmentService.extensionTestsLocationURI) { + opts.execArgv.unshift('--expose-gc'); + } - // Catch all output coming from the extension host process - type Output = { data: string; format: string[] }; - const onStdout = this._handleProcessOutputStream(this._extensionHostProcess.onStdout); - const onStderr = this._handleProcessOutputStream(this._extensionHostProcess.onStderr); - const onOutput = Event.any( - Event.map(onStdout.event, o => ({ data: `%c${o}`, format: [''] })), - Event.map(onStderr.event, o => ({ data: `%c${o}`, format: ['color: red'] })) - ); + if (this._environmentService.args['prof-v8-extensions']) { + opts.execArgv.unshift('--prof'); + } - // Debounce all output, so we can render it in the Chrome console as a group - const onDebouncedOutput = Event.debounce(onOutput, (r, o) => { - return r - ? { data: r.data + o.data, format: [...r.format, ...o.format] } - : { data: o.data, format: o.format }; - }, 100); - - // Print out extension host output - onDebouncedOutput(output => { - const inspectorUrlMatch = output.data && output.data.match(/ws:\/\/([^\s]+:(\d+)\/[^\s]+)/); - if (inspectorUrlMatch) { - if (!this._environmentService.isBuilt && !this._isExtensionDevTestFromCli) { - console.log(`%c[Extension Host] %cdebugger inspector at chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=${inspectorUrlMatch[1]}`, 'color: blue', 'color:'); - } - if (!this._inspectPort) { - this._inspectPort = Number(inspectorUrlMatch[2]); - this._onDidSetInspectPort.fire(); - } - } else { - if (!this._isExtensionDevTestFromCli) { - console.group('Extension Host'); - console.log(output.data, ...output.format); - console.groupEnd(); - } - } - }); + if (this._environmentService.args['max-memory']) { + opts.execArgv.unshift(`--max-old-space-size=${this._environmentService.args['max-memory']}`); + } - // Support logging from extension host - this._extensionHostProcess.onMessage(msg => { - if (msg && (msg).type === '__$console') { - this._logExtensionHostMessage(msg); - } - }); + // Catch all output coming from the extension host process + type Output = { data: string; format: string[] }; + const onStdout = this._handleProcessOutputStream(this._extensionHostProcess.onStdout); + const onStderr = this._handleProcessOutputStream(this._extensionHostProcess.onStderr); + const onOutput = Event.any( + Event.map(onStdout.event, o => ({ data: `%c${o}`, format: [''] })), + Event.map(onStderr.event, o => ({ data: `%c${o}`, format: ['color: red'] })) + ); + + // Debounce all output, so we can render it in the Chrome console as a group + const onDebouncedOutput = Event.debounce(onOutput, (r, o) => { + return r + ? { data: r.data + o.data, format: [...r.format, ...o.format] } + : { data: o.data, format: o.format }; + }, 100); + + // Print out extension host output + onDebouncedOutput(output => { + const inspectorUrlMatch = output.data && output.data.match(/ws:\/\/([^\s]+:(\d+)\/[^\s]+)/); + if (inspectorUrlMatch) { + if (!this._environmentService.isBuilt && !this._isExtensionDevTestFromCli) { + console.log(`%c[Extension Host] %cdebugger inspector at chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=${inspectorUrlMatch[1]}`, 'color: blue', 'color:'); + } + if (!this._inspectPort) { + this._inspectPort = Number(inspectorUrlMatch[2]); + this._onDidSetInspectPort.fire(); + } + } else { + if (!this._isExtensionDevTestFromCli) { + console.group('Extension Host'); + console.log(output.data, ...output.format); + console.groupEnd(); + } + } + }); - // Lifecycle + // Support logging from extension host + this._extensionHostProcess.onMessage(msg => { + if (msg && (msg).type === '__$console') { + this._logExtensionHostMessage(msg); + } + }); - this._extensionHostProcess.onError((e) => this._onExtHostProcessError(e.error)); - this._extensionHostProcess.onExit(({ code, signal }) => this._onExtHostProcessExit(code, signal)); + // Lifecycle - // Notify debugger that we are ready to attach to the process if we run a development extension - if (portNumber) { - if (this._isExtensionDevHost && portNumber && this._isExtensionDevDebug && this._environmentService.debugExtensionHost.debugId) { - this._extensionHostDebugService.attachSession(this._environmentService.debugExtensionHost.debugId, portNumber); - } - this._inspectPort = portNumber; - this._onDidSetInspectPort.fire(); - } + this._extensionHostProcess.onError((e) => this._onExtHostProcessError(e.error)); + this._extensionHostProcess.onExit(({ code, signal }) => this._onExtHostProcessExit(code, signal)); - // Help in case we fail to start it - let startupTimeoutHandle: any; - if (!this._environmentService.isBuilt && !this._environmentService.remoteAuthority || this._isExtensionDevHost) { - startupTimeoutHandle = setTimeout(() => { - this._logService.error(`[LocalProcessExtensionHost]: Extension host did not start in 10 seconds (debugBrk: ${this._isExtensionDevDebugBrk})`); - - const msg = this._isExtensionDevDebugBrk - ? nls.localize('extensionHost.startupFailDebug', "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.") - : nls.localize('extensionHost.startupFail', "Extension host did not start in 10 seconds, that might be a problem."); - - this._notificationService.prompt(Severity.Warning, msg, - [{ - label: nls.localize('reloadWindow', "Reload Window"), - run: () => this._hostService.reload() - }], - { sticky: true } - ); - }, 10000); - } + // Notify debugger that we are ready to attach to the process if we run a development extension + if (portNumber) { + if (this._isExtensionDevHost && portNumber && this._isExtensionDevDebug && this._environmentService.debugExtensionHost.debugId) { + this._extensionHostDebugService.attachSession(this._environmentService.debugExtensionHost.debugId, portNumber); + } + this._inspectPort = portNumber; + this._onDidSetInspectPort.fire(); + } - // Initialize extension host process with hand shakes - return this._tryExtHostHandshake(opts).then((protocol) => { - clearTimeout(startupTimeoutHandle); - return protocol; - }); - }); + // Help in case we fail to start it + let startupTimeoutHandle: any; + if (!this._environmentService.isBuilt && !this._environmentService.remoteAuthority || this._isExtensionDevHost) { + startupTimeoutHandle = setTimeout(() => { + this._logService.error(`[LocalProcessExtensionHost]: Extension host did not start in 10 seconds (debugBrk: ${this._isExtensionDevDebugBrk})`); + + const msg = this._isExtensionDevDebugBrk + ? nls.localize('extensionHost.startupFailDebug', "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.") + : nls.localize('extensionHost.startupFail', "Extension host did not start in 10 seconds, that might be a problem."); + + this._notificationService.prompt(Severity.Warning, msg, + [{ + label: nls.localize('reloadWindow', "Reload Window"), + run: () => this._hostService.reload() + }], + { sticky: true } + ); + }, 10000); } - return this._messageProtocol; + // Initialize extension host process with hand shakes + const protocol = await ( + listenOnPipeResult + ? this._performNamedPipeHandshake(listenOnPipeResult.namedPipeServer, opts) + : this._performMessagePortHandshake(opts) + ); + clearTimeout(startupTimeoutHandle); + return protocol; } /** * Start a server (`this._namedPipeServer`) that listens on a named pipe and return the named pipe name. */ - private _tryListenOnPipe(): Promise { - return new Promise((resolve, reject) => { + private _tryListenOnPipe(): Promise<{ pipeName: string; namedPipeServer: Server }> { + return new Promise<{ pipeName: string; namedPipeServer: Server }>((resolve, reject) => { const pipeName = createRandomIPCHandle(); - this._namedPipeServer = createServer(); - this._namedPipeServer.on('error', reject); - this._namedPipeServer.listen(pipeName, () => { - if (this._namedPipeServer) { - this._namedPipeServer.removeListener('error', reject); + const namedPipeServer = createServer(); + namedPipeServer.on('error', reject); + namedPipeServer.listen(pipeName, () => { + if (namedPipeServer) { + namedPipeServer.removeListener('error', reject); } - resolve(pipeName); + resolve({ pipeName, namedPipeServer }); }); + this._toDispose.add(toDisposable(() => { + if (namedPipeServer.listening) { + namedPipeServer.close(); + } + })); }); } @@ -394,33 +433,82 @@ export class LocalProcessExtensionHost implements IExtensionHost { return port || 0; } - private _tryExtHostHandshake(opts: IExtensionHostProcessOptions): Promise { + private _performMessagePortHandshake(opts: IExtensionHostProcessOptions): Promise { + + // Get ready to acquire the message port from the shared process worker + const portPromise = acquirePort(undefined /* we trigger the request via service call! */, opts.responseChannel, opts.responseNonce); + + return new Promise((resolve, reject) => { + + const handle = setTimeout(() => { + reject('The local extension host took longer than 60s to connect.'); + }, 60 * 1000); + + portPromise.then((port) => { + clearTimeout(handle); + + const onMessage = new BufferedEmitter(); + port.onmessage = ((e) => onMessage.fire(VSBuffer.wrap(e.data))); + port.start(); + + resolve({ + onMessage: onMessage.event, + send: message => port.postMessage(message.buffer), + }); + }); + + // Now that the message port listener is installed, start the ext host process + const sw = StopWatch.create(false); + this._extensionHostProcess!.start(opts).then(() => { + const duration = sw.elapsed(); + if (platform.isCI) { + this._logService.info(`IExtensionHostStarter.start() took ${duration} ms.`); + } + }, (err) => { + // Starting the ext host process resulted in an error + reject(err); + }); + + }).then((protocol) => { + return this._performHandshake(protocol); + }); + } + + private _performNamedPipeHandshake(namedPipeServer: Server, opts: IExtensionHostProcessOptions): Promise { return new Promise((resolve, reject) => { // Wait for the extension host to connect to our named pipe // and wrap the socket in the message passing protocol - let handle = setTimeout(() => { - if (this._namedPipeServer) { - this._namedPipeServer.close(); - this._namedPipeServer = null; + const handle = setTimeout(() => { + if (namedPipeServer.listening) { + namedPipeServer.close(); } reject('The local extension host took longer than 60s to connect.'); }, 60 * 1000); - this._namedPipeServer!.on('connection', socket => { + namedPipeServer.on('connection', socket => { clearTimeout(handle); - if (this._namedPipeServer) { - this._namedPipeServer.close(); - this._namedPipeServer = null; + if (namedPipeServer.listening) { + namedPipeServer.close(); } - this._extensionHostConnection = socket; - // using a buffered message protocol here because between now - // and the first time a `then` executes some messages might be lost - // unless we immediately register a listener for `onMessage`. - resolve(new PersistentProtocol(new NodeSocket(this._extensionHostConnection, 'renderer-exthost'))); + const nodeSocket = new NodeSocket(socket, 'renderer-exthost'); + const protocol = new PersistentProtocol(nodeSocket); + + this._toDispose.add(toDisposable(() => { + // Send the extension host a request to terminate itself + // (graceful termination) + protocol.send(createMessageOfType(MessageType.Terminate)); + protocol.flush(); + + socket.end(); + nodeSocket.dispose(); + protocol.dispose(); + })); + + resolve(protocol); }); // Now that the named pipe listener is installed, start the ext host process @@ -436,60 +524,62 @@ export class LocalProcessExtensionHost implements IExtensionHost { }); }).then((protocol) => { + return this._performHandshake(protocol); + }); + } - // 1) wait for the incoming `ready` event and send the initialization data. - // 2) wait for the incoming `initialized` event. - return new Promise((resolve, reject) => { + private _performHandshake(protocol: IMessagePassingProtocol): Promise { + // 1) wait for the incoming `ready` event and send the initialization data. + // 2) wait for the incoming `initialized` event. + return new Promise((resolve, reject) => { - let timeoutHandle: NodeJS.Timer; - const installTimeoutCheck = () => { - timeoutHandle = setTimeout(() => { - reject('The local extenion host took longer than 60s to send its ready message.'); - }, 60 * 1000); - }; - const uninstallTimeoutCheck = () => { - clearTimeout(timeoutHandle); - }; + let timeoutHandle: NodeJS.Timer; + const installTimeoutCheck = () => { + timeoutHandle = setTimeout(() => { + reject('The local extenion host took longer than 60s to send its ready message.'); + }, 60 * 1000); + }; + const uninstallTimeoutCheck = () => { + clearTimeout(timeoutHandle); + }; - // Wait 60s for the ready message - installTimeoutCheck(); + // Wait 60s for the ready message + installTimeoutCheck(); - const disposable = protocol.onMessage(msg => { + const disposable = protocol.onMessage(msg => { - if (isMessageOfType(msg, MessageType.Ready)) { + if (isMessageOfType(msg, MessageType.Ready)) { - // 1) Extension Host is ready to receive messages, initialize it - uninstallTimeoutCheck(); + // 1) Extension Host is ready to receive messages, initialize it + uninstallTimeoutCheck(); - this._createExtHostInitData().then(data => { + this._createExtHostInitData().then(data => { - // Wait 60s for the initialized message - installTimeoutCheck(); + // Wait 60s for the initialized message + installTimeoutCheck(); - protocol.send(VSBuffer.fromString(JSON.stringify(data))); - }); - return; - } - - if (isMessageOfType(msg, MessageType.Initialized)) { + protocol.send(VSBuffer.fromString(JSON.stringify(data))); + }); + return; + } - // 2) Extension Host is initialized - uninstallTimeoutCheck(); + if (isMessageOfType(msg, MessageType.Initialized)) { - // stop listening for messages here - disposable.dispose(); + // 2) Extension Host is initialized + uninstallTimeoutCheck(); - // Register log channel for exthost log - Registry.as(Extensions.OutputChannels).registerChannel({ id: 'extHostLog', label: nls.localize('extension host Log', "Extension Host"), file: this._extensionHostLogFile, log: true }); + // stop listening for messages here + disposable.dispose(); - // release this promise - resolve(protocol); - return; - } + // Register log channel for exthost log + Registry.as(Extensions.OutputChannels).registerChannel({ id: 'extHostLog', label: nls.localize('extension host Log', "Extension Host"), file: this._extensionHostLogFile, log: true }); - console.error(`received unexpected message during handshake phase from the extension host: `, msg); - }); + // release this promise + resolve(protocol); + return; + } + console.error(`received unexpected message during handshake phase from the extension host: `, msg); }); }); @@ -632,58 +722,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { return withNullAsUndefined(this._inspectPort); } - private terminate(): void { - if (this._terminating) { - return; - } - this._terminating = true; - - this._toDispose.dispose(); - - if (!this._messageProtocol) { - // .start() was not called - return; - } - - this._messageProtocol.then((protocol) => { - - // Send the extension host a request to terminate itself - // (graceful termination) - protocol.send(createMessageOfType(MessageType.Terminate)); - - protocol.getSocket().dispose(); - - protocol.dispose(); - - // Give the extension host 10s, after which we will - // try to kill the process and release any resources - setTimeout(() => this._cleanResources(), 10 * 1000); - - }, (err) => { - - // Establishing a protocol with the extension host failed, so - // try to kill the process and release any resources. - this._cleanResources(); - }); - } - - private _cleanResources(): void { - if (this._namedPipeServer) { - this._namedPipeServer.close(); - this._namedPipeServer = null; - } - if (this._extensionHostConnection) { - this._extensionHostConnection.end(); - this._extensionHostConnection = null; - } - if (this._extensionHostProcess) { - this._extensionHostProcess.kill(); - this._extensionHostProcess = null; - } - } - private _onWillShutdown(event: WillShutdownEvent): void { - // If the extension development host was started without debugger attached we need // to communicate this back to the main side to terminate the debug session if (this._isExtensionDevHost && !this._isExtensionDevTestFromCli && !this._isExtensionDevDebug && this._environmentService.debugExtensionHost.debugId) { From 54cf6a154f31750bc6cb77b16b9eb18cae8a32a8 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 24 May 2022 17:01:41 +0200 Subject: [PATCH 277/942] SCMInput enablement proposed api (#150270) --- src/vs/workbench/api/browser/mainThreadSCM.ts | 10 ++++++++++ .../workbench/api/common/extHost.protocol.ts | 1 + src/vs/workbench/api/common/extHostSCM.ts | 19 ++++++++++++++++++ .../contrib/scm/browser/scmViewPane.ts | 7 +++++++ src/vs/workbench/contrib/scm/common/scm.ts | 3 +++ .../contrib/scm/common/scmService.ts | 14 +++++++++++++ .../common/extensionsApiProposals.ts | 1 + src/vscode-dts/vscode.proposed.scmInput.d.ts | 20 +++++++++++++++++++ 8 files changed, 75 insertions(+) create mode 100644 src/vscode-dts/vscode.proposed.scmInput.d.ts diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 3cb6a499c8c91..5ac8c93566704 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -421,6 +421,16 @@ export class MainThreadSCM implements MainThreadSCMShape { repository.input.placeholder = placeholder; } + $setInputBoxEnablement(sourceControlHandle: number, enabled: boolean): void { + const repository = this._repositories.get(sourceControlHandle); + + if (!repository) { + return; + } + + repository.input.enabled = enabled; + } + $setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void { const repository = this._repositories.get(sourceControlHandle); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 33fcc3fb351d8..c7ebcd110f804 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1187,6 +1187,7 @@ export interface MainThreadSCMShape extends IDisposable { $setInputBoxValue(sourceControlHandle: number, value: string): void; $setInputBoxPlaceholder(sourceControlHandle: number, placeholder: string): void; + $setInputBoxEnablement(sourceControlHandle: number, enabled: boolean): void; $setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void; $showValidationMessage(sourceControlHandle: number, message: string | IMarkdownString, type: InputValidationType): void; $setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void; diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 49a54bc71fe49..b65c52714b040 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -249,6 +249,25 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { this.#proxy.$setValidationProviderIsEnabled(this._sourceControlHandle, !!fn); } + private _enabled: boolean = true; + + get enabled(): boolean { + checkProposedApiEnabled(this._extension, 'scmInput'); + return this._enabled; + } + + set enabled(enabled: boolean) { + checkProposedApiEnabled(this._extension, 'scmInput'); + enabled = !!enabled; + + if (this._enabled === enabled) { + return; + } + + this._enabled = enabled; + this.#proxy.$setInputBoxEnablement(this._sourceControlHandle, enabled); + } + private _visible: boolean = true; get visible(): boolean { diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index a3059234eb15f..53c6abecef487 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1862,6 +1862,13 @@ class SCMInputWidget extends Disposable { this.repositoryDisposables.add(input.repository.provider.onDidChangeCommitTemplate(updateTemplate, this)); updateTemplate(); + // Update input enablement + const updateEnablement = (enabled: boolean) => { + this.inputEditor.updateOptions({ readOnly: enabled }); + }; + this.repositoryDisposables.add(input.onDidChangeEnablement(enabled => updateEnablement(enabled))); + updateEnablement(input.enabled); + // Save model this.model = { input, textModel }; } diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index e180e8e9d3ca9..9d18d25a13160 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -121,6 +121,9 @@ export interface ISCMInput { validateInput: IInputValidator; readonly onDidChangeValidateInput: Event; + enabled: boolean; + readonly onDidChangeEnablement: Event; + visible: boolean; readonly onDidChangeVisibility: Event; diff --git a/src/vs/workbench/contrib/scm/common/scmService.ts b/src/vs/workbench/contrib/scm/common/scmService.ts index 7877ba1d34e6b..73da6c00923f4 100644 --- a/src/vs/workbench/contrib/scm/common/scmService.ts +++ b/src/vs/workbench/contrib/scm/common/scmService.ts @@ -38,6 +38,20 @@ class SCMInput implements ISCMInput { private readonly _onDidChangePlaceholder = new Emitter(); readonly onDidChangePlaceholder: Event = this._onDidChangePlaceholder.event; + private _enabled = true; + + get enabled(): boolean { + return this._enabled; + } + + set enabled(enabled: boolean) { + this._enabled = enabled; + this._onDidChangeEnablement.fire(enabled); + } + + private readonly _onDidChangeEnablement = new Emitter(); + readonly onDidChangeEnablement: Event = this._onDidChangeEnablement.event; + private _visible = true; get visible(): boolean { diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 993118366837b..3c4bf725a84a3 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -46,6 +46,7 @@ export const allApiProposals = Object.freeze({ quickPickSortByLabel: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts', resolvers: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.resolvers.d.ts', scmActionButton: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmActionButton.d.ts', + scmInput: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmInput.d.ts', scmSelectedProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts', scmValidation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmValidation.d.ts', taskPresentationGroup: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts', diff --git a/src/vscode-dts/vscode.proposed.scmInput.d.ts b/src/vscode-dts/vscode.proposed.scmInput.d.ts new file mode 100644 index 0000000000000..6efdb57ae4578 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.scmInput.d.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/150268 + + /** + * Represents the input box in the Source Control viewlet. + */ + export interface SourceControlInputBox { + + /** + * Controls whether the input box is enabled (default is `true`). + */ + enabled: boolean; + } +} From ac0a282547d16089582d7506e721415954505733 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Tue, 24 May 2022 08:33:53 -0700 Subject: [PATCH 278/942] Fix default override model value (#150232) --- .../settingsEditorSettingIndicators.ts | 6 +++--- .../preferences/browser/settingsTree.ts | 2 +- .../preferences/browser/settingsTreeModels.ts | 19 +++++++++++++++++-- .../preferences/common/preferences.ts | 2 +- .../preferences/common/preferencesModels.ts | 2 +- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts index 2f5cfdd2caa42..7f5d1e698ed28 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts @@ -122,7 +122,7 @@ export class SettingsTreeIndicatorsLabel { updateDefaultOverrideIndicator(element: SettingsTreeSettingElement) { this.defaultOverrideIndicatorElement.style.display = 'none'; - const defaultValueSource = element.setting.defaultValueSource; + const defaultValueSource = element.defaultValueSource; if (defaultValueSource) { this.defaultOverrideIndicatorElement.style.display = 'inline'; if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { @@ -157,8 +157,8 @@ export function getIndicatorsLabelAriaLabel(element: SettingsTreeSettingElement, } // Add default override indicator text - if (element.setting.defaultValueSource) { - const defaultValueSource = element.setting.defaultValueSource; + if (element.defaultValueSource) { + const defaultValueSource = element.defaultValueSource; if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { const extensionSource = defaultValueSource.displayName ?? defaultValueSource.id; ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", extensionSource)); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 5a9aa77dcf2b0..b65d169f75c80 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1115,7 +1115,7 @@ export class SettingComplexRenderer extends AbstractSettingRenderer implements I template.elementDisposables.add(template.button.onDidClick(() => { if (isLanguageTagSetting) { - this._onApplyFilter.fire(`@${LANGUAGE_SETTING_TAG}:${plainKey}`); + this._onApplyFilter.fire(`@${LANGUAGE_SETTING_TAG}${plainKey}`); } else { this._onDidOpenSettings.fire(dataElement.setting.key); } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index b16ea3ac9428e..aa4cce1ae2b44 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -18,7 +18,7 @@ import { FOLDER_SCOPES, WORKSPACE_SCOPES, REMOTE_MACHINE_SCOPES, LOCAL_MACHINE_S import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import { ConfigurationScope, EditPresentationTypes, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { ConfigurationScope, EditPresentationTypes, Extensions, IConfigurationRegistry, IExtensionInfo } from 'vs/platform/configuration/common/configurationRegistry'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -129,6 +129,12 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { */ defaultValue?: any; + /** + * The source of the default value to display. + * This value also accounts for extension-contributed language-specific default value overrides. + */ + defaultValueSource: string | IExtensionInfo | undefined; + /** * Whether the setting is configured in the selected scope. */ @@ -146,7 +152,12 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { tags?: Set; overriddenScopeList: string[] = []; + + /** + * For each language that contributes setting values or default overrides, we can see those values here. + */ languageOverrideValues: Map> = new Map>(); + description!: string; valueType!: SettingValueType; @@ -219,6 +230,10 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { } } + // The user might have added, removed, or modified a language filter, + // so we reset the default value source to the non-language-specific default value source for now. + this.defaultValueSource = this.setting.nonLanguageSpecificDefaultValueSource; + if (inspected.policyValue) { this.hasPolicyValue = true; isConfigured = false; // The user did not manually configure the setting themselves. @@ -236,7 +251,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { const registryValues = Registry.as(Extensions.Configuration).getConfigurationDefaultsOverrides(); const overrideValueSource = registryValues.get(`[${languageSelector}]`)?.valuesSources?.get(this.setting.key); if (overrideValueSource) { - this.setting.defaultValueSource = overrideValueSource; + this.defaultValueSource = overrideValueSource; } } else { this.scopeValue = isConfigured && inspected[targetSelector]; diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index 5aaf1198fe43c..06b43562999a6 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -87,7 +87,7 @@ export interface ISetting { enumItemLabels?: string[]; allKeysAreBoolean?: boolean; editPresentation?: EditPresentationTypes; - defaultValueSource?: string | IExtensionInfo; + nonLanguageSpecificDefaultValueSource?: string | IExtensionInfo; isLanguageTagSetting?: boolean; categoryOrder?: number; categoryLabel?: string; diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index d93923deb81f8..9fb02061b6e11 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -751,7 +751,7 @@ export class DefaultSettings extends Disposable { allKeysAreBoolean, editPresentation: prop.editPresentation, order: prop.order, - defaultValueSource, + nonLanguageSpecificDefaultValueSource: defaultValueSource, isLanguageTagSetting, categoryLabel, categoryOrder From 9950ba00d9bd21fa46f86fbbd5ac88b6c021e838 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 24 May 2022 18:52:13 +0200 Subject: [PATCH 279/942] Extract connection strategies to two classes --- .../localProcessExtensionHost.ts | 313 +++++++++--------- 1 file changed, 165 insertions(+), 148 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index 87be62fa31f6e..8fdad2df0692a 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -10,7 +10,7 @@ import * as nls from 'vs/nls'; import { timeout } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter, Event } from 'vs/base/common/event'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -210,28 +210,23 @@ export class LocalProcessExtensionHost implements IExtensionHost { private async _start(): Promise { const usesUtilityProcess = await this._extensionHostStarter.usesUtilityProcess(); - - let extensionHostCreationResult: { id: string }; - let listenOnPipeResult: { pipeName: string; namedPipeServer: Server } | undefined; - let portNumber: number; - let processEnv: platform.IProcessEnvironment; - if (usesUtilityProcess) { - // We will receive a message port - [extensionHostCreationResult, portNumber, processEnv] = await Promise.all([ - this._extensionHostStarter.createExtensionHost(), - this._tryFindDebugPort(), - this._shellEnvironmentService.getShellEnv(), - ]); + const communication = this._toDispose.add(new ExtHostMessagePortCommunication(this._logService)); + return this._startWithCommunication(communication); } else { - // We will listen on a named pipe - [extensionHostCreationResult, listenOnPipeResult, portNumber, processEnv] = await Promise.all([ - this._extensionHostStarter.createExtensionHost(), - this._tryListenOnPipe(), - this._tryFindDebugPort(), - this._shellEnvironmentService.getShellEnv(), - ]); + const communication = this._toDispose.add(new ExtHostNamedPipeCommunication(this._logService)); + return this._startWithCommunication(communication); } + } + + private async _startWithCommunication(communication: IExtHostCommunication): Promise { + + const [extensionHostCreationResult, communicationPreparedData, portNumber, processEnv] = await Promise.all([ + this._extensionHostStarter.createExtensionHost(), + communication.prepare(), + this._tryFindDebugPort(), + this._shellEnvironmentService.getShellEnv(), + ]); this._extensionHostProcess = new ExtensionHostProcess(extensionHostCreationResult.id, this._extensionHostStarter); @@ -240,8 +235,6 @@ export class LocalProcessExtensionHost implements IExtensionHost { VSCODE_PIPE_LOGGING: 'true', VSCODE_VERBOSE_LOGGING: true, VSCODE_LOG_NATIVE: this._isExtensionDevHost, - VSCODE_WILL_SEND_MESSAGE_PORT: listenOnPipeResult ? undefined : 'true', - VSCODE_IPC_HOOK_EXTHOST: listenOnPipeResult ? listenOnPipeResult.pipeName : undefined, VSCODE_HANDLES_UNCAUGHT_ERRORS: true, VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || this._productService.quality !== 'stable' || this._environmentService.verbose) }); @@ -371,38 +364,12 @@ export class LocalProcessExtensionHost implements IExtensionHost { } // Initialize extension host process with hand shakes - const protocol = await ( - listenOnPipeResult - ? this._performNamedPipeHandshake(listenOnPipeResult.namedPipeServer, opts) - : this._performMessagePortHandshake(opts) - ); + const protocol = await communication.establishProtocol(communicationPreparedData, this._extensionHostProcess, opts); + await this._performHandshake(protocol); clearTimeout(startupTimeoutHandle); return protocol; } - /** - * Start a server (`this._namedPipeServer`) that listens on a named pipe and return the named pipe name. - */ - private _tryListenOnPipe(): Promise<{ pipeName: string; namedPipeServer: Server }> { - return new Promise<{ pipeName: string; namedPipeServer: Server }>((resolve, reject) => { - const pipeName = createRandomIPCHandle(); - - const namedPipeServer = createServer(); - namedPipeServer.on('error', reject); - namedPipeServer.listen(pipeName, () => { - if (namedPipeServer) { - namedPipeServer.removeListener('error', reject); - } - resolve({ pipeName, namedPipeServer }); - }); - this._toDispose.add(toDisposable(() => { - if (namedPipeServer.listening) { - namedPipeServer.close(); - } - })); - }); - } - /** * Find a free port if extension host debugging is enabled. */ @@ -433,105 +400,10 @@ export class LocalProcessExtensionHost implements IExtensionHost { return port || 0; } - private _performMessagePortHandshake(opts: IExtensionHostProcessOptions): Promise { - - // Get ready to acquire the message port from the shared process worker - const portPromise = acquirePort(undefined /* we trigger the request via service call! */, opts.responseChannel, opts.responseNonce); - - return new Promise((resolve, reject) => { - - const handle = setTimeout(() => { - reject('The local extension host took longer than 60s to connect.'); - }, 60 * 1000); - - portPromise.then((port) => { - clearTimeout(handle); - - const onMessage = new BufferedEmitter(); - port.onmessage = ((e) => onMessage.fire(VSBuffer.wrap(e.data))); - port.start(); - - resolve({ - onMessage: onMessage.event, - send: message => port.postMessage(message.buffer), - }); - }); - - // Now that the message port listener is installed, start the ext host process - const sw = StopWatch.create(false); - this._extensionHostProcess!.start(opts).then(() => { - const duration = sw.elapsed(); - if (platform.isCI) { - this._logService.info(`IExtensionHostStarter.start() took ${duration} ms.`); - } - }, (err) => { - // Starting the ext host process resulted in an error - reject(err); - }); - - }).then((protocol) => { - return this._performHandshake(protocol); - }); - } - - private _performNamedPipeHandshake(namedPipeServer: Server, opts: IExtensionHostProcessOptions): Promise { - - return new Promise((resolve, reject) => { - - // Wait for the extension host to connect to our named pipe - // and wrap the socket in the message passing protocol - const handle = setTimeout(() => { - if (namedPipeServer.listening) { - namedPipeServer.close(); - } - reject('The local extension host took longer than 60s to connect.'); - }, 60 * 1000); - - namedPipeServer.on('connection', socket => { - - clearTimeout(handle); - if (namedPipeServer.listening) { - namedPipeServer.close(); - } - - const nodeSocket = new NodeSocket(socket, 'renderer-exthost'); - const protocol = new PersistentProtocol(nodeSocket); - - this._toDispose.add(toDisposable(() => { - // Send the extension host a request to terminate itself - // (graceful termination) - protocol.send(createMessageOfType(MessageType.Terminate)); - protocol.flush(); - - socket.end(); - nodeSocket.dispose(); - protocol.dispose(); - })); - - resolve(protocol); - }); - - // Now that the named pipe listener is installed, start the ext host process - const sw = StopWatch.create(false); - this._extensionHostProcess!.start(opts).then(() => { - const duration = sw.elapsed(); - if (platform.isCI) { - this._logService.info(`IExtensionHostStarter.start() took ${duration} ms.`); - } - }, (err) => { - // Starting the ext host process resulted in an error - reject(err); - }); - - }).then((protocol) => { - return this._performHandshake(protocol); - }); - } - - private _performHandshake(protocol: IMessagePassingProtocol): Promise { + private _performHandshake(protocol: IMessagePassingProtocol): Promise { // 1) wait for the incoming `ready` event and send the initialization data. // 2) wait for the incoming `initialized` event. - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let timeoutHandle: NodeJS.Timer; const installTimeoutCheck = () => { @@ -575,7 +447,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { Registry.as(Extensions.OutputChannels).registerChannel({ id: 'extHostLog', label: nls.localize('extension host Log', "Extension Host"), file: this._extensionHostLogFile, log: true }); // release this promise - resolve(protocol); + resolve(); return; } @@ -731,3 +603,148 @@ export class LocalProcessExtensionHost implements IExtensionHost { } } } + +interface IExtHostCommunication { + prepare(): Promise; + establishProtocol(prepared: T, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise; +} + +class ExtHostMessagePortCommunication extends Disposable implements IExtHostCommunication { + + constructor( + @ILogService private readonly _logService: ILogService + ) { + super(); + } + + async prepare(): Promise { + } + + establishProtocol(prepared: void, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { + opts.env['VSCODE_WILL_SEND_MESSAGE_PORT'] = 'true'; + + // Get ready to acquire the message port from the shared process worker + const portPromise = acquirePort(undefined /* we trigger the request via service call! */, opts.responseChannel, opts.responseNonce); + + return new Promise((resolve, reject) => { + + const handle = setTimeout(() => { + reject('The local extension host took longer than 60s to connect.'); + }, 60 * 1000); + + portPromise.then((port) => { + clearTimeout(handle); + + const onMessage = new BufferedEmitter(); + port.onmessage = ((e) => onMessage.fire(VSBuffer.wrap(e.data))); + port.start(); + + resolve({ + onMessage: onMessage.event, + send: message => port.postMessage(message.buffer), + }); + }); + + // Now that the message port listener is installed, start the ext host process + const sw = StopWatch.create(false); + extensionHostProcess.start(opts).then(() => { + const duration = sw.elapsed(); + if (platform.isCI) { + this._logService.info(`IExtensionHostStarter.start() took ${duration} ms.`); + } + }, (err) => { + // Starting the ext host process resulted in an error + reject(err); + }); + }); + } +} + +interface INamedPipePreparedData { + pipeName: string; + namedPipeServer: Server; +} + +class ExtHostNamedPipeCommunication extends Disposable implements IExtHostCommunication { + + constructor( + @ILogService private readonly _logService: ILogService + ) { + super(); + } + + prepare(): Promise { + return new Promise<{ pipeName: string; namedPipeServer: Server }>((resolve, reject) => { + const pipeName = createRandomIPCHandle(); + + const namedPipeServer = createServer(); + namedPipeServer.on('error', reject); + namedPipeServer.listen(pipeName, () => { + if (namedPipeServer) { + namedPipeServer.removeListener('error', reject); + } + resolve({ pipeName, namedPipeServer }); + }); + this._register(toDisposable(() => { + if (namedPipeServer.listening) { + namedPipeServer.close(); + } + })); + }); + } + + establishProtocol(prepared: INamedPipePreparedData, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { + const { namedPipeServer, pipeName } = prepared; + + opts.env['VSCODE_IPC_HOOK_EXTHOST'] = pipeName; + + return new Promise((resolve, reject) => { + + // Wait for the extension host to connect to our named pipe + // and wrap the socket in the message passing protocol + const handle = setTimeout(() => { + if (namedPipeServer.listening) { + namedPipeServer.close(); + } + reject('The local extension host took longer than 60s to connect.'); + }, 60 * 1000); + + namedPipeServer.on('connection', (socket) => { + + clearTimeout(handle); + if (namedPipeServer.listening) { + namedPipeServer.close(); + } + + const nodeSocket = new NodeSocket(socket, 'renderer-exthost'); + const protocol = new PersistentProtocol(nodeSocket); + + this._register(toDisposable(() => { + // Send the extension host a request to terminate itself + // (graceful termination) + protocol.send(createMessageOfType(MessageType.Terminate)); + protocol.flush(); + + socket.end(); + nodeSocket.dispose(); + protocol.dispose(); + })); + + resolve(protocol); + }); + + // Now that the named pipe listener is installed, start the ext host process + const sw = StopWatch.create(false); + extensionHostProcess.start(opts).then(() => { + const duration = sw.elapsed(); + if (platform.isCI) { + this._logService.info(`IExtensionHostStarter.start() took ${duration} ms.`); + } + }, (err) => { + // Starting the ext host process resulted in an error + reject(err); + }); + + }); + } +} From 9322fd543d401efa136d24656c8be455edd7c010 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Tue, 24 May 2022 10:13:04 -0700 Subject: [PATCH 280/942] Fix test to pass --- .../src/singlefolder-tests/interactiveWindow.test.ts | 2 +- .../vscode-api-tests/src/singlefolder-tests/notebook.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts index eafc18804fdd0..0890418484cbe 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts @@ -32,7 +32,7 @@ async function createInteractiveWindow(kernel: Kernel) { setup(async function () { // there should be ONE default kernel in this suite - defaultKernel = new Kernel('mainKernel', 'Notebook Default Kernel'); + defaultKernel = new Kernel('mainKernel', 'Notebook Default Kernel', 'interactive'); testDisposables.push(defaultKernel.controller); await saveAllFilesAndCloseAll(); }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index b68eb58792207..8c413aaf9fb02 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -35,8 +35,8 @@ export class Kernel { readonly associatedNotebooks = new Set(); - constructor(id: string, label: string) { - this.controller = vscode.notebooks.createNotebookController(id, 'notebookCoreTest', label); + constructor(id: string, label: string, viewType: string = 'notebookCoreTest') { + this.controller = vscode.notebooks.createNotebookController(id, viewType, label); this.controller.executeHandler = this._execute.bind(this); this.controller.supportsExecutionOrder = true; this.controller.supportedLanguages = ['typescript', 'javascript']; From 1b88f28b495c90bb656bc3fb710a49d662074a3e Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 24 May 2022 10:32:27 -0700 Subject: [PATCH 281/942] pass in registry --- .../contrib/tasks/common/taskConfiguration.ts | 12 ++++++------ .../tasks/common/taskDefinitionRegistry.ts | 2 +- .../tasks/test/common/taskConfiguration.test.ts | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index 1fd6ca72d5bf5..7bf202d5c8ee5 100644 --- a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -20,7 +20,7 @@ import { import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; import * as Tasks from './tasks'; -import { TaskDefinitionRegistry } from './taskDefinitionRegistry'; +import { ITaskDefinitionRegistry, TaskDefinitionRegistry } from './taskDefinitionRegistry'; import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { URI } from 'vs/base/common/uri'; import { USER_TASKS_GROUP_KEY, ShellExecutionSupportedContext, ProcessExecutionSupportedContext } from 'vs/workbench/contrib/tasks/common/taskService'; @@ -1389,7 +1389,7 @@ namespace ConfiguringTask { customize: string; } - export function from(this: void, external: ConfiguringTask, context: ParseContext, index: number, source: TaskConfigSource, testTaskDefinition?: Tasks.TaskDefinition): Tasks.ConfiguringTask | undefined { + export function from(this: void, external: ConfiguringTask, context: ParseContext, index: number, source: TaskConfigSource, registry?: Partial): Tasks.ConfiguringTask | undefined { if (!external) { return undefined; } @@ -1399,7 +1399,7 @@ namespace ConfiguringTask { context.problemReporter.error(nls.localize('ConfigurationParser.noTaskType', 'Error: tasks configuration must have a type property. The configuration will be ignored.\n{0}\n', JSON.stringify(external, null, 4))); return undefined; } - let typeDeclaration = type ? testTaskDefinition || TaskDefinitionRegistry.get(type) : undefined; + let typeDeclaration = type ? registry?.get?.(type) || TaskDefinitionRegistry.get(type) : undefined; if (!typeDeclaration) { let message = nls.localize('ConfigurationParser.noTypeDefinition', 'Error: there is no registered task type \'{0}\'. Did you miss installing an extension that provides a corresponding task provider?', type); context.problemReporter.error(message); @@ -1672,7 +1672,7 @@ export namespace TaskParser { process: ProcessExecutionSupportedContext }; - export function from(this: void, externals: Array | undefined, globals: Globals, context: ParseContext, source: TaskConfigSource, testTaskDefinition?: Tasks.TaskDefinition): TaskParseResult { + export function from(this: void, externals: Array | undefined, globals: Globals, context: ParseContext, source: TaskConfigSource, registry?: Partial): TaskParseResult { let result: TaskParseResult = { custom: [], configured: [] }; if (!externals) { return result; @@ -1683,7 +1683,7 @@ export namespace TaskParser { const baseLoadIssues = Objects.deepClone(context.taskLoadIssues); for (let index = 0; index < externals.length; index++) { let external = externals[index]; - const definition = external.type ? testTaskDefinition || TaskDefinitionRegistry.get(external.type) : undefined; + const definition = external.type ? registry?.get?.(external.type) || TaskDefinitionRegistry.get(external.type) : undefined; let typeNotSupported: boolean = false; if (definition && definition.when && !context.contextKeyService.contextMatchesRules(definition.when)) { typeNotSupported = true; @@ -1743,7 +1743,7 @@ export namespace TaskParser { result.custom.push(customTask); } } else { - let configuredTask = ConfiguringTask.from(external, context, index, source, testTaskDefinition); + let configuredTask = ConfiguringTask.from(external, context, index, source, registry); if (configuredTask) { configuredTask.addTaskLoadMessages(context.taskLoadIssues); result.configured.push(configuredTask); diff --git a/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts b/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts index 8d612f6d9a6e3..340990cb62cd3 100644 --- a/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts +++ b/src/vs/workbench/contrib/tasks/common/taskDefinitionRegistry.ts @@ -99,7 +99,7 @@ export interface ITaskDefinitionRegistry { onDefinitionsChanged: Event; } -class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { +export class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { private taskTypes: IStringDictionary; private readyPromise: Promise; diff --git a/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts b/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts index 95fb3eb7b55e8..4db99d3955c6f 100644 --- a/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts @@ -19,6 +19,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { IContext } from 'vs/platform/contextkey/common/contextkey'; import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { ITaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry'; const workspaceFolder: WorkspaceFolder = new WorkspaceFolder({ uri: URI.file('/workspace/folderOne'), @@ -1772,7 +1773,19 @@ class TestNamedProblemMatcher implements Partial { class TestParseContext implements Partial { } +class TestTaskDefinitionRegistry implements Partial { + private _task: Tasks.TaskDefinition | undefined; + public get(key: string): Tasks.TaskDefinition { + return this._task!; + } + public set(task: Tasks.TaskDefinition) { + this._task = task; + } +} + suite('To task configuration from', () => { + + const TaskDefinitionRegistry = new TestTaskDefinitionRegistry(); let instantiationService: TestInstantiationService; let parseContext: ParseContext; let namedProblemMatcher: NamedProblemMatcher; @@ -1827,7 +1840,8 @@ suite('To task configuration from', () => { suite('ConfiguredTask', () => { test('returns expected result', () => { const expected = [{ taskName: 'task', command: 'echo test', type: 'any', label: 'task' }, { taskName: 'task 2', command: 'echo test', type: 'any', label: 'task 2' }]; - const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource, { extensionId: 'registered', taskType: 'any', properties: {} } as Tasks.TaskDefinition); + TaskDefinitionRegistry.set({ extensionId: 'registered', taskType: 'any', properties: {} } as Tasks.TaskDefinition); + const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource, TaskDefinitionRegistry); assertTaskParseResult(result, { configured: expected }, problemReporter, undefined); }); }); From 397fa12a6c86ea3e9ce3a44826bb9b638a3048d5 Mon Sep 17 00:00:00 2001 From: Song Xie Date: Tue, 24 May 2022 10:33:48 -0700 Subject: [PATCH 282/942] [easy] Fix a typo for "synchronizing" in log string (#150236) * [easy] Fix a typo for "synchronizing" in log string * Include artifacts from yarn build compile --- build/lib/builtInExtensions.js | 2 +- build/lib/builtInExtensions.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/lib/builtInExtensions.js b/build/lib/builtInExtensions.js index 221e5ba7516c8..2671a5a3a6cba 100644 --- a/build/lib/builtInExtensions.js +++ b/build/lib/builtInExtensions.js @@ -97,7 +97,7 @@ function writeControlFile(control) { fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2)); } function getBuiltInExtensions() { - log('Syncronizing built-in extensions...'); + log('Synchronizing built-in extensions...'); log(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`); const control = readControlFile(); const streams = []; diff --git a/build/lib/builtInExtensions.ts b/build/lib/builtInExtensions.ts index f5a03d1304ebf..1abc85b3b09c5 100644 --- a/build/lib/builtInExtensions.ts +++ b/build/lib/builtInExtensions.ts @@ -136,7 +136,7 @@ function writeControlFile(control: IControlFile): void { } export function getBuiltInExtensions(): Promise { - log('Syncronizing built-in extensions...'); + log('Synchronizing built-in extensions...'); log(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`); const control = readControlFile(); From b6aaee8c617f6861ddd355bab7d702264be9dc73 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 24 May 2022 10:39:21 -0700 Subject: [PATCH 283/942] remove a bunch of casts --- .../test/common/taskConfiguration.test.ts | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts b/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts index 4db99d3955c6f..bf496555a15ec 100644 --- a/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts @@ -1783,8 +1783,9 @@ class TestTaskDefinitionRegistry implements Partial { } } -suite('To task configuration from', () => { - +suite('Task configuration conversions', () => { + const globals = {} as Globals; + const taskConfigSource = {} as TaskConfigSource; const TaskDefinitionRegistry = new TestTaskDefinitionRegistry(); let instantiationService: TestInstantiationService; let parseContext: ParseContext; @@ -1801,7 +1802,7 @@ suite('To task configuration from', () => { parseContext.namedProblemMatchers = { 'real': namedProblemMatcher }; parseContext.uuidMap = new UUIDMap(); }); - suite('ProblemMatcher config', () => { + suite('ProblemMatcherConverter.from', () => { test('returns [] and an error for an unknown problem matcher', () => { const result = (ProblemMatcherConverter.from('$fake', parseContext)); assert.deepEqual(result.value, []); @@ -1819,21 +1820,24 @@ suite('To task configuration from', () => { assert.deepEqual(result.value, [{ "label": "real label", "applyTo": ApplyToKind.closedDocuments }]); }); }); - suite('TaskParser external config', () => { + suite('TaskParser.from', () => { suite('CustomTask', () => { suite('incomplete config reports an appropriate error for missing', () => { test('name', () => { - const result = TaskParser.from([{} as CustomTask], {} as Globals, parseContext, {} as TaskConfigSource); + const result = TaskParser.from([{} as CustomTask], globals, parseContext, taskConfigSource); assertTaskParseResult(result, undefined, problemReporter, 'Error: a task must provide a label property'); }); test('command', () => { - const result = TaskParser.from([{ taskName: 'task' } as CustomTask], {} as Globals, parseContext, {} as TaskConfigSource); + const result = TaskParser.from([{ taskName: 'task' } as CustomTask], globals, parseContext, taskConfigSource); assertTaskParseResult(result, undefined, problemReporter, "Error: the task 'task' doesn't define a command"); }); }); test('returns expected result', () => { - const expected = [{ taskName: 'task', command: 'echo test' } as CustomTask, { taskName: 'task 2', command: 'echo test' } as CustomTask]; - const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource); + const expected = [ + { taskName: 'task', command: 'echo test' } as CustomTask, + { taskName: 'task 2', command: 'echo test' } as CustomTask + ]; + const result = TaskParser.from(expected, globals, parseContext, taskConfigSource); assertTaskParseResult(result, { custom: expected }, problemReporter, undefined); }); }); @@ -1841,7 +1845,7 @@ suite('To task configuration from', () => { test('returns expected result', () => { const expected = [{ taskName: 'task', command: 'echo test', type: 'any', label: 'task' }, { taskName: 'task 2', command: 'echo test', type: 'any', label: 'task 2' }]; TaskDefinitionRegistry.set({ extensionId: 'registered', taskType: 'any', properties: {} } as Tasks.TaskDefinition); - const result = TaskParser.from(expected, {} as Globals, parseContext, {} as TaskConfigSource, TaskDefinitionRegistry); + const result = TaskParser.from(expected, globals, parseContext, taskConfigSource, TaskDefinitionRegistry); assertTaskParseResult(result, { configured: expected }, problemReporter, undefined); }); }); From bf6971ac461b2c5a99765e437f5019d7e7c19213 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 24 May 2022 19:43:27 +0200 Subject: [PATCH 284/942] Fix enablement check (#150297) --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 53c6abecef487..a738e3d7a21a8 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1864,7 +1864,7 @@ class SCMInputWidget extends Disposable { // Update input enablement const updateEnablement = (enabled: boolean) => { - this.inputEditor.updateOptions({ readOnly: enabled }); + this.inputEditor.updateOptions({ readOnly: !enabled }); }; this.repositoryDisposables.add(input.onDidChangeEnablement(enabled => updateEnablement(enabled))); updateEnablement(input.enabled); From fa120bbacde9c8018e78d79d299534f752e5e659 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 24 May 2022 19:46:45 +0200 Subject: [PATCH 285/942] Have the renderer side decide if it wants to use `UtilityProcess` --- .../extensions/common/extensionHostStarter.ts | 4 ++-- .../electron-main/extensionHostStarter.ts | 20 +++++++++++-------- .../localProcessExtensionHost.ts | 12 ++++++++--- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/vs/platform/extensions/common/extensionHostStarter.ts b/src/vs/platform/extensions/common/extensionHostStarter.ts index 33f3192f2061f..5d2a9e3a83a8a 100644 --- a/src/vs/platform/extensions/common/extensionHostStarter.ts +++ b/src/vs/platform/extensions/common/extensionHostStarter.ts @@ -30,8 +30,8 @@ export interface IExtensionHostStarter { onDynamicError(id: string): Event<{ error: SerializedError }>; onDynamicExit(id: string): Event<{ code: number; signal: string }>; - usesUtilityProcess(): Promise; - createExtensionHost(): Promise<{ id: string }>; + canUseUtilityProcess(): Promise; + createExtensionHost(useUtilityProcess: boolean): Promise<{ id: string }>; start(id: string, opts: IExtensionHostProcessOptions): Promise; enableInspectPort(id: string): Promise; kill(id: string): Promise; diff --git a/src/vs/platform/extensions/electron-main/extensionHostStarter.ts b/src/vs/platform/extensions/electron-main/extensionHostStarter.ts index 8ef9c5a0da7ab..1647d46f55b33 100644 --- a/src/vs/platform/extensions/electron-main/extensionHostStarter.ts +++ b/src/vs/platform/extensions/electron-main/extensionHostStarter.ts @@ -36,7 +36,7 @@ declare namespace UtilityProcessProposedApi { } } const UtilityProcess = ((electron as any).UtilityProcess); -const canUseUtilityProcess = (typeof UtilityProcess !== 'undefined') && !!process.env['VSCODE_USE_UTILITY_PROCESS']; +const canUseUtilityProcess = (typeof UtilityProcess !== 'undefined'); export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter { _serviceBrand: undefined; @@ -91,20 +91,24 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter return this._getExtHost(id).onExit; } - async usesUtilityProcess(): Promise { + async canUseUtilityProcess(): Promise { return canUseUtilityProcess; } - async createExtensionHost(): Promise<{ id: string }> { + async createExtensionHost(useUtilityProcess: boolean): Promise<{ id: string }> { if (this._shutdown) { throw canceled(); } const id = String(++ExtensionHostStarter._lastId); - const extHost = ( - canUseUtilityProcess - ? new UtilityExtensionHostProcess(id, this._logService) - : new ExtensionHostProcess(id, this._logService) - ); + let extHost: UtilityExtensionHostProcess | ExtensionHostProcess; + if (useUtilityProcess) { + if (!canUseUtilityProcess) { + throw new Error(`Cannot use UtilityProcess!`); + } + extHost = new UtilityExtensionHostProcess(id, this._logService); + } else { + extHost = new ExtensionHostProcess(id, this._logService); + } this._extHosts.set(id, extHost); extHost.onExit(({ pid, code, signal }) => { this._logService.info(`Extension host with pid ${pid} exited with code: ${code}, signal: ${signal}.`); diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index 8fdad2df0692a..c43ae39d09f7e 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -209,8 +209,8 @@ export class LocalProcessExtensionHost implements IExtensionHost { } private async _start(): Promise { - const usesUtilityProcess = await this._extensionHostStarter.usesUtilityProcess(); - if (usesUtilityProcess) { + const canUseUtilityProcess = await this._extensionHostStarter.canUseUtilityProcess(); + if (canUseUtilityProcess && process.env['VSCODE_USE_UTILITY_PROCESS']) { const communication = this._toDispose.add(new ExtHostMessagePortCommunication(this._logService)); return this._startWithCommunication(communication); } else { @@ -222,7 +222,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { private async _startWithCommunication(communication: IExtHostCommunication): Promise { const [extensionHostCreationResult, communicationPreparedData, portNumber, processEnv] = await Promise.all([ - this._extensionHostStarter.createExtensionHost(), + this._extensionHostStarter.createExtensionHost(communication.useUtilityProcess), communication.prepare(), this._tryFindDebugPort(), this._shellEnvironmentService.getShellEnv(), @@ -605,12 +605,15 @@ export class LocalProcessExtensionHost implements IExtensionHost { } interface IExtHostCommunication { + readonly useUtilityProcess: boolean; prepare(): Promise; establishProtocol(prepared: T, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise; } class ExtHostMessagePortCommunication extends Disposable implements IExtHostCommunication { + readonly useUtilityProcess = true; + constructor( @ILogService private readonly _logService: ILogService ) { @@ -621,6 +624,7 @@ class ExtHostMessagePortCommunication extends Disposable implements IExtHostComm } establishProtocol(prepared: void, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { + opts.env['VSCODE_WILL_SEND_MESSAGE_PORT'] = 'true'; // Get ready to acquire the message port from the shared process worker @@ -667,6 +671,8 @@ interface INamedPipePreparedData { class ExtHostNamedPipeCommunication extends Disposable implements IExtHostCommunication { + readonly useUtilityProcess = false; + constructor( @ILogService private readonly _logService: ILogService ) { From b79d02db5c11960188a43d8bfa12001ab84bb3b8 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Tue, 24 May 2022 11:01:51 -0700 Subject: [PATCH 286/942] Add test for scrolling --- .../interactiveWindow.test.ts | 37 +++++++++++++++---- .../src/singlefolder-tests/notebook.test.ts | 9 ++++- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts index 0890418484cbe..6880607ca7d77 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/interactiveWindow.test.ts @@ -24,6 +24,22 @@ async function createInteractiveWindow(kernel: Kernel) { return notebookEditor; } +async function addCell(code: string, notebook: vscode.NotebookDocument) { + const cell = new vscode.NotebookCellData(vscode.NotebookCellKind.Code, code, 'typescript'); + const edit = vscode.NotebookEdit.insertCells(notebook.cellCount, [cell]); + const workspaceEdit = new vscode.WorkspaceEdit(); + workspaceEdit.set(notebook.uri, [edit]); + await vscode.workspace.applyEdit(workspaceEdit); + return notebook.cellAt(notebook.cellCount - 1); +} + +async function addCellAndRun(code: string, notebook: vscode.NotebookDocument) { + const cell = await addCell(code, notebook); + await vscode.commands.executeCommand('notebook.execute'); + assert.strictEqual(cell.outputs.length, 1, 'execute failed'); + return cell; +} + (vscode.env.uiKind === vscode.UIKind.Web ? suite.skip : suite)('Interactive Window', function () { @@ -49,17 +65,24 @@ async function createInteractiveWindow(kernel: Kernel) { assert.ok(notebookEditor); // Try adding a cell and running it. - const cell = new vscode.NotebookCellData(vscode.NotebookCellKind.Code, 'print foo', 'typescript'); - const edit = vscode.NotebookEdit.insertCells(0, [cell]); - const workspaceEdit = new vscode.WorkspaceEdit(); - workspaceEdit.set(notebookEditor.notebook.uri, [edit]); - await vscode.workspace.applyEdit(workspaceEdit); + await addCell('print foo', notebookEditor.notebook); assert.strictEqual(notebookEditor.notebook.cellCount, 1); assert.strictEqual(notebookEditor.notebook.cellAt(0).kind, vscode.NotebookCellKind.Code); + }); + + test('Interactive window scrolls after execute', async () => { + assert.ok(vscode.workspace.workspaceFolders); + const notebookEditor = await createInteractiveWindow(defaultKernel); + assert.ok(notebookEditor); + + // Run and add a bunch of cells + for (let i = 0; i < 20; i++) { + await addCellAndRun(`print ${i}`, notebookEditor.notebook); + } - await vscode.commands.executeCommand('notebook.execute'); - assert.strictEqual(notebookEditor.notebook.cellAt(0).outputs.length, 1, 'should execute'); + // Verify visible range has the last cell + assert.strictEqual(notebookEditor.visibleRanges[notebookEditor.visibleRanges.length - 1].end, notebookEditor.notebook.cellCount, `Last cell is not visible`); }); }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 8c413aaf9fb02..33cad6edb978a 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -29,6 +29,12 @@ async function withEvent(event: vscode.Event, callback: (e: Promise) => } +function sleep(ms: number): Promise { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); +} + export class Kernel { readonly controller: vscode.NotebookController; @@ -59,8 +65,9 @@ export class Kernel { // create a single output with exec order 1 and output is plain/text // of either the cell itself or (iff empty) the cell's document's uri const task = this.controller.createNotebookCellExecution(cell); - task.start(); + task.start(Date.now()); task.executionOrder = 1; + await sleep(10); // Force to be take some time await task.replaceOutput([new vscode.NotebookCellOutput([ vscode.NotebookCellOutputItem.text(cell.document.getText() || cell.document.uri.toString(), 'text/plain') ])]); From f9b9f42034dd39fc473ea27003afdb57e5588c03 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 24 May 2022 11:14:12 -0700 Subject: [PATCH 287/942] update layout troubleshooting --- .../browser/contrib/troubleshoot/layout.ts | 64 ++++++++++++++----- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout.ts b/src/vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout.ts index 29d4e867f651d..b3af414374166 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout.ts @@ -7,9 +7,10 @@ import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/commo import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { CATEGORIES } from 'vs/workbench/common/actions'; -import { getNotebookEditorFromEditorPane, ICellViewModel, ICommonCellViewModelLayoutChangeInfo, INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { getNotebookEditorFromEditorPane, ICellViewModel, ICommonCellViewModelLayoutChangeInfo, INotebookDeltaCellStatusBarItems, INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { CellStatusbarAlignment, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -18,31 +19,37 @@ export class TroubleshootController extends Disposable implements INotebookEdito private readonly _localStore = this._register(new DisposableStore()); private _cellStateListeners: IDisposable[] = []; - private _logging: boolean = false; + private _enabled: boolean = false; + private _cellStatusItems: string[] = []; constructor(private readonly _notebookEditor: INotebookEditor) { super(); this._register(this._notebookEditor.onDidChangeModel(() => { - this._localStore.clear(); - this._cellStateListeners.forEach(listener => listener.dispose()); - - if (!this._notebookEditor.hasModel()) { - return; - } - - this._updateListener(); + this._update(); })); - this._updateListener(); + this._update(); } - toggleLogging(): void { - this._logging = !this._logging; + toggle(): void { + this._enabled = !this._enabled; + this._update(); + } + + private _update() { + this._localStore.clear(); + this._cellStateListeners.forEach(listener => listener.dispose()); + + if (!this._notebookEditor.hasModel()) { + return; + } + + this._updateListener(); } private _log(cell: ICellViewModel, e: any) { - if (this._logging) { + if (this._enabled) { const oldHeight = (this._notebookEditor as NotebookEditorWidget).getViewHeight(cell); console.log(`cell#${cell.handle}`, e, `${oldHeight} -> ${cell.layoutInfo.totalHeight}`); } @@ -73,6 +80,33 @@ export class TroubleshootController extends Disposable implements INotebookEdito dispose(deletedCells); }); })); + + const vm = this._notebookEditor._getViewModel(); + let items: INotebookDeltaCellStatusBarItems[] = []; + + if (this._enabled) { + items = this._getItemsForCells(); + } + + this._cellStatusItems = vm.deltaCellStatusBarItems(this._cellStatusItems, items); + } + + private _getItemsForCells(): INotebookDeltaCellStatusBarItems[] { + const items: INotebookDeltaCellStatusBarItems[] = []; + for (let i = 0; i < this._notebookEditor.getLength(); i++) { + items.push({ + handle: i, + items: [ + { + text: `index: ${i}`, + alignment: CellStatusbarAlignment.Left, + priority: Number.MAX_SAFE_INTEGER + } + ] + }); + } + + return items; } override dispose() { @@ -102,7 +136,7 @@ registerAction2(class extends Action2 { } const controller = editor.getContribution(TroubleshootController.id); - controller?.toggleLogging(); + controller?.toggle(); } }); From 98fdef2b5e18193dccab264da977f6d2988c4804 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 24 May 2022 11:15:46 -0700 Subject: [PATCH 288/942] make sure page up and down goes to next page. --- src/vs/base/browser/ui/list/listWidget.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 5508222146725..87a271fc02fe4 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1598,9 +1598,10 @@ export class List implements ISpliceable, IThemable, IDisposable { let lastPageIndex = this.view.indexAt(this.view.getScrollTop() + this.view.renderHeight); lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1; const lastPageElement = this.view.element(lastPageIndex); - const currentlyFocusedElement = this.getFocusedElements()[0]; + const currentlyFocusedElementIndex = this.getFocus()[0]; + const currentlyFocusedElement = this.view.element(currentlyFocusedElementIndex); - if (currentlyFocusedElement !== lastPageElement) { + if (currentlyFocusedElement !== lastPageElement && lastPageIndex > currentlyFocusedElementIndex) { const lastGoodPageIndex = this.findPreviousIndex(lastPageIndex, false, filter); if (lastGoodPageIndex > -1 && currentlyFocusedElement !== this.view.element(lastGoodPageIndex)) { @@ -1610,7 +1611,13 @@ export class List implements ISpliceable, IThemable, IDisposable { } } else { const previousScrollTop = this.view.getScrollTop(); - this.view.setScrollTop(previousScrollTop + this.view.renderHeight - this.view.elementHeight(lastPageIndex)); + let nextpageScrollTop = previousScrollTop + this.view.renderHeight; + if (lastPageIndex > currentlyFocusedElementIndex) { + // scroll last page element to the top only if the last page element is below the focused element + nextpageScrollTop -= this.view.elementHeight(lastPageIndex); + } + + this.view.setScrollTop(nextpageScrollTop); if (this.view.getScrollTop() !== previousScrollTop) { this.setFocus([]); @@ -1633,9 +1640,10 @@ export class List implements ISpliceable, IThemable, IDisposable { } const firstPageElement = this.view.element(firstPageIndex); - const currentlyFocusedElement = this.getFocusedElements()[0]; + const currentlyFocusedElementIndex = this.getFocus()[0]; + const currentlyFocusedElement = this.view.element(currentlyFocusedElementIndex); - if (currentlyFocusedElement !== firstPageElement) { + if (currentlyFocusedElement !== firstPageElement && currentlyFocusedElementIndex >= firstPageIndex) { const firstGoodPageIndex = this.findNextIndex(firstPageIndex, false, filter); if (firstGoodPageIndex > -1 && currentlyFocusedElement !== this.view.element(firstGoodPageIndex)) { From 21e64f84458090e52aede145202c9940ae21172c Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Tue, 24 May 2022 11:24:44 -0700 Subject: [PATCH 289/942] Remove unnecessary changes --- .../browser/view/renderers/webviewPreloads.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 5aff1145fd87e..bdaa36e08b311 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -1829,8 +1829,6 @@ async function webviewPreloads(ctx: PreloadContext) { class OutputCell { public readonly element: HTMLElement; - public readonly bottomElement: HTMLElement; - private readonly outputElements = new Map(); constructor(cellId: string) { @@ -1849,14 +1847,8 @@ async function webviewPreloads(ctx: PreloadContext) { this.element = this.element; - this.bottomElement = createFocusSink(cellId, true); - container.appendChild(this.bottomElement); - - // New thoughts. - // Send message to indicate force scroll. - // Add resizeObserver on the element - // When resize occurs, scroll lowerWrapperElement into view - // Send message to indicate stop scrolling + const lowerWrapperElement = createFocusSink(cellId, true); + container.appendChild(lowerWrapperElement); } public createOutputElement(outputId: string, outputOffset: number, left: number): OutputElement { From 7baaf796b0a03571ed793325b37e36ea49152b35 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 24 May 2022 20:29:14 +0200 Subject: [PATCH 290/942] Extract `NativeLocalProcessExtensionHost` --- .../electron-browser/extensionService.ts | 5 +- .../nativeLocalProcessExtensionHost.ts | 121 +++++++++++++++++ .../localProcessExtensionHost.ts | 128 ++---------------- 3 files changed, 138 insertions(+), 116 deletions(-) create mode 100644 src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts rename src/vs/workbench/services/extensions/{electron-browser => electron-sandbox}/localProcessExtensionHost.ts (85%) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 388033c90a15e..548ae5b4b16f1 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ILocalProcessExtensionHostDataProvider, ILocalProcessExtensionHostInitData, LocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-browser/localProcessExtensionHost'; +import { NativeLocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost'; import { CachedExtensionScanner } from 'vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -50,6 +50,7 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import { isCI } from 'vs/base/common/platform'; import { IResolveAuthorityErrorResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; import { URI } from 'vs/base/common/uri'; +import { ILocalProcessExtensionHostDataProvider, ILocalProcessExtensionHostInitData } from 'vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -240,7 +241,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten protected _createExtensionHost(runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null { switch (runningLocation.kind) { case ExtensionHostKind.LocalProcess: { - return this._instantiationService.createInstance(LocalProcessExtensionHost, runningLocation, this._createLocalExtensionHostDataProvider(isInitialStart, runningLocation)); + return this._instantiationService.createInstance(NativeLocalProcessExtensionHost, runningLocation, this._createLocalExtensionHostDataProvider(isInitialStart, runningLocation)); } case ExtensionHostKind.LocalWebWorker: { if (this._enableLocalWebWorker) { diff --git a/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts new file mode 100644 index 0000000000000..d498ddd79607f --- /dev/null +++ b/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts @@ -0,0 +1,121 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createServer, Server } from 'net'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; +import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; +import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import { IExtensionHostProcessOptions } from 'vs/platform/extensions/common/extensionHostStarter'; +import { ILogService } from 'vs/platform/log/common/log'; +import { createMessageOfType, MessageType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { ExtensionHostProcess, ExtHostMessagePortCommunication, IExtHostCommunication, SandboxLocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost'; + +export class NativeLocalProcessExtensionHost extends SandboxLocalProcessExtensionHost { + protected override async _start(): Promise { + const canUseUtilityProcess = await this._extensionHostStarter.canUseUtilityProcess(); + if (canUseUtilityProcess && process.env['VSCODE_USE_UTILITY_PROCESS']) { + const communication = this._toDispose.add(new ExtHostMessagePortCommunication(this._logService)); + return this._startWithCommunication(communication); + } else { + const communication = this._toDispose.add(new ExtHostNamedPipeCommunication(this._logService)); + return this._startWithCommunication(communication); + } + } +} + +interface INamedPipePreparedData { + pipeName: string; + namedPipeServer: Server; +} + +class ExtHostNamedPipeCommunication extends Disposable implements IExtHostCommunication { + + readonly useUtilityProcess = false; + + constructor( + @ILogService private readonly _logService: ILogService + ) { + super(); + } + + prepare(): Promise { + return new Promise<{ pipeName: string; namedPipeServer: Server }>((resolve, reject) => { + const pipeName = createRandomIPCHandle(); + + const namedPipeServer = createServer(); + namedPipeServer.on('error', reject); + namedPipeServer.listen(pipeName, () => { + if (namedPipeServer) { + namedPipeServer.removeListener('error', reject); + } + resolve({ pipeName, namedPipeServer }); + }); + this._register(toDisposable(() => { + if (namedPipeServer.listening) { + namedPipeServer.close(); + } + })); + }); + } + + establishProtocol(prepared: INamedPipePreparedData, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { + const { namedPipeServer, pipeName } = prepared; + + opts.env['VSCODE_IPC_HOOK_EXTHOST'] = pipeName; + + return new Promise((resolve, reject) => { + + // Wait for the extension host to connect to our named pipe + // and wrap the socket in the message passing protocol + const handle = setTimeout(() => { + if (namedPipeServer.listening) { + namedPipeServer.close(); + } + reject('The local extension host took longer than 60s to connect.'); + }, 60 * 1000); + + namedPipeServer.on('connection', (socket) => { + + clearTimeout(handle); + if (namedPipeServer.listening) { + namedPipeServer.close(); + } + + const nodeSocket = new NodeSocket(socket, 'renderer-exthost'); + const protocol = new PersistentProtocol(nodeSocket); + + this._register(toDisposable(() => { + // Send the extension host a request to terminate itself + // (graceful termination) + protocol.send(createMessageOfType(MessageType.Terminate)); + protocol.flush(); + + socket.end(); + nodeSocket.dispose(); + protocol.dispose(); + })); + + resolve(protocol); + }); + + // Now that the named pipe listener is installed, start the ext host process + const sw = StopWatch.create(false); + extensionHostProcess.start(opts).then(() => { + const duration = sw.elapsed(); + if (platform.isCI) { + this._logService.info(`IExtensionHostStarter.start() took ${duration} ms.`); + } + }, (err) => { + // Starting the ext host process resulted in an error + reject(err); + }); + + }); + } +} diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts similarity index 85% rename from src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts rename to src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts index c43ae39d09f7e..800bf0576aeb4 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts @@ -3,21 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Server, createServer } from 'net'; -import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; - import * as nls from 'vs/nls'; import { timeout } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { IRemoteConsoleLog, log } from 'vs/base/common/console'; import { logRemoteEntry, logRemoteEntryIfError } from 'vs/workbench/services/extensions/common/remoteConsoleUtil'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; -import { BufferedEmitter, PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; +import { BufferedEmitter } from 'vs/base/parts/ipc/common/ipc.net'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILifecycleService, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -27,7 +24,7 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { isUntitledWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { MessageType, createMessageOfType, isMessageOfType, IExtensionHostInitData, UIKind } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { MessageType, isMessageOfType, IExtensionHostInitData, UIKind } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { withNullAsUndefined } from 'vs/base/common/types'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { parseExtensionDevOptions } from '../common/extensionDevOptions'; @@ -62,7 +59,7 @@ const enum NativeLogMarkers { End = 'END_NATIVE_LOG', } -class ExtensionHostProcess { +export class ExtensionHostProcess { private readonly _id: string; @@ -106,7 +103,7 @@ class ExtensionHostProcess { } } -export class LocalProcessExtensionHost implements IExtensionHost { +export class SandboxLocalProcessExtensionHost implements IExtensionHost { public readonly remoteAuthority = null; public readonly lazyStart = false; @@ -117,7 +114,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { private readonly _onDidSetInspectPort = new Emitter(); - private readonly _toDispose = new DisposableStore(); + protected readonly _toDispose = new DisposableStore(); private readonly _isExtensionDevHost: boolean; private readonly _isExtensionDevDebug: boolean; @@ -144,13 +141,13 @@ export class LocalProcessExtensionHost implements IExtensionHost { @ILifecycleService private readonly _lifecycleService: ILifecycleService, @INativeWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService, @ITelemetryService private readonly _telemetryService: ITelemetryService, - @ILogService private readonly _logService: ILogService, + @ILogService protected readonly _logService: ILogService, @ILabelService private readonly _labelService: ILabelService, @IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService, @IHostService private readonly _hostService: IHostService, @IProductService private readonly _productService: IProductService, @IShellEnvironmentService private readonly _shellEnvironmentService: IShellEnvironmentService, - @IExtensionHostStarter private readonly _extensionHostStarter: IExtensionHostStarter, + @IExtensionHostStarter protected readonly _extensionHostStarter: IExtensionHostStarter, ) { const devOpts = parseExtensionDevOptions(this._environmentService); this._isExtensionDevHost = devOpts.isExtensionDevHost; @@ -208,18 +205,12 @@ export class LocalProcessExtensionHost implements IExtensionHost { return this._messageProtocol; } - private async _start(): Promise { - const canUseUtilityProcess = await this._extensionHostStarter.canUseUtilityProcess(); - if (canUseUtilityProcess && process.env['VSCODE_USE_UTILITY_PROCESS']) { - const communication = this._toDispose.add(new ExtHostMessagePortCommunication(this._logService)); - return this._startWithCommunication(communication); - } else { - const communication = this._toDispose.add(new ExtHostNamedPipeCommunication(this._logService)); - return this._startWithCommunication(communication); - } + protected async _start(): Promise { + const communication = this._toDispose.add(new ExtHostMessagePortCommunication(this._logService)); + return this._startWithCommunication(communication); } - private async _startWithCommunication(communication: IExtHostCommunication): Promise { + protected async _startWithCommunication(communication: IExtHostCommunication): Promise { const [extensionHostCreationResult, communicationPreparedData, portNumber, processEnv] = await Promise.all([ this._extensionHostStarter.createExtensionHost(communication.useUtilityProcess), @@ -604,13 +595,13 @@ export class LocalProcessExtensionHost implements IExtensionHost { } } -interface IExtHostCommunication { +export interface IExtHostCommunication { readonly useUtilityProcess: boolean; prepare(): Promise; establishProtocol(prepared: T, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise; } -class ExtHostMessagePortCommunication extends Disposable implements IExtHostCommunication { +export class ExtHostMessagePortCommunication extends Disposable implements IExtHostCommunication { readonly useUtilityProcess = true; @@ -663,94 +654,3 @@ class ExtHostMessagePortCommunication extends Disposable implements IExtHostComm }); } } - -interface INamedPipePreparedData { - pipeName: string; - namedPipeServer: Server; -} - -class ExtHostNamedPipeCommunication extends Disposable implements IExtHostCommunication { - - readonly useUtilityProcess = false; - - constructor( - @ILogService private readonly _logService: ILogService - ) { - super(); - } - - prepare(): Promise { - return new Promise<{ pipeName: string; namedPipeServer: Server }>((resolve, reject) => { - const pipeName = createRandomIPCHandle(); - - const namedPipeServer = createServer(); - namedPipeServer.on('error', reject); - namedPipeServer.listen(pipeName, () => { - if (namedPipeServer) { - namedPipeServer.removeListener('error', reject); - } - resolve({ pipeName, namedPipeServer }); - }); - this._register(toDisposable(() => { - if (namedPipeServer.listening) { - namedPipeServer.close(); - } - })); - }); - } - - establishProtocol(prepared: INamedPipePreparedData, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { - const { namedPipeServer, pipeName } = prepared; - - opts.env['VSCODE_IPC_HOOK_EXTHOST'] = pipeName; - - return new Promise((resolve, reject) => { - - // Wait for the extension host to connect to our named pipe - // and wrap the socket in the message passing protocol - const handle = setTimeout(() => { - if (namedPipeServer.listening) { - namedPipeServer.close(); - } - reject('The local extension host took longer than 60s to connect.'); - }, 60 * 1000); - - namedPipeServer.on('connection', (socket) => { - - clearTimeout(handle); - if (namedPipeServer.listening) { - namedPipeServer.close(); - } - - const nodeSocket = new NodeSocket(socket, 'renderer-exthost'); - const protocol = new PersistentProtocol(nodeSocket); - - this._register(toDisposable(() => { - // Send the extension host a request to terminate itself - // (graceful termination) - protocol.send(createMessageOfType(MessageType.Terminate)); - protocol.flush(); - - socket.end(); - nodeSocket.dispose(); - protocol.dispose(); - })); - - resolve(protocol); - }); - - // Now that the named pipe listener is installed, start the ext host process - const sw = StopWatch.create(false); - extensionHostProcess.start(opts).then(() => { - const duration = sw.elapsed(); - if (platform.isCI) { - this._logService.info(`IExtensionHostStarter.start() took ${duration} ms.`); - } - }, (err) => { - // Starting the ext host process resulted in an error - reject(err); - }); - - }); - } -} From a88075a8472184b6507b39dc1cd872eb6bfcef46 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 24 May 2022 20:42:47 +0200 Subject: [PATCH 291/942] show migrate action in extensions viewlet (#150301) --- src/vs/workbench/contrib/extensions/browser/extensionsList.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 9b5c1397ee459..d61e4518b9bf1 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -13,7 +13,7 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { Event } from 'vs/base/common/event'; import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; -import { UpdateAction, ManageExtensionAction, ReloadAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { UpdateAction, ManageExtensionAction, ReloadAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtension } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget, ExtensionHoverWidget, ExtensionActivationStatusWidget, PreReleaseBookmarkWidget, extensionVerifiedPublisherIconColor } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; @@ -118,6 +118,7 @@ export class Renderer implements IPagedRenderer { const reloadAction = this.instantiationService.createInstance(ReloadAction); const actions = [ this.instantiationService.createInstance(ExtensionStatusLabelAction), + this.instantiationService.createInstance(MigrateDeprecatedExtension, true), this.instantiationService.createInstance(UpdateAction), reloadAction, this.instantiationService.createInstance(InstallDropdownAction), From acc1f9c29ba3c16214af1e77832454e25d66afcd Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Tue, 24 May 2022 11:48:40 -0700 Subject: [PATCH 292/942] Remove empty line --- .../contrib/notebook/browser/view/renderers/webviewPreloads.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index bdaa36e08b311..441f75067b90d 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -1846,7 +1846,6 @@ async function webviewPreloads(ctx: PreloadContext) { container.appendChild(this.element); this.element = this.element; - const lowerWrapperElement = createFocusSink(cellId, true); container.appendChild(lowerWrapperElement); } From d9373893583a1b78df346f4671bc24221d5e5aba Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 24 May 2022 14:58:08 -0400 Subject: [PATCH 293/942] Add owners to all telemetry events (#150296) --- src/vs/base/common/actions.ts | 1 + src/vs/code/electron-main/app.ts | 2 +- src/vs/editor/browser/editorExtensions.ts | 1 + .../contrib/suggest/browser/suggestController.ts | 10 +++++----- .../common/extensionGalleryService.ts | 3 +++ .../electron-sandbox/extensionTipsService.ts | 1 + .../files/browser/indexedDBFileSystemProvider.ts | 1 + src/vs/platform/telemetry/common/telemetryUtils.ts | 6 ++++-- .../terminal/common/xterm/shellIntegrationAddon.ts | 4 ++-- .../update/electron-main/abstractUpdateService.ts | 1 + .../update/electron-main/updateService.darwin.ts | 1 + .../userDataSync/common/abstractSynchronizer.ts | 1 + .../userDataSync/common/userDataAutoSyncService.ts | 4 +++- .../common/userDataSyncEnablementService.ts | 1 + .../userDataSync/common/userDataSyncService.ts | 1 + src/vs/server/node/remoteExtensionHostAgentServer.ts | 8 ++++---- .../workbench/api/common/extHostExtensionService.ts | 5 ++++- .../workbench/api/common/extHostRequireInterceptor.ts | 3 +++ src/vs/workbench/api/common/extHostSCM.ts | 5 ++++- .../browser/parts/activitybar/activitybarActions.ts | 1 + .../workbench/browser/parts/editor/editorCommands.ts | 1 + src/vs/workbench/browser/parts/views/viewPane.ts | 1 + .../browser/parts/views/viewPaneContainer.ts | 1 + .../contrib/experiments/common/experimentService.ts | 1 + .../browser/dynamicWorkspaceRecommendations.ts | 1 + .../extensionRecommendationNotificationService.ts | 2 ++ .../browser/extensionRecommendationsService.ts | 1 + .../contrib/extensions/browser/extensionsViews.ts | 1 + .../extensions/browser/extensionsWorkbenchService.ts | 1 + .../extensions/browser/fileBasedRecommendations.ts | 1 + .../contrib/notebook/browser/notebookEditor.ts | 1 + .../contrib/notebook/browser/notebookEditorWidget.ts | 1 + .../contrib/preferences/browser/keybindingsEditor.ts | 1 + .../searchEditor/browser/searchEditorActions.ts | 4 ++-- .../contrib/searchEditor/browser/searchEditorInput.ts | 2 +- .../contrib/tags/electron-sandbox/workspaceTags.ts | 2 +- .../telemetry/browser/telemetry.contribution.ts | 11 +++++++++-- .../contrib/terminal/browser/terminalInstance.ts | 2 +- .../terminal/browser/terminalProcessManager.ts | 2 +- .../contrib/userDataSync/browser/userDataSync.ts | 1 + .../electron-sandbox/accessibilityService.ts | 1 + .../extensions/browser/extensionUrlHandler.ts | 1 + .../browser/languageDetectionWorkerServiceImpl.ts | 2 ++ .../preferences/browser/preferencesService.ts | 1 + .../workbench/services/search/common/searchService.ts | 3 +++ .../services/themes/browser/workbenchThemeService.ts | 1 + .../browser/userDataSyncWorkbenchService.ts | 4 +++- 47 files changed, 85 insertions(+), 26 deletions(-) diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index 2fe5d37c03f31..f9729a187709f 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -14,6 +14,7 @@ export interface ITelemetryData { } export type WorkbenchActionExecutedClassification = { + owner: 'bpasero'; id: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; from: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 2d7e71bef347b..eb752810e1cea 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -1105,7 +1105,7 @@ export class CodeApplication extends Disposable { code: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The type of shared process crash to understand the nature of the crash better.' }; visible: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Whether shared process window was visible or not.' }; shuttingdown: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Whether the application is shutting down when the crash happens.' }; - owner: 'bpaser'; + owner: 'bpasero'; comment: 'Event which fires whenever an error occurs in the shared process'; }; diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 50766a815c248..93b20f1d2edcf 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -338,6 +338,7 @@ export abstract class EditorAction extends EditorCommand { protected reportTelemetry(accessor: ServicesAccessor, editor: ICodeEditor) { type EditorActionInvokedClassification = { + owner: 'alexdima'; name: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; id: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index 6c66d2a31eae3..56a882e650c52 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -451,11 +451,11 @@ export class SuggestController implements IEditorContribution { type AcceptedSuggestionClassification = { owner: 'jrieken'; comment: 'Information accepting completion items'; - providerId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comments: 'Provider of the completions item' }; - basenameHash: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comments: 'Hash of the basename of the file into which the completion was inserted' }; - fileExtension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comments: 'File extension of the file into which the completion was inserted' }; - languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comments: 'Language type of the file into which the completion was inserted' }; - kind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comments: 'The completion item kind' }; + providerId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'Provider of the completions item' }; + basenameHash: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'Hash of the basename of the file into which the completion was inserted' }; + fileExtension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File extension of the file into which the completion was inserted' }; + languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Language type of the file into which the completion was inserted' }; + kind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The completion item kind' }; }; // _debugDisplayName looks like `vscode.css-language-features(/-:)`, where the last bit is the trigger chars // normalize it to just the extension ID and lowercase diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 3f563b19e404b..ad990114c43bf 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -235,6 +235,7 @@ const DefaultQueryState: IQueryState = { }; type GalleryServiceQueryClassification = { + owner: 'sandy081'; readonly filterTypes: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; readonly flags: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; readonly sortBy: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; @@ -272,6 +273,7 @@ type GalleryServiceQueryEvent = QueryTelemetryData & { }; type GalleryServiceAdditionalQueryClassification = { + owner: 'sandy081'; readonly duration: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; 'isMeasurement': true }; readonly count: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; @@ -1124,6 +1126,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi const message = getErrorMessage(err); type GalleryServiceCDNFallbackClassification = { + owner: 'sandy081'; url: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; message: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; diff --git a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts index f0a1419887846..91cda7e7d16c3 100644 --- a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts +++ b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts @@ -27,6 +27,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; type ExeExtensionRecommendationsClassification = { + owner: 'sandy081'; extensionId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; exeName: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; }; diff --git a/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts b/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts index ee053cda77a15..6436f0a4d4b10 100644 --- a/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts +++ b/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts @@ -16,6 +16,7 @@ import { createFileSystemProviderError, FileChangeType, IFileDeleteOptions, IFil import { DBClosedError, IndexedDB } from 'vs/base/browser/indexedDB'; export type IndexedDBFileSystemProviderErrorDataClassification = { + owner: 'sandy081'; readonly scheme: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; readonly operation: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; readonly code: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index 5a30a5743de37..859af104212c2 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -84,8 +84,10 @@ export function configurationTelemetry(telemetryService: ITelemetryService, conf return configurationService.onDidChangeConfiguration(event => { if (event.source !== ConfigurationTarget.DEFAULT) { type UpdateConfigurationClassification = { - configurationSource: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; - configurationKeys: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; + owner: 'lramos15, sbatten'; + comment: 'Event which fires when user updates telemetry configuration'; + configurationSource: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What configuration file was updated i.e user or workspace' }; + configurationKeys: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What configuration keys were updated' }; }; type UpdateConfigurationEvent = { configurationSource: string; diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index df36e27041d8f..0c02dfbf90802 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -144,7 +144,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private _handleVSCodeSequence(data: string): boolean { const didHandle = this._doHandleVSCodeSequence(data); if (!this._hasUpdatedTelemetry && didHandle) { - this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivationSucceeded'); + this._telemetryService?.publicLog2<{}, { owner: 'meganrogge' }>('terminal/shellIntegrationActivationSucceeded'); this._hasUpdatedTelemetry = true; if (this._activationTimeout !== undefined) { clearTimeout(this._activationTimeout); @@ -232,7 +232,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private async _ensureCapabilitiesOrAddFailureTelemetry(): Promise { this._activationTimeout = setTimeout(() => { if (!this.capabilities.get(TerminalCapability.CommandDetection) && !this.capabilities.get(TerminalCapability.CwdDetection)) { - this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivationTimeout'); + this._telemetryService?.publicLog2<{}, { owner: 'meganrogge' }>('terminal/shellIntegrationActivationTimeout'); this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); } this._hasUpdatedTelemetry = true; diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index 42b9e8cee7a43..8d777b69a7e50 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -19,6 +19,7 @@ export function createUpdateURL(platform: string, quality: string, productServic } export type UpdateNotAvailableClassification = { + owner: 'joaomoreno'; explicit: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; }; diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index 0c008ea850493..df08a4b796677 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -92,6 +92,7 @@ export class DarwinUpdateService extends AbstractUpdateService { } type UpdateDownloadedClassification = { + owner: 'joaomoreno'; version: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; this.telemetryService.publicLog2<{ version: String }, UpdateDownloadedClassification>('update:downloaded', { version: update.version }); diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index 932841567c669..74d549a5beeea 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -29,6 +29,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { Change, getLastSyncResourceUri, IRemoteUserData, IResourcePreview as IBaseResourcePreview, ISyncData, ISyncResourceHandle, ISyncResourcePreview as IBaseSyncResourcePreview, IUserData, IUserDataInitializer, IUserDataManifest, IUserDataSyncBackupStoreService, IUserDataSyncConfiguration, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, IUserDataSyncUtilService, MergeState, PREVIEW_DIR_NAME, SyncResource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_CONFIGURATION_SCOPE, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync'; type SyncSourceClassification = { + owner: 'sandy081'; source?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; }; diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts index bea7313f0b37b..108017e834bdd 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts @@ -22,10 +22,12 @@ import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/use import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; type AutoSyncClassification = { + owner: 'sandy081'; sources: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; }; type AutoSyncErrorClassification = { + owner: 'sandy081'; code: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; service: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; }; @@ -194,7 +196,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto // Reset if (everywhere) { - this.telemetryService.publicLog2('sync/turnOffEveryWhere'); + this.telemetryService.publicLog2<{}, { owner: 'sandy081' }>('sync/turnOffEveryWhere'); await this.userDataSyncService.reset(); } else { await this.userDataSyncService.resetLocal(); diff --git a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts b/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts index 8e237a29063d9..9538b66d37364 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts @@ -12,6 +12,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ALL_SYNC_RESOURCES, getEnablementKey, IUserDataSyncEnablementService, IUserDataSyncStoreManagementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; type SyncEnablementClassification = { + owner: 'sandy081'; enabled?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; }; diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index f3dc3c82982b1..ff26c4d2b880f 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -28,6 +28,7 @@ import { TasksSynchroniser } from 'vs/platform/userDataSync/common/tasksSync'; import { ALL_SYNC_RESOURCES, Change, createSyncHeaders, IManualSyncTask, IResourcePreview, ISyncResourceHandle, ISyncResourcePreview, ISyncTask, IUserDataManifest, IUserDataSyncConfiguration, IUserDataSyncEnablementService, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncService, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, MergeState, SyncResource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, UserDataSyncStoreError, USER_DATA_SYNC_CONFIGURATION_SCOPE } from 'vs/platform/userDataSync/common/userDataSync'; type SyncErrorClassification = { + owner: 'sandy081'; code: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; service: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; serverCode?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; diff --git a/src/vs/server/node/remoteExtensionHostAgentServer.ts b/src/vs/server/node/remoteExtensionHostAgentServer.ts index 3ae68711aa16a..09aafc649a78d 100644 --- a/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -757,10 +757,10 @@ export async function createServer(address: string | net.AddressInfo | null, arg type ServerStartClassification = { owner: 'alexdima'; comment: 'The server has started up'; - startTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; - startedTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; - codeLoadedTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; - readyTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; + startTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The time the server started at.' }; + startedTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The time the server began listening for connections.' }; + codeLoadedTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The time which the code loaded on the server' }; + readyTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The time when the server was completely ready' }; }; type ServerStartEvent = { startTime: number; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 49eaa65d462db..d23efb3a11184 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -424,6 +424,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) { const event = getTelemetryActivationEvent(extensionDescription, reason); type ExtensionActivationTimesClassification = { + owner: 'jrieken'; outcome: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; } & TelemetryActivationEventFragment & ExtensionActivationTimesFragment; @@ -447,7 +448,9 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { const event = getTelemetryActivationEvent(extensionDescription, reason); - type ActivatePluginClassification = {} & TelemetryActivationEventFragment; + type ActivatePluginClassification = { + owner: 'jrieken'; + } & TelemetryActivationEventFragment; this._mainThreadTelemetryProxy.$publicLog2('activatePlugin', event); const entryPoint = this._getEntryPoint(extensionDescription); if (!entryPoint) { diff --git a/src/vs/workbench/api/common/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts index 4a07295239272..4ae49cc2a8b62 100644 --- a/src/vs/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -249,6 +249,7 @@ class KeytarNodeModuleFactory implements INodeModuleFactory { public load(_request: string, parent: URI): any { const ext = this._extensionPaths.findSubstr(parent); type ShimmingKeytarClassification = { + owner: 'jrieken'; extension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; this._mainThreadTelemetry.$publicLog2<{ extension: string }, ShimmingKeytarClassification>('shimming.keytar', { extension: ext?.identifier.value ?? 'unknown_extension' }); @@ -346,6 +347,7 @@ class OpenNodeModuleFactory implements INodeModuleFactory { return; } type ShimmingOpenClassification = { + owner: 'jrieken'; extension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; this._mainThreadTelemetry.$publicLog2<{ extension: string }, ShimmingOpenClassification>('shimming.open', { extension: this._extensionId }); @@ -356,6 +358,7 @@ class OpenNodeModuleFactory implements INodeModuleFactory { return; } type ShimmingOpenCallNoForwardClassification = { + owner: 'jrieken'; extension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; this._mainThreadTelemetry.$publicLog2<{ extension: string }, ShimmingOpenCallNoForwardClassification>('shimming.open.call.noForward', { extension: this._extensionId }); diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index b65c52714b040..db595e3c9a5bc 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -741,7 +741,10 @@ export class ExtHostSCM implements ExtHostSCMShape { this.logService.trace('ExtHostSCM#createSourceControl', extension.identifier.value, id, label, rootUri); type TEvent = { extensionId: string }; - type TMeta = { extensionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' } }; + type TMeta = { + owner: 'joaomoreno'; + extensionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; + }; this._telemetry.$publicLog2('api/scm/createSourceControl', { extensionId: extension.identifier.value, }); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 36647e6dcfe1e..307fd9e9feca7 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -102,6 +102,7 @@ export class ViewContainerActivityAction extends ActivityAction { private logAction(action: string) { type ActivityBarActionClassification = { + owner: 'sbatten'; viewletId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; action: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 3624936e41f1a..a07f202b0760c 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -968,6 +968,7 @@ function registerCloseEditorCommands() { ]); type WorkbenchEditorReopenClassification = { + owner: 'rebornix'; scheme: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; ext: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; from: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; diff --git a/src/vs/workbench/browser/parts/views/viewPane.ts b/src/vs/workbench/browser/parts/views/viewPane.ts index 80be8b967241f..20f83615cabed 100644 --- a/src/vs/workbench/browser/parts/views/viewPane.ts +++ b/src/vs/workbench/browser/parts/views/viewPane.ts @@ -51,6 +51,7 @@ export interface IViewPaneOptions extends IPaneOptions { } type WelcomeActionClassification = { + owner: 'sandy081'; viewId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; uri: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index be75bf31cd14c..0ff11494826aa 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -821,6 +821,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { if (this.viewContainerModel.activeViewDescriptors.some(viewDescriptor => viewDescriptor.id === viewId)) { const visible = !this.viewContainerModel.isVisible(viewId); type ViewsToggleVisibilityClassification = { + owner: 'sandy081'; viewId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; visible: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index 0770dbb143e61..07741ba7717e4 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -306,6 +306,7 @@ export class ExperimentService extends Disposable implements IExperimentService const promises = rawExperiments.map(experiment => this.evaluateExperiment(experiment)); return Promise.all(promises).then(() => { type ExperimentsClassification = { + owner: 'sbatten'; experiments: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; this.telemetryService.publicLog2<{ experiments: string[] }, ExperimentsClassification>('experiments', { experiments: this._experiments.map(e => e.id) }); diff --git a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts index f287d1ebe38db..28a22fa7191f0 100644 --- a/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/dynamicWorkspaceRecommendations.ts @@ -16,6 +16,7 @@ import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRe import { localize } from 'vs/nls'; type DynamicWorkspaceRecommendationsClassification = { + owner: 'sandy081'; count: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; cache: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; }; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index 4e95edf7f9e4a..dc5bbff9180e3 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -27,12 +27,14 @@ import { EnablementState, IWorkbenchExtensionManagementService, IWorkbenchExtens import { IExtensionIgnoredRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; type ExtensionRecommendationsNotificationClassification = { + owner: 'sandy081'; userReaction: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; extensionId?: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; type ExtensionWorkspaceRecommendationsNotificationClassification = { + owner: 'sandy081'; userReaction: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 26e23dad630ed..86ffb382e28c7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -29,6 +29,7 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/com import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; type IgnoreRecommendationClassification = { + owner: 'sandy081'; recommendationReason: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; extensionId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; }; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index de628c54a8d39..31424b2285ab8 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -61,6 +61,7 @@ import { isOfflineError } from 'vs/base/parts/request/common/request'; const FORCE_FEATURE_EXTENSIONS = ['vscode.git', 'vscode.git-base', 'vscode.search-result']; type WorkspaceRecommendationsClassification = { + owner: 'sandy081'; count: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; 'isMeasurement': true }; }; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index ece35b058d130..764a26d89ecb4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -57,6 +57,7 @@ interface InstalledExtensionsEvent { readonly count: number; } interface ExtensionsLoadClassification extends GDPRClassification { + owner: 'digitarald'; readonly extensionIds: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; readonly count: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; } diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index b81342b1bb99c..26527cd7d6879 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -37,6 +37,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; type FileExtensionSuggestionClassification = { + owner: 'sandy081'; userReaction: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; fileExtension: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; }; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index c6b4e71bb47cd..1ae1e7ea6bf90 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -245,6 +245,7 @@ export class NotebookEditor extends EditorPane implements IEditorPaneWithSelecti mark(input.resource, 'editorLoaded'); type WorkbenchNotebookOpenClassification = { + owner: 'rebornix'; scheme: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; ext: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; viewType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 8567bdded40a3..aabbb8f23decb 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1130,6 +1130,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD }); } type WorkbenchNotebookOpenClassification = { + owner: 'rebornix'; scheme: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; ext: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; viewType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index 018b31c5768ad..c7c5e722f4f53 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -51,6 +51,7 @@ import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; type KeybindingEditorActionClassification = { + owner: 'sandy081'; action: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; }; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts index 28e8b4fd1e559..8878b684466e1 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts @@ -139,7 +139,7 @@ export const openNewSearchEditor = } } - telemetryService.publicLog2('searchEditor/openNewSearchEditor'); + telemetryService.publicLog2<{}, { owner: 'roblourens' }>('searchEditor/openNewSearchEditor'); const seedSearchStringFromSelection = _args.location === 'new' || configurationService.getValue('editor').find!.seedSearchStringFromSelection; const args: OpenSearchEditorArgs = { query: seedSearchStringFromSelection ? selected : undefined }; @@ -189,7 +189,7 @@ export const createEditorFromSearchResult = const sortOrder = configurationService.getValue('search').sortOrder; - telemetryService.publicLog2('searchEditor/createEditorFromSearchResult'); + telemetryService.publicLog2<{}, { owner: 'roblourens' }>('searchEditor/createEditorFromSearchResult'); const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true }); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts index dfd56c332670c..56f822657f553 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts @@ -184,7 +184,7 @@ export class SearchEditorInput extends EditorInput { override async saveAs(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { const path = await this.fileDialogService.pickFileToSave(await this.suggestFileName(), options?.availableFileSystems); if (path) { - this.telemetryService.publicLog2('searchEditor/saveSearchResults'); + this.telemetryService.publicLog2<{}, { owner: 'roblourens' }>('searchEditor/saveSearchResults'); const toWrite = await this.serializeForDisk(); if (await this.textFileService.create([{ resource: path, value: toWrite, options: { overwrite: true } }])) { this.setDirty(false); diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTags.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTags.ts index 8e263a874010c..c5156ebe74b13 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTags.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTags.ts @@ -67,7 +67,7 @@ export class WorkspaceTags implements IWorkbenchContribution { value = 'Unknown'; } - this.telemetryService.publicLog2<{ edition: string }, { edition: { classification: 'SystemMetaData'; purpose: 'BusinessInsight' } }>('windowsEdition', { edition: value }); + this.telemetryService.publicLog2<{ edition: string }, { owner: 'sbatten'; edition: { classification: 'SystemMetaData'; purpose: 'BusinessInsight' } }>('windowsEdition', { edition: value }); } private async getWorkspaceInformation(): Promise { diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index adf6f60619f5a..2ebf2547eca03 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -72,6 +72,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr }; type WorkspaceLoadClassification = { + owner: 'bpasero'; userAgent: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; emptyWorkbench: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; windowSize: WindowSizeFragment; @@ -134,12 +135,15 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr const settingsType = this.getTypeIfSettings(e.model.resource); if (settingsType) { type SettingsReadClassification = { + owner: 'bpasero'; settingsType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; this.telemetryService.publicLog2<{ settingsType: string }, SettingsReadClassification>('settingsRead', { settingsType }); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data } else { - type FileGetClassification = {} & FileTelemetryDataFragment; + type FileGetClassification = { + owner: 'bpasero'; + } & FileTelemetryDataFragment; this.telemetryService.publicLog2('fileGet', this.getTelemetryData(e.model.resource, e.reason)); } @@ -149,11 +153,14 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr const settingsType = this.getTypeIfSettings(e.model.resource); if (settingsType) { type SettingsWrittenClassification = { + owner: 'bpasero'; settingsType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; this.telemetryService.publicLog2<{ settingsType: string }, SettingsWrittenClassification>('settingsWritten', { settingsType }); // Do not log write to user settings.json and .vscode folder as a filePUT event as it ruins our JSON usage data } else { - type FilePutClassfication = {} & FileTelemetryDataFragment; + type FilePutClassfication = { + owner: 'bpasero'; + } & FileTelemetryDataFragment; this.telemetryService.publicLog2('filePUT', this.getTelemetryData(e.model.resource, e.reason)); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 61c2efbb88501..67614b1df92c2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1634,7 +1634,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } if (failedShellIntegrationInjection) { - this._telemetryService.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationFailureProcessExit'); + this._telemetryService.publicLog2<{}, { owner: 'meganrogge' }>('terminal/shellIntegrationFailureProcessExit'); } // First onExit to consumers, this can happen after the terminal has already been disposed. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 7ae7dc3c4fc93..bf5ad2f3b6852 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -335,7 +335,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce this._hasChildProcesses = value; break; case ProcessPropertyType.FailedShellIntegrationActivation: - this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivationFailureCustomArgs'); + this._telemetryService?.publicLog2<{}, { owner: 'meganrogge' }>('terminal/shellIntegrationActivationFailureCustomArgs'); break; } this._onDidChangeProperty.fire({ type, value }); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 4a7fe8b620255..dec9d627cb940 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -66,6 +66,7 @@ const CONTEXT_CONFLICTS_SOURCES = new RawContextKey('conflictsSources', type ConfigureSyncQuickPickItem = { id: SyncResource; label: string; description?: string }; type SyncConflictsClassification = { + owner: 'sandy081'; source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; action?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; }; diff --git a/src/vs/workbench/services/accessibility/electron-sandbox/accessibilityService.ts b/src/vs/workbench/services/accessibility/electron-sandbox/accessibilityService.ts index af9a08af7c86a..eb818e7fc164c 100644 --- a/src/vs/workbench/services/accessibility/electron-sandbox/accessibilityService.ts +++ b/src/vs/workbench/services/accessibility/electron-sandbox/accessibilityService.ts @@ -22,6 +22,7 @@ interface AccessibilityMetrics { enabled: boolean; } type AccessibilityMetricsClassification = { + owner: 'isidorn'; enabled: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; diff --git a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts index 5c045163b1b99..f2008b0759251 100644 --- a/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/browser/extensionUrlHandler.ts @@ -82,6 +82,7 @@ export interface ExtensionUrlHandlerEvent { } export interface ExtensionUrlHandlerClassification extends GDPRClassification { + owner: 'joaomoreno'; readonly extensionId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; } diff --git a/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts b/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts index d34284098963e..e70dd7619514a 100644 --- a/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts +++ b/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts @@ -203,6 +203,7 @@ export class LanguageDetectionWorkerHost { async sendTelemetryEvent(languages: string[], confidences: number[], timeSpent: number): Promise { type LanguageDetectionStats = { languages: string; confidences: string; timeSpent: number }; type LanguageDetectionStatsClassification = { + owner: 'TylerLeonhardt'; languages: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; confidences: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; timeSpent: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; @@ -337,6 +338,7 @@ export class LanguageDetectionWorkerClient extends EditorWorkerClient { } type LanguageDetectionPerfClassification = { + owner: 'TylerLeonhardt'; timeSpent: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; detection: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; }; diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 555a3a7ee20a8..27785f847cc51 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -304,6 +304,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic async openGlobalKeybindingSettings(textual: boolean, options?: IKeybindingsEditorOptions): Promise { type OpenKeybindingsClassification = { + owner: 'sandy081'; textual: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; }; this.telemetryService.publicLog2<{ textual: boolean }, OpenKeybindingsClassification>('openKeybindings', { textual }); diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index 282bbfbe40e0f..cf0aad3563a91 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -288,6 +288,7 @@ export class SearchService extends Disposable implements ISearchService { const cacheStats: ICachedSearchStats = fileSearchStats.detailStats as ICachedSearchStats; type CachedSearchCompleteClassifcation = { + owner: 'roblourens'; reason?: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; resultCount: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true }; workspaceFolderCount: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true }; @@ -330,6 +331,7 @@ export class SearchService extends Disposable implements ISearchService { const searchEngineStats: ISearchEngineStats = fileSearchStats.detailStats as ISearchEngineStats; type SearchCompleteClassification = { + owner: 'roblourens'; reason?: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; resultCount: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true }; workspaceFolderCount: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true }; @@ -387,6 +389,7 @@ export class SearchService extends Disposable implements ISearchService { } type TextSearchCompleteClassification = { + owner: 'roblourens'; reason?: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth' }; workspaceFolderCount: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true }; endToEndTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true }; diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 901f92d9f3e8e..e2b130592fb9a 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -579,6 +579,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { const key = themeType + themeData.extensionId; if (!this.themeExtensionsActivated.get(key)) { type ActivatePluginClassification = { + owner: 'aeschli'; id: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; name: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight' }; isBuiltin: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 482be496d9b0e..d90996fd7ab7f 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -37,11 +37,13 @@ import { ICredentialsService } from 'vs/platform/credentials/common/credentials' import { CancellationError } from 'vs/base/common/errors'; type UserAccountClassification = { + owner: 'sandy081'; id: { classification: 'EndUserPseudonymizedInformation'; purpose: 'BusinessInsight' }; providerId: { classification: 'EndUserPseudonymizedInformation'; purpose: 'BusinessInsight' }; }; type FirstTimeSyncClassification = { + owner: 'sandy081'; action: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; }; @@ -617,7 +619,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } private async onDidSuccessiveAuthFailures(): Promise { - this.telemetryService.publicLog2('sync/successiveAuthFailures'); + this.telemetryService.publicLog2<{}, { owner: 'sandy081' }>('sync/successiveAuthFailures'); this.currentSessionId = undefined; await this.update(); From 476e8cc521d07b7ce130795bfc8c1a0e8f44ce8f Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 24 May 2022 21:07:29 +0200 Subject: [PATCH 294/942] Extract `NativeExtensionService` and register a real implementation based on `UtilityProcess` for the extension service in the sandbox case --- .../nativeExtensionService.ts | 20 +++++++++++++++++++ .../electronExtensionService.ts} | 15 +++++--------- .../sandboxExtensionService.ts | 13 ++++++++++++ src/vs/workbench/workbench.desktop.main.ts | 2 +- .../workbench.desktop.sandbox.main.ts | 8 +------- 5 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 src/vs/workbench/services/extensions/electron-browser/nativeExtensionService.ts rename src/vs/workbench/services/extensions/{electron-browser/extensionService.ts => electron-sandbox/electronExtensionService.ts} (96%) create mode 100644 src/vs/workbench/services/extensions/electron-sandbox/sandboxExtensionService.ts diff --git a/src/vs/workbench/services/extensions/electron-browser/nativeExtensionService.ts b/src/vs/workbench/services/extensions/electron-browser/nativeExtensionService.ts new file mode 100644 index 0000000000000..2e4c745a9f854 --- /dev/null +++ b/src/vs/workbench/services/extensions/electron-browser/nativeExtensionService.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ExtensionHostKind, ExtensionRunningLocation, IExtensionHost, IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { NativeLocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost'; +import { ElectronExtensionService } from 'vs/workbench/services/extensions/electron-sandbox/electronExtensionService'; + +export class NativeExtensionService extends ElectronExtensionService { + protected override _createExtensionHost(runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null { + if (runningLocation.kind === ExtensionHostKind.LocalProcess) { + return this._instantiationService.createInstance(NativeLocalProcessExtensionHost, runningLocation, this._createLocalExtensionHostDataProvider(isInitialStart, runningLocation)); + } + return super._createExtensionHost(runningLocation, isInitialStart); + } +} + +registerSingleton(IExtensionService, NativeExtensionService); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts similarity index 96% rename from src/vs/workbench/services/extensions/electron-browser/extensionService.ts rename to src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts index 548ae5b4b16f1..dabd7358452dc 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts @@ -3,10 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { NativeLocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost'; - import { CachedExtensionScanner } from 'vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { AbstractExtensionService, ExtensionHostCrashTracker, ExtensionRunningPreference, extensionRunningPreferenceToString, filterByRunningLocation } from 'vs/workbench/services/extensions/common/abstractExtensionService'; import * as nls from 'vs/nls'; import { runWhenIdle } from 'vs/base/common/async'; @@ -50,9 +47,9 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import { isCI } from 'vs/base/common/platform'; import { IResolveAuthorityErrorResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; import { URI } from 'vs/base/common/uri'; -import { ILocalProcessExtensionHostDataProvider, ILocalProcessExtensionHostInitData } from 'vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost'; +import { ILocalProcessExtensionHostDataProvider, ILocalProcessExtensionHostInitData, SandboxLocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost'; -export class ExtensionService extends AbstractExtensionService implements IExtensionService { +export abstract class ElectronExtensionService extends AbstractExtensionService implements IExtensionService { private readonly _enableLocalWebWorker: boolean; private readonly _lazyLocalWebWorker: boolean; @@ -156,7 +153,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten ])); } - private _createLocalExtensionHostDataProvider(isInitialStart: boolean, desiredRunningLocation: ExtensionRunningLocation): ILocalProcessExtensionHostDataProvider & IWebWorkerExtensionHostDataProvider { + protected _createLocalExtensionHostDataProvider(isInitialStart: boolean, desiredRunningLocation: ExtensionRunningLocation): ILocalProcessExtensionHostDataProvider & IWebWorkerExtensionHostDataProvider { return { getInitData: async (): Promise => { if (isInitialStart) { @@ -194,7 +191,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten } protected _pickExtensionHostKind(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null { - const result = ExtensionService.pickExtensionHostKind(extensionKinds, isInstalledLocally, isInstalledRemotely, preference, Boolean(this._environmentService.remoteAuthority), this._enableLocalWebWorker); + const result = ElectronExtensionService.pickExtensionHostKind(extensionKinds, isInstalledLocally, isInstalledRemotely, preference, Boolean(this._environmentService.remoteAuthority), this._enableLocalWebWorker); this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionHostKindToString(result)}`); return result; } @@ -241,7 +238,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten protected _createExtensionHost(runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null { switch (runningLocation.kind) { case ExtensionHostKind.LocalProcess: { - return this._instantiationService.createInstance(NativeLocalProcessExtensionHost, runningLocation, this._createLocalExtensionHostDataProvider(isInitialStart, runningLocation)); + return this._instantiationService.createInstance(SandboxLocalProcessExtensionHost, runningLocation, this._createLocalExtensionHostDataProvider(isInitialStart, runningLocation)); } case ExtensionHostKind.LocalWebWorker: { if (this._enableLocalWebWorker) { @@ -677,8 +674,6 @@ function getRemoteAuthorityPrefix(remoteAuthority: string): string { return remoteAuthority.substring(0, plusIndex); } -registerSingleton(IExtensionService, ExtensionService); - class RestartExtensionHostAction extends Action2 { constructor() { diff --git a/src/vs/workbench/services/extensions/electron-sandbox/sandboxExtensionService.ts b/src/vs/workbench/services/extensions/electron-sandbox/sandboxExtensionService.ts new file mode 100644 index 0000000000000..ea51d7abc8f60 --- /dev/null +++ b/src/vs/workbench/services/extensions/electron-sandbox/sandboxExtensionService.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ElectronExtensionService } from 'vs/workbench/services/extensions/electron-sandbox/electronExtensionService'; + +export class SandboxExtensionService extends ElectronExtensionService { +} + +registerSingleton(IExtensionService, SandboxExtensionService); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 75b3b27affafb..6b7b44d415593 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -37,7 +37,7 @@ import 'vs/workbench/workbench.sandbox.main'; // // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -import 'vs/workbench/services/extensions/electron-browser/extensionService'; +import 'vs/workbench/services/extensions/electron-browser/nativeExtensionService'; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // diff --git a/src/vs/workbench/workbench.desktop.sandbox.main.ts b/src/vs/workbench/workbench.desktop.sandbox.main.ts index 792585471994c..894e678521269 100644 --- a/src/vs/workbench/workbench.desktop.sandbox.main.ts +++ b/src/vs/workbench/workbench.desktop.sandbox.main.ts @@ -27,12 +27,6 @@ import 'vs/workbench/electron-sandbox/desktop.main'; //#region --- workbench services -import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; - -// TODO@bpasero sandbox: remove me when extension host is present -class SimpleExtensionService extends NullExtensionService { } - -registerSingleton(IExtensionService, SimpleExtensionService); +import 'vs/workbench/services/extensions/electron-sandbox/sandboxExtensionService'; //#endregion From 7226b69ff49978173938a73a5d6ff59d51022e8b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 24 May 2022 21:14:47 +0200 Subject: [PATCH 295/942] Fix layering issue --- .../extensions/electron-sandbox/localProcessExtensionHost.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts index 800bf0576aeb4..cee3a17b7f1ae 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts @@ -396,7 +396,7 @@ export class SandboxLocalProcessExtensionHost implements IExtensionHost { // 2) wait for the incoming `initialized` event. return new Promise((resolve, reject) => { - let timeoutHandle: NodeJS.Timer; + let timeoutHandle: any; const installTimeoutCheck = () => { timeoutHandle = setTimeout(() => { reject('The local extenion host took longer than 60s to send its ready message.'); From bac03e54cb568044486f26efe91178f2e82545df Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Tue, 24 May 2022 13:02:08 -0700 Subject: [PATCH 296/942] Move "Configure Display Language" to Action2 (#150308) --- .../browser/localizations.contribution.ts | 6 +- .../browser/localizationsActions.ts | 60 ++++++++++--------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts index 92c5e9c0348b4..f05063b6b057b 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts @@ -6,8 +6,7 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { registerAction2 } from 'vs/platform/actions/common/actions'; import { Disposable } from 'vs/base/common/lifecycle'; import { ConfigureLocaleAction } from 'vs/workbench/contrib/localizations/browser/localizationsActions'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; @@ -28,8 +27,7 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { ViewContainerLocation } from 'vs/workbench/common/views'; // Register action to configure locale and related settings -const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(SyncActionDescriptor.from(ConfigureLocaleAction), 'Configure Display Language'); +registerAction2(ConfigureLocaleAction); const LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY = 'extensionsAssistant/languagePackSuggestionIgnore'; diff --git a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts index 5384fc3e0b0e7..016000d58c99c 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { Action } from 'vs/base/common/actions'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; @@ -17,27 +16,22 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProductService } from 'vs/platform/product/common/productService'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; +import { Action2, MenuId } from 'vs/platform/actions/common/actions'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -export class ConfigureLocaleAction extends Action { - public static readonly ID = 'workbench.action.configureLocale'; - public static readonly LABEL = localize('configureLocale', "Configure Display Language"); - - constructor(id: string, label: string, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ILocalizationsService private readonly localizationService: ILocalizationsService, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, - @IHostService private readonly hostService: IHostService, - @INotificationService private readonly notificationService: INotificationService, - @IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService, - @IDialogService private readonly dialogService: IDialogService, - @IProductService private readonly productService: IProductService - ) { - super(id, label); +export class ConfigureLocaleAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.configureLocale', + title: { original: 'Configure Display Language', value: localize('configureLocale', "Configure Display Language") }, + menu: { + id: MenuId.CommandPalette + } + }); } - private async getLanguageOptions(): Promise { - const availableLanguages = await this.localizationService.getLanguageIds(); + private async getLanguageOptions(localizationService: ILocalizationsService): Promise { + const availableLanguages = await localizationService.getLanguageIds(); availableLanguages.sort(); return availableLanguages @@ -45,12 +39,22 @@ export class ConfigureLocaleAction extends Action { .concat({ label: localize('installAdditionalLanguages', "Install Additional Languages...") }); } - public override async run(): Promise { - const languageOptions = await this.getLanguageOptions(); + public override async run(accessor: ServicesAccessor): Promise { + const environmentService: IEnvironmentService = accessor.get(IEnvironmentService); + const localizationService: ILocalizationsService = accessor.get(ILocalizationsService); + const quickInputService: IQuickInputService = accessor.get(IQuickInputService); + const jsonEditingService: IJSONEditingService = accessor.get(IJSONEditingService); + const hostService: IHostService = accessor.get(IHostService); + const notificationService: INotificationService = accessor.get(INotificationService); + const paneCompositeService: IPaneCompositePartService = accessor.get(IPaneCompositePartService); + const dialogService: IDialogService = accessor.get(IDialogService); + const productService: IProductService = accessor.get(IProductService); + + const languageOptions = await this.getLanguageOptions(localizationService); const currentLanguageIndex = languageOptions.findIndex(l => l.label === language); try { - const selectedLanguage = await this.quickInputService.pick(languageOptions, + const selectedLanguage = await quickInputService.pick(languageOptions, { canPickMany: false, placeHolder: localize('chooseDisplayLanguage', "Select Display Language"), @@ -58,7 +62,7 @@ export class ConfigureLocaleAction extends Action { }); if (selectedLanguage === languageOptions[languageOptions.length - 1]) { - return this.paneCompositeService.openPaneComposite(EXTENSIONS_VIEWLET_ID, ViewContainerLocation.Sidebar, true) + return paneCompositeService.openPaneComposite(EXTENSIONS_VIEWLET_ID, ViewContainerLocation.Sidebar, true) .then(viewlet => viewlet?.getViewPaneContainer()) .then(viewlet => { const extensionsViewlet = viewlet as IExtensionsViewPaneContainer; @@ -68,20 +72,20 @@ export class ConfigureLocaleAction extends Action { } if (selectedLanguage) { - await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: selectedLanguage.label }], true); - const restart = await this.dialogService.confirm({ + await jsonEditingService.write(environmentService.argvResource, [{ path: ['locale'], value: selectedLanguage.label }], true); + const restart = await dialogService.confirm({ type: 'info', message: localize('relaunchDisplayLanguageMessage', "A restart is required for the change in display language to take effect."), - detail: localize('relaunchDisplayLanguageDetail', "Press the restart button to restart {0} and change the display language.", this.productService.nameLong), + detail: localize('relaunchDisplayLanguageDetail', "Press the restart button to restart {0} and change the display language.", productService.nameLong), primaryButton: localize('restart', "&&Restart") }); if (restart.confirmed) { - this.hostService.restart(); + hostService.restart(); } } } catch (e) { - this.notificationService.error(e); + notificationService.error(e); } } } From b1a0aa5cfdacecf5e4f7b1991353243a07bd3f89 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Tue, 24 May 2022 13:10:40 -0700 Subject: [PATCH 297/942] Use existing resize observer instead --- .../browser/view/renderers/webviewPreloads.ts | 66 ++++++++----------- 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 441f75067b90d..471fdba68bc7b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -294,7 +294,8 @@ async function webviewPreloads(ctx: PreloadContext) { private readonly _observer: ResizeObserver; - private readonly _observedElements = new WeakMap(); + private readonly _observedElements = new WeakMap(); + private _outputResizeTimer: any; constructor() { this._observer = new ResizeObserver(entries => { @@ -308,6 +309,8 @@ async function webviewPreloads(ctx: PreloadContext) { continue; } + this.postResizeMessage(observedElementInfo.cellId); + if (entry.target.id === observedElementInfo.id && entry.contentRect) { if (observedElementInfo.output) { if (entry.contentRect.height !== 0) { @@ -329,14 +332,26 @@ async function webviewPreloads(ctx: PreloadContext) { }); } - public observe(container: Element, id: string, output: boolean) { + public observe(container: Element, id: string, output: boolean, cellId: string) { if (this._observedElements.has(container)) { return; } - this._observedElements.set(container, { id, output, lastKnownHeight: -1 }); + this._observedElements.set(container, { id, output, lastKnownHeight: -1, cellId }); this._observer.observe(container); } + + private postResizeMessage(cellId: string) { + // Debounce this callback to only happen after + // 250 ms. Don't need resize events that often. + clearTimeout(this._outputResizeTimer); + this._outputResizeTimer = setTimeout(() => { + postNotebookMessage('outputResized', { + cellId + }); + }, 250); + + } }; function scrollWillGoToParent(event: WheelEvent) { @@ -1395,14 +1410,10 @@ async function webviewPreloads(ctx: PreloadContext) { private readonly _markupCells = new Map(); private readonly _outputCells = new Map(); - private _outputResizeObservers: ResizeObserver[] = []; - private _outputResizeTimer: any; public clearAll() { this._markupCells.clear(); this._outputCells.clear(); - this._outputResizeObservers.forEach(o => o.disconnect()); - this._outputResizeObservers = []; } public rerender() { @@ -1517,12 +1528,9 @@ async function webviewPreloads(ctx: PreloadContext) { } const cellOutput = this.ensureOutputCell(data.cellId, data.cellTop, false); - const outputNode = cellOutput.createOutputElement(data.outputId, data.outputOffset, data.left); + const outputNode = cellOutput.createOutputElement(data.outputId, data.outputOffset, data.left, data.cellId); outputNode.render(data.content, preloadsAndErrors); - // Track resizes on this output - this.trackOutputResize(data.cellId, outputNode.element); - // don't hide until after this step so that the height is right cellOutput.element.style.visibility = data.initiallyHidden ? 'hidden' : 'visible'; } @@ -1533,10 +1541,6 @@ async function webviewPreloads(ctx: PreloadContext) { if (!cell) { cell = new OutputCell(cellId); this._outputCells.set(cellId, cell); - - // New output cell, clear resize handlers - this._outputResizeObservers.forEach(o => o.disconnect); - this._outputResizeObservers = []; } if (existed && skipCellTopUpdateIfExist) { @@ -1578,24 +1582,6 @@ async function webviewPreloads(ctx: PreloadContext) { cell?.updateScroll(request); } } - - private outputResizeHandler(cellId: string) { - // Debounce this callback to only happen after - // 250 ms. Don't need resize events that often. - clearTimeout(this._outputResizeTimer); - this._outputResizeTimer = setTimeout(() => { - postNotebookMessage('outputResized', { - cellId - }); - }, 250); - } - - private trackOutputResize(cellId: string, outputContainer: HTMLElement) { - const handler = this.outputResizeHandler.bind(this, cellId); - const observer = new ResizeObserver(handler); - this._outputResizeObservers.push(observer); - observer.observe(outputContainer); - } }(); class MarkdownCodeBlock { @@ -1695,7 +1681,7 @@ async function webviewPreloads(ctx: PreloadContext) { this.addEventListeners(); this.updateContentAndRender(this._content.value).then(() => { - resizeObserver.observe(this.element, this.id, false); + resizeObserver.observe(this.element, this.id, false, this.id); resolveReady(); }); } @@ -1850,7 +1836,7 @@ async function webviewPreloads(ctx: PreloadContext) { container.appendChild(lowerWrapperElement); } - public createOutputElement(outputId: string, outputOffset: number, left: number): OutputElement { + public createOutputElement(outputId: string, outputOffset: number, left: number, cellId: string): OutputElement { let outputContainer = this.outputElements.get(outputId); if (!outputContainer) { outputContainer = new OutputContainer(outputId); @@ -1858,7 +1844,7 @@ async function webviewPreloads(ctx: PreloadContext) { this.outputElements.set(outputId, outputContainer); } - return outputContainer.createOutputElement(outputId, outputOffset, left); + return outputContainer.createOutputElement(outputId, outputOffset, left, cellId); } public clearOutput(outputId: string, rendererId: string | undefined) { @@ -1940,12 +1926,12 @@ async function webviewPreloads(ctx: PreloadContext) { this.element.style.top = `${outputOffset}px`; } - public createOutputElement(outputId: string, outputOffset: number, left: number): OutputElement { + public createOutputElement(outputId: string, outputOffset: number, left: number, cellId: string): OutputElement { this.element.innerText = ''; this.element.style.maxHeight = '0px'; this.element.style.top = `${outputOffset}px`; - this._outputNode = new OutputElement(outputId, left); + this._outputNode = new OutputElement(outputId, left, cellId); this.element.appendChild(this._outputNode.element); return this._outputNode; } @@ -1977,13 +1963,13 @@ async function webviewPreloads(ctx: PreloadContext) { class OutputElement { public readonly element: HTMLElement; - private _content?: { content: webviewMessages.ICreationContent; preloadsAndErrors: unknown[] }; private hasResizeObserver = false; constructor( private readonly outputId: string, left: number, + public readonly cellId: string ) { this.element = document.createElement('div'); this.element.id = outputId; @@ -2017,7 +2003,7 @@ async function webviewPreloads(ctx: PreloadContext) { if (!this.hasResizeObserver) { this.hasResizeObserver = true; - resizeObserver.observe(this.element, this.outputId, true); + resizeObserver.observe(this.element, this.outputId, true, this.cellId); } const offsetHeight = this.element.offsetHeight; From 60c8307e9642d2d950584450efdd8bd240b056c9 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Tue, 24 May 2022 13:18:29 -0700 Subject: [PATCH 298/942] Remove unnecessary interface addition --- .../contrib/notebook/browser/diff/notebookTextDiffEditor.ts | 4 ---- .../contrib/notebook/browser/notebookEditorWidget.ts | 1 - .../notebook/browser/view/renderers/backLayerWebView.ts | 1 - 3 files changed, 6 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 61c5daf994955..c0951594b1c6d 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -792,10 +792,6 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD throw new Error('Not implemented'); } - getCellByHandle(cellHandle: number): IGenericCellViewModel | undefined { - throw new Error('Not implemented'); - } - removeInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: DiffNestedCellViewModel, displayOutput: ICellOutputViewModel, diffSide: DiffSide) { this._insetModifyQueueByOutputId.queue(displayOutput.model.outputId + (diffSide === DiffSide.Modified ? '-right' : 'left'), async () => { const activeWebview = diffSide === DiffSide.Modified ? this._modifiedWebview : this._originalWebview; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 4bd1f431a7ff3..f5ce4eaa6962b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1387,7 +1387,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD triggerScroll(event: IMouseWheelEvent) { that._listViewInfoAccessor.triggerScroll(event); }, getCellByInfo: that.getCellByInfo.bind(that), getCellById: that._getCellById.bind(that), - getCellByHandle: that.getCellByHandle.bind(that), toggleNotebookCellSelection: that._toggleNotebookCellSelection.bind(that), focusNotebookCell: that.focusNotebookCell.bind(that), focusNextNotebookCell: that.focusNextNotebookCell.bind(that), diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 316ac6490d704..dedbe999773ab 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -67,7 +67,6 @@ export interface INotebookDelegateForWebview { focusNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output', options?: IFocusNotebookCellOptions): Promise; toggleNotebookCellSelection(cell: IGenericCellViewModel, selectFromPrevious: boolean): void; getCellByInfo(cellInfo: ICommonCellInfo): IGenericCellViewModel; - getCellByHandle(cellHandle: number): IGenericCellViewModel | undefined; focusNextNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): Promise; updateOutputHeight(cellInfo: ICommonCellInfo, output: IDisplayOutputViewModel, height: number, isInit: boolean, source?: string): void; scheduleOutputHeightAck(cellInfo: ICommonCellInfo, outputId: string, height: number): void; From bf7ec548f58cae6421d62206137763fa1a2452b4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 24 May 2022 13:19:04 -0700 Subject: [PATCH 299/942] Adopt TS 4.7 final (#150313) Picks up the offical TS 4.7 release --- extensions/package.json | 2 +- extensions/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index b6d00d2faafbe..3d89442d04fb2 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -4,7 +4,7 @@ "license": "MIT", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "4.7.1-rc" + "typescript": "4.7" }, "scripts": { "postinstall": "node ./postinstall.mjs" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 1c746b8d2d001..4ab30e237c68e 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -42,10 +42,10 @@ node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== -typescript@4.7.1-rc: - version "4.7.1-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.1-rc.tgz#23a0517d36c56de887b4457f29e2d265647bbd7c" - integrity sha512-EQd2NVelDe6ZVc2sO1CSpuSs+RHzY8c2n/kTNQAHw4um/eAXY+ZY4IKoUpNK0wO6C5hN+XcUXR7yqT8VbwwNIQ== +typescript@4.7: + version "4.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.2.tgz#1f9aa2ceb9af87cca227813b4310fff0b51593c4" + integrity sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A== vscode-grammar-updater@^1.1.0: version "1.1.0" From f363be172ca1ccbc6f037126926d6ca86c5760fc Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Tue, 24 May 2022 15:05:17 -0700 Subject: [PATCH 300/942] Rename LocalizationsService to LanguagePackService and misc moves (#150314) * rename LocalizationsService to LanguagePackService and misc moves * couple more renames * one more rename --- build/lib/i18n.resources.json | 2 +- .../contrib/localizationsUpdater.ts | 6 +-- .../sharedProcess/sharedProcessMain.ts | 12 +++--- src/vs/code/node/cliProcessMain.ts | 6 +-- src/vs/platform/driver/browser/driver.ts | 2 +- .../languagePacks/common/languagePacks.ts | 12 ++++++ .../common/localizedStrings.ts | 0 .../node/languagePacks.ts} | 31 ++++++++++++++-- .../localizations/common/localizations.ts | 37 ------------------- .../node/remoteExtensionHostAgentCli.ts | 6 +-- src/vs/server/node/serverServices.ts | 6 +-- .../browser/localizationsActions.ts | 10 ++--- .../localization.contribution.ts} | 8 ++-- .../electron-sandbox}/minimalTranslations.ts | 0 .../electron-sandbox/languagePackService.ts} | 4 +- src/vs/workbench/workbench.sandbox.main.ts | 4 +- 16 files changed, 73 insertions(+), 73 deletions(-) create mode 100644 src/vs/platform/languagePacks/common/languagePacks.ts rename src/vs/platform/{localizations => languagePacks}/common/localizedStrings.ts (100%) rename src/vs/platform/{localizations/node/localizations.ts => languagePacks/node/languagePacks.ts} (88%) delete mode 100644 src/vs/platform/localizations/common/localizations.ts rename src/vs/workbench/contrib/{localizations => localization}/browser/localizationsActions.ts (90%) rename src/vs/workbench/contrib/{localizations/browser/localizations.contribution.ts => localization/electron-sandbox/localization.contribution.ts} (98%) rename src/vs/workbench/contrib/{localizations/browser => localization/electron-sandbox}/minimalTranslations.ts (100%) rename src/vs/workbench/services/{localizations/electron-sandbox/localizationsService.ts => localization/electron-sandbox/languagePackService.ts} (68%) diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 06bfbb2b506b9..7c99da42ae77b 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -119,7 +119,7 @@ "project": "vscode-workbench" }, { - "name": "vs/workbench/contrib/localizations", + "name": "vs/workbench/contrib/localization", "project": "vscode-workbench" }, { diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater.ts b/src/vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater.ts index 6c9bfa8125a03..68085dcefcc8d 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; +import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; +import { LanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; export class LocalizationsUpdater extends Disposable { constructor( - @ILocalizationsService private readonly localizationsService: LocalizationsService + @ILanguagePackService private readonly localizationsService: LanguagePackService ) { super(); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 243729c4e3723..fb6cac109a97d 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -46,8 +46,8 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantia import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { MessagePortMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; +import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; +import { LanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; import { ConsoleLogger, ILoggerService, ILogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { FollowerLogService, LoggerChannelClient, LogLevelChannelClient } from 'vs/platform/log/common/logIpc'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; @@ -311,7 +311,7 @@ class SharedProcessMain extends Disposable { services.set(IExtensionTipsService, new SyncDescriptor(ExtensionTipsService)); // Localizations - services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); + services.set(ILanguagePackService, new SyncDescriptor(LanguagePackService)); // Diagnostics services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); @@ -360,9 +360,9 @@ class SharedProcessMain extends Disposable { const channel = new ExtensionManagementChannel(accessor.get(IExtensionManagementService), () => null); this.server.registerChannel('extensions', channel); - // Localizations - const localizationsChannel = ProxyChannel.fromService(accessor.get(ILocalizationsService)); - this.server.registerChannel('localizations', localizationsChannel); + // Language Packs + const languagePacksChannel = ProxyChannel.fromService(accessor.get(ILanguagePackService)); + this.server.registerChannel('languagePacks', languagePacksChannel); // Diagnostics const diagnosticsChannel = ProxyChannel.fromService(accessor.get(IDiagnosticsService)); diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 6c197b1766148..88f9dc88a7a5a 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -36,8 +36,8 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; +import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; +import { LanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; import { ConsoleLogger, getLogLevel, ILogger, ILogService, LogLevel, MultiplexLogService } from 'vs/platform/log/common/log'; import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; @@ -161,7 +161,7 @@ class CliMain extends Disposable { services.set(IExtensionManagementCLIService, new SyncDescriptor(ExtensionManagementCLIService)); // Localizations - services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); + services.set(ILanguagePackService, new SyncDescriptor(LanguagePackService)); // Telemetry const appenders: AppInsightsAppender[] = []; diff --git a/src/vs/platform/driver/browser/driver.ts b/src/vs/platform/driver/browser/driver.ts index c16032c953f1c..ce2167ba328e5 100644 --- a/src/vs/platform/driver/browser/driver.ts +++ b/src/vs/platform/driver/browser/driver.ts @@ -7,7 +7,7 @@ import { getClientArea, getTopLeftOffset } from 'vs/base/browser/dom'; import { coalesce } from 'vs/base/common/arrays'; import { language, locale } from 'vs/base/common/platform'; import { IElement, ILocaleInfo, ILocalizedStrings, IWindowDriver } from 'vs/platform/driver/common/driver'; -import localizedStrings from 'vs/platform/localizations/common/localizedStrings'; +import localizedStrings from 'vs/platform/languagePacks/common/localizedStrings'; export class BrowserWindowDriver implements IWindowDriver { diff --git a/src/vs/platform/languagePacks/common/languagePacks.ts b/src/vs/platform/languagePacks/common/languagePacks.ts new file mode 100644 index 0000000000000..473e358148a7e --- /dev/null +++ b/src/vs/platform/languagePacks/common/languagePacks.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const ILanguagePackService = createDecorator('languagePackService'); +export interface ILanguagePackService { + readonly _serviceBrand: undefined; + getInstalledLanguages(): Promise; +} diff --git a/src/vs/platform/localizations/common/localizedStrings.ts b/src/vs/platform/languagePacks/common/localizedStrings.ts similarity index 100% rename from src/vs/platform/localizations/common/localizedStrings.ts rename to src/vs/platform/languagePacks/common/localizedStrings.ts diff --git a/src/vs/platform/localizations/node/localizations.ts b/src/vs/platform/languagePacks/node/languagePacks.ts similarity index 88% rename from src/vs/platform/localizations/node/localizations.ts rename to src/vs/platform/languagePacks/node/languagePacks.ts index 9d8bd79ac7d98..009c936bdb061 100644 --- a/src/vs/platform/localizations/node/localizations.ts +++ b/src/vs/platform/languagePacks/node/languagePacks.ts @@ -13,7 +13,8 @@ import { Promises } from 'vs/base/node/pfs'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { IExtensionIdentifier, IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { ILocalizationsService, isValidLocalization } from 'vs/platform/localizations/common/localizations'; +import { ILocalizationContribution } from 'vs/platform/extensions/common/extensions'; +import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; import { ILogService } from 'vs/platform/log/common/log'; interface ILanguagePack { @@ -25,7 +26,7 @@ interface ILanguagePack { translations: { [id: string]: string }; } -export class LocalizationsService extends Disposable implements ILocalizationsService { +export class LanguagePackService extends Disposable implements ILanguagePackService { declare readonly _serviceBrand: undefined; @@ -48,7 +49,7 @@ export class LocalizationsService extends Disposable implements ILocalizationsSe }); } - async getLanguageIds(): Promise { + async getInstalledLanguages(): Promise { const languagePacks = await this.cache.getLanguagePacks(); // Contributed languages are those installed via extension packs, so does not include English const languages = ['en', ...Object.keys(languagePacks)]; @@ -174,3 +175,27 @@ class LanguagePacksCache extends Disposable { }); } } + +function isValidLocalization(localization: ILocalizationContribution): boolean { + if (typeof localization.languageId !== 'string') { + return false; + } + if (!Array.isArray(localization.translations) || localization.translations.length === 0) { + return false; + } + for (const translation of localization.translations) { + if (typeof translation.id !== 'string') { + return false; + } + if (typeof translation.path !== 'string') { + return false; + } + } + if (localization.languageName && typeof localization.languageName !== 'string') { + return false; + } + if (localization.localizedLanguageName && typeof localization.localizedLanguageName !== 'string') { + return false; + } + return true; +} diff --git a/src/vs/platform/localizations/common/localizations.ts b/src/vs/platform/localizations/common/localizations.ts deleted file mode 100644 index 6792440125ced..0000000000000 --- a/src/vs/platform/localizations/common/localizations.ts +++ /dev/null @@ -1,37 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ILocalizationContribution } from 'vs/platform/extensions/common/extensions'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export const ILocalizationsService = createDecorator('localizationsService'); -export interface ILocalizationsService { - readonly _serviceBrand: undefined; - getLanguageIds(): Promise; -} - -export function isValidLocalization(localization: ILocalizationContribution): boolean { - if (typeof localization.languageId !== 'string') { - return false; - } - if (!Array.isArray(localization.translations) || localization.translations.length === 0) { - return false; - } - for (const translation of localization.translations) { - if (typeof translation.id !== 'string') { - return false; - } - if (typeof translation.path !== 'string') { - return false; - } - } - if (localization.languageName && typeof localization.languageName !== 'string') { - return false; - } - if (localization.localizedLanguageName && typeof localization.localizedLanguageName !== 'string') { - return false; - } - return true; -} diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index 36c01538db41d..2d8bde94e63de 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -28,8 +28,8 @@ import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; import { RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IServerEnvironmentService, ServerEnvironmentService, ServerParsedArgs } from 'vs/server/node/serverEnvironmentService'; import { ExtensionManagementCLIService } from 'vs/platform/extensionManagement/common/extensionManagementCLIService'; -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; +import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; +import { LanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; import { getErrorMessage } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { isAbsolute, join } from 'vs/base/common/path'; @@ -104,7 +104,7 @@ class CliMain extends Disposable { services.set(IExtensionsScannerService, new SyncDescriptor(ExtensionsScannerService)); services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionManagementCLIService, new SyncDescriptor(ExtensionManagementCLIService)); - services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); + services.set(ILanguagePackService, new SyncDescriptor(LanguagePackService)); return new InstantiationService(services); } diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 9885e9a19829d..9a4bdee0c1ad9 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -35,8 +35,8 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; +import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; +import { LanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; import { AbstractLogger, DEFAULT_LOG_LEVEL, getLogLevel, ILogService, LogLevel, LogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { LogLevelChannel } from 'vs/platform/log/common/logIpc'; import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; @@ -159,7 +159,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); const instantiationService: IInstantiationService = new InstantiationService(services); - services.set(ILocalizationsService, instantiationService.createInstance(LocalizationsService)); + services.set(ILanguagePackService, instantiationService.createInstance(LanguagePackService)); const extensionManagementCLIService = instantiationService.createInstance(ExtensionManagementCLIService); services.set(IExtensionManagementCLIService, extensionManagementCLIService); diff --git a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts b/src/vs/workbench/contrib/localization/browser/localizationsActions.ts similarity index 90% rename from src/vs/workbench/contrib/localizations/browser/localizationsActions.ts rename to src/vs/workbench/contrib/localization/browser/localizationsActions.ts index 016000d58c99c..afebaf8f1e13e 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts +++ b/src/vs/workbench/contrib/localization/browser/localizationsActions.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; +import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IHostService } from 'vs/workbench/services/host/browser/host'; @@ -30,8 +30,8 @@ export class ConfigureLocaleAction extends Action2 { }); } - private async getLanguageOptions(localizationService: ILocalizationsService): Promise { - const availableLanguages = await localizationService.getLanguageIds(); + private async getLanguageOptions(localizationService: ILanguagePackService): Promise { + const availableLanguages = await localizationService.getInstalledLanguages(); availableLanguages.sort(); return availableLanguages @@ -41,7 +41,7 @@ export class ConfigureLocaleAction extends Action2 { public override async run(accessor: ServicesAccessor): Promise { const environmentService: IEnvironmentService = accessor.get(IEnvironmentService); - const localizationService: ILocalizationsService = accessor.get(ILocalizationsService); + const languagePackService: ILanguagePackService = accessor.get(ILanguagePackService); const quickInputService: IQuickInputService = accessor.get(IQuickInputService); const jsonEditingService: IJSONEditingService = accessor.get(IJSONEditingService); const hostService: IHostService = accessor.get(IHostService); @@ -50,7 +50,7 @@ export class ConfigureLocaleAction extends Action2 { const dialogService: IDialogService = accessor.get(IDialogService); const productService: IProductService = accessor.get(IProductService); - const languageOptions = await this.getLanguageOptions(localizationService); + const languageOptions = await this.getLanguageOptions(languagePackService); const currentLanguageIndex = languageOptions.findIndex(l => l.label === language); try { diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts similarity index 98% rename from src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts rename to src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts index f05063b6b057b..30f9660514dcf 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts @@ -6,9 +6,7 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContribution, Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { registerAction2 } from 'vs/platform/actions/common/actions'; import { Disposable } from 'vs/base/common/lifecycle'; -import { ConfigureLocaleAction } from 'vs/workbench/contrib/localizations/browser/localizationsActions'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import * as platform from 'vs/base/common/platform'; @@ -20,11 +18,13 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { VIEWLET_ID as EXTENSIONS_VIEWLET_ID, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; -import { minimumTranslatedStrings } from 'vs/workbench/contrib/localizations/browser/minimalTranslations'; +import { minimumTranslatedStrings } from 'vs/workbench/contrib/localization/electron-sandbox/minimalTranslations'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; +import { registerAction2 } from 'vs/platform/actions/common/actions'; +import { ConfigureLocaleAction } from 'vs/workbench/contrib/localization/browser/localizationsActions'; // Register action to configure locale and related settings registerAction2(ConfigureLocaleAction); @@ -117,7 +117,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo const loc = manifest && manifest.contributes && manifest.contributes.localizations && manifest.contributes.localizations.filter(x => x.languageId.toLowerCase() === locale)[0]; const languageName = loc ? (loc.languageName || locale) : locale; const languageDisplayName = loc ? (loc.localizedLanguageName || loc.languageName || locale) : locale; - const translationsFromPack: any = translation && translation.contents ? translation.contents['vs/workbench/contrib/localizations/browser/minimalTranslations'] : {}; + const translationsFromPack: any = translation && translation.contents ? translation.contents['vs/workbench/contrib/localization/electron-sandbox/minimalTranslations'] : {}; const promptMessageKey = extensionToInstall ? 'installAndRestartMessage' : 'showLanguagePackExtensions'; const useEnglish = !translationsFromPack[promptMessageKey]; diff --git a/src/vs/workbench/contrib/localizations/browser/minimalTranslations.ts b/src/vs/workbench/contrib/localization/electron-sandbox/minimalTranslations.ts similarity index 100% rename from src/vs/workbench/contrib/localizations/browser/minimalTranslations.ts rename to src/vs/workbench/contrib/localization/electron-sandbox/minimalTranslations.ts diff --git a/src/vs/workbench/services/localizations/electron-sandbox/localizationsService.ts b/src/vs/workbench/services/localization/electron-sandbox/languagePackService.ts similarity index 68% rename from src/vs/workbench/services/localizations/electron-sandbox/localizationsService.ts rename to src/vs/workbench/services/localization/electron-sandbox/languagePackService.ts index 5b7160047933e..b71303b3a9053 100644 --- a/src/vs/workbench/services/localizations/electron-sandbox/localizationsService.ts +++ b/src/vs/workbench/services/localization/electron-sandbox/languagePackService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; +import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; import { registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; -registerSharedProcessRemoteService(ILocalizationsService, 'localizations', { supportsDelayedInstantiation: true }); +registerSharedProcessRemoteService(ILanguagePackService, 'languagePacks', { supportsDelayedInstantiation: true }); diff --git a/src/vs/workbench/workbench.sandbox.main.ts b/src/vs/workbench/workbench.sandbox.main.ts index e0708193c3472..dd9ddd715b108 100644 --- a/src/vs/workbench/workbench.sandbox.main.ts +++ b/src/vs/workbench/workbench.sandbox.main.ts @@ -58,7 +58,7 @@ import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionMana import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionUrlTrustService'; import 'vs/workbench/services/credentials/electron-sandbox/credentialsService'; import 'vs/workbench/services/encryption/electron-sandbox/encryptionService'; -import 'vs/workbench/services/localizations/electron-sandbox/localizationsService'; +import 'vs/workbench/services/localization/electron-sandbox/languagePackService'; import 'vs/workbench/services/telemetry/electron-sandbox/telemetryService'; import 'vs/workbench/services/extensions/electron-sandbox/extensionHostStarter'; import 'vs/platform/extensionManagement/electron-sandbox/extensionsScannerService'; @@ -98,7 +98,7 @@ registerSingleton(IUserDataInitializationService, UserDataInitializationService) import 'vs/workbench/contrib/logs/electron-sandbox/logs.contribution'; // Localizations -import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; +import 'vs/workbench/contrib/localization/electron-sandbox/localization.contribution'; // Explorer import 'vs/workbench/contrib/files/electron-sandbox/files.contribution'; From 3a8b1fe03ebbcf57fb9c50b161db91229e2fe04a Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 24 May 2022 19:00:55 -0400 Subject: [PATCH 301/942] More telemetry comments (#150303) * Add owners to all telemetry events * More comments * Add comments to non typescript GDPR annotation * Another comment --- extensions/git/src/commands.ts | 6 ++++++ extensions/git/src/main.ts | 4 +++- extensions/git/src/repository.ts | 1 + .../github-authentication/src/githubServer.ts | 3 +++ .../src/languageFeatures/completions.ts | 2 ++ .../src/languageFeatures/organizeImports.ts | 1 + .../src/languageFeatures/quickFix.ts | 2 ++ .../src/languageFeatures/refactor.ts | 1 + .../src/tsServer/server.ts | 1 + .../src/typescriptServiceClient.ts | 6 ++++++ .../src/utils/largeProjectStatus.ts | 1 + .../diagnostics/node/diagnosticsService.ts | 14 +++++++++----- .../common/abstractExtensionManagementService.ts | 3 +++ .../common/extensionGalleryService.ts | 1 + .../browser/parts/editor/editorGroupView.ts | 2 ++ .../contrib/debug/browser/variablesView.ts | 1 + .../contrib/debug/common/debugTelemetry.ts | 2 ++ .../contrib/extensions/browser/extensionEditor.ts | 1 + .../browser/extensionRecommendationsService.ts | 1 + .../browser/extensionsWorkbenchService.ts | 2 ++ .../electron-sandbox/localization.contribution.ts | 1 + .../workbench/contrib/search/common/searchModel.ts | 3 +++ .../contrib/surveys/browser/ces.contribution.ts | 5 ++++- .../contrib/tags/electron-sandbox/workspaceTags.ts | 3 +++ .../terminal/browser/terminalTypeAheadAddon.ts | 1 + .../contrib/watermark/browser/watermark.ts | 4 +++- .../electron-sandbox/electronExtensionService.ts | 1 + .../services/timer/browser/timerService.ts | 1 + 28 files changed, 66 insertions(+), 8 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index a3199cffc3a44..6852639fcda4d 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -416,6 +416,7 @@ export class CommandCenter { if (!url) { /* __GDPR__ "clone" : { + "owner": "lszomoru", "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ @@ -441,6 +442,7 @@ export class CommandCenter { if (!uris || uris.length === 0) { /* __GDPR__ "clone" : { + "owner": "lszomoru", "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ @@ -499,6 +501,7 @@ export class CommandCenter { /* __GDPR__ "clone" : { + "owner": "lszomoru", "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "openFolder": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } } @@ -518,6 +521,7 @@ export class CommandCenter { if (/already exists and is not an empty directory/.test(err && err.stderr || '')) { /* __GDPR__ "clone" : { + "owner": "lszomoru", "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ @@ -527,6 +531,7 @@ export class CommandCenter { } else { /* __GDPR__ "clone" : { + "owner": "lszomoru", "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ @@ -2920,6 +2925,7 @@ export class CommandCenter { /* __GDPR__ "git.command" : { + "owner": "lszomoru", "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 0327aae486875..ee60d2cb1b1cc 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -192,7 +192,9 @@ export async function _activate(context: ExtensionContext): Promise { /* __GDPR__ "organizeImports.execute" : { + "owner": "mjbvz", "${include}": [ "${TypeScriptCommonProperties}" ] diff --git a/extensions/typescript-language-features/src/languageFeatures/quickFix.ts b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts index 977bc20dc61c7..9ea0780a44689 100644 --- a/extensions/typescript-language-features/src/languageFeatures/quickFix.ts +++ b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts @@ -37,6 +37,7 @@ class ApplyCodeActionCommand implements Command { ): Promise { /* __GDPR__ "quickFix.execute" : { + "owner": "mjbvz", "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, "${include}": [ "${TypeScriptCommonProperties}" @@ -67,6 +68,7 @@ class ApplyFixAllCodeAction implements Command { public async execute(args: ApplyFixAllCodeAction_args): Promise { /* __GDPR__ "quickFixAll.execute" : { + "owner": "mjbvz", "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, "${include}": [ "${TypeScriptCommonProperties}" diff --git a/extensions/typescript-language-features/src/languageFeatures/refactor.ts b/extensions/typescript-language-features/src/languageFeatures/refactor.ts index 392cc5a3e181e..d5d1723533ccc 100644 --- a/extensions/typescript-language-features/src/languageFeatures/refactor.ts +++ b/extensions/typescript-language-features/src/languageFeatures/refactor.ts @@ -36,6 +36,7 @@ class DidApplyRefactoringCommand implements Command { public async execute(args: DidApplyRefactoringCommand_Args): Promise { /* __GDPR__ "refactor.execute" : { + "owner": "mjbvz", "action" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, "${include}": [ "${TypeScriptCommonProperties}" diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index 12e6f70d91b47..a035fbc9fdb6e 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -230,6 +230,7 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe if (!executeInfo.token || !executeInfo.token.isCancellationRequested) { /* __GDPR__ "languageServiceErrorResponse" : { + "owner": "mjbvz", "${include}": [ "${TypeScriptCommonProperties}", "${TypeScriptRequestErrorProperties}" diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 306dd8570eb6c..0fdecc5c95220 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -388,6 +388,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType /* __GDPR__ "tsserver.spawned" : { + "owner": "mjbvz", "${include}": [ "${TypeScriptCommonProperties}" ], @@ -418,6 +419,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType /* __GDPR__ "tsserver.error" : { + "owner": "mjbvz", "${include}": [ "${TypeScriptCommonProperties}" ] @@ -443,6 +445,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.error(`TSServer exited with code: ${code}. Signal: ${signal}`); /* __GDPR__ "tsserver.exitWithCode" : { + "owner": "mjbvz", "code" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "signal" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "${include}": [ @@ -601,6 +604,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType /* __GDPR__ "serviceExited" : { + "owner": "mjbvz", "${include}": [ "${TypeScriptCommonProperties}" ] @@ -846,6 +850,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType private fatalError(command: string, error: unknown): void { /* __GDPR__ "fatalError" : { + "owner": "mjbvz", "${include}": [ "${TypeScriptCommonProperties}", "${TypeScriptRequestErrorProperties}" @@ -977,6 +982,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType /* __GDPR__ "typingsInstalled" : { + "owner": "mjbvz", "installedPackages" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, "installSuccess": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, "typingsInstallerVersion": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, diff --git a/extensions/typescript-language-features/src/utils/largeProjectStatus.ts b/extensions/typescript-language-features/src/utils/largeProjectStatus.ts index 346f95c697960..f6bdbf3721c91 100644 --- a/extensions/typescript-language-features/src/utils/largeProjectStatus.ts +++ b/extensions/typescript-language-features/src/utils/largeProjectStatus.ts @@ -49,6 +49,7 @@ class ExcludeHintItem { this._item.show(); /* __GDPR__ "js.hintProjectExcludes" : { + "owner": "mjbvz", "${include}": [ "${TypeScriptCommonProperties}" ] diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index 19dd0e65b2a4a..7a7a3ec76ba39 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -536,8 +536,10 @@ export class DiagnosticsService implements IDiagnosticsService { try { const stats = await collectWorkspaceStats(folder, ['node_modules', '.git']); type WorkspaceStatsClassification = { - 'workspace.id': { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; - rendererSessionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; + owner: 'lramos15'; + comment: 'Metadata related to the workspace'; + 'workspace.id': { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'A UUID given to a workspace to identify it.' }; + rendererSessionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the session' }; }; type WorkspaceStatsEvent = { 'workspace.id': string | undefined; @@ -548,9 +550,11 @@ export class DiagnosticsService implements IDiagnosticsService { rendererSessionId: workspace.rendererSessionId }); type WorkspaceStatsFileClassification = { - rendererSessionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight' }; - type: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; - count: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true }; + owner: 'lramos15'; + comment: 'Helps us gain insights into what type of files are being used in a workspace'; + rendererSessionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the session.' }; + type: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The type of file' }; + count: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'How many types of that file are present' }; }; type WorkspaceStatsFileEvent = { rendererSessionId: string; diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 9279c520625d4..a2ff49e9bfd13 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -616,6 +616,7 @@ export function reportTelemetry(telemetryService: ITelemetryService, eventName: const errorcode = error ? error instanceof ExtensionManagementError ? error.code : ExtensionManagementErrorCode.Internal : undefined; /* __GDPR__ "extensionGallery:install" : { + "owner": "sandy081", "success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "durationSinceUpdate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -628,6 +629,7 @@ export function reportTelemetry(telemetryService: ITelemetryService, eventName: */ /* __GDPR__ "extensionGallery:uninstall" : { + "owner": "sandy081", "success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, @@ -638,6 +640,7 @@ export function reportTelemetry(telemetryService: ITelemetryService, eventName: */ /* __GDPR__ "extensionGallery:update" : { + "owner": "sandy081", "success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index ad990114c43bf..443107d84b9b3 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -994,6 +994,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi const startTime = new Date().getTime(); /* __GDPR__ "galleryService:downloadVSIX" : { + "owner": "sandy081", "duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "${include}": [ "${GalleryExtensionTelemetryData}" diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 7ea0ca109d473..21b5d7321070c 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -575,6 +575,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { /* __GDPR__ "editorOpened" : { + "owner": "bpasero", "${include}": [ "${EditorTelemetryDescriptor}" ] @@ -611,6 +612,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { /* __GDPR__ "editorClosed" : { + "owner": "bpasero", "${include}": [ "${EditorTelemetryDescriptor}" ] diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 677b8c86440e5..01aac2fa0b6c7 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -541,6 +541,7 @@ CommandsRegistry.registerCommand({ if (ext || await tryInstallHexEditor(notifications, progressService, extensionService, commandService)) { /* __GDPR__ "debug/didViewMemory" : { + "owner": "connor4312", "debugType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ diff --git a/src/vs/workbench/contrib/debug/common/debugTelemetry.ts b/src/vs/workbench/contrib/debug/common/debugTelemetry.ts index a68ce7e798ec4..a39c0651c55ae 100644 --- a/src/vs/workbench/contrib/debug/common/debugTelemetry.ts +++ b/src/vs/workbench/contrib/debug/common/debugTelemetry.ts @@ -18,6 +18,7 @@ export class DebugTelemetry { const extension = dbgr.getMainExtensionDescriptor(); /* __GDPR__ "debugSessionStart" : { + "owner": "connor4312", "type": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "breakpointCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "exceptionBreakpoints": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, @@ -44,6 +45,7 @@ export class DebugTelemetry { /* __GDPR__ "debugSessionStop" : { + "owner": "connor4312", "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "success": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "sessionLengthInSeconds": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index f2da4fedd3460..c0c3e3685403b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -562,6 +562,7 @@ export class ExtensionEditor extends EditorPane { } /* __GDPR__ "extensionGallery:openExtension" : { + "owner": "sandy081", "recommendationReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "${include}": [ "${GalleryExtensionTelemetryData}" diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 86ffb382e28c7..de0d081513a15 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -234,6 +234,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte if (recommendationReason) { /* __GDPR__ "extensionGallery:install:recommendations" : { + "owner": "sandy081", "recommendationReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "${include}": [ "${GalleryExtensionTelemetryData}" diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 764a26d89ecb4..0bc773af868ac 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -1455,6 +1455,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (changed[i]) { /* __GDPR__ "extension:enable" : { + "owner": "sandy081", "${include}": [ "${GalleryExtensionTelemetryData}" ] @@ -1462,6 +1463,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension */ /* __GDPR__ "extension:disable" : { + "owner": "sandy081", "${include}": [ "${GalleryExtensionTelemetryData}" ] diff --git a/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts b/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts index 30f9660514dcf..c725565a55de7 100644 --- a/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts +++ b/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts @@ -133,6 +133,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo const logUserReaction = (userReaction: string) => { /* __GDPR__ "languagePackSuggestion:popup" : { + "owner": "TylerLeonhardt", "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "language": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index 578221a80ef12..b10af56a62597 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -1076,6 +1076,7 @@ export class SearchModel extends Disposable { Promise.race([currentRequest, Event.toPromise(progressEmitter.event)]).finally(() => { /* __GDPR__ "searchResultsFirstRender" : { + "owner": "roblourens", "duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } } */ @@ -1091,6 +1092,7 @@ export class SearchModel extends Disposable { } finally { /* __GDPR__ "searchResultsFinished" : { + "owner": "roblourens", "duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } } */ @@ -1119,6 +1121,7 @@ export class SearchModel extends Disposable { /* __GDPR__ "searchResultsShown" : { + owner": "roblourens", "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "fileCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "options": { "${inline}": [ "${IPatternInfo}" ] }, diff --git a/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts b/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts index 6c2dab8126833..a2c5a2beaf768 100644 --- a/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts @@ -66,6 +66,7 @@ class CESContribution extends Disposable implements IWorkbenchContribution { const sendTelemetry = (userReaction: 'accept' | 'remindLater' | 'neverShowAgain' | 'cancelled') => { /* __GDPR__ "cesSurvey:popup" : { + "owner": "digitarald", "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ @@ -141,7 +142,9 @@ class CESContribution extends Disposable implements IWorkbenchContribution { } } /* __GDPR__ - "cesSurvey:schedule" : { } + "cesSurvey:schedule" : { + "owner": "digitarald" + } */ this.telemetryService.publicLog('cesSurvey:schedule'); diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTags.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTags.ts index c5156ebe74b13..ab86a14c06974 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTags.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTags.ts @@ -89,6 +89,7 @@ export class WorkspaceTags implements IWorkbenchContribution { private reportWorkspaceTags(tags: Tags): void { /* __GDPR__ "workspce.tags" : { + "owner": "lramos15", "${include}": [ "${WorkspaceTags}" ] @@ -116,6 +117,7 @@ export class WorkspaceTags implements IWorkbenchContribution { set.forEach(item => list.push(item)); /* __GDPR__ "workspace.remotes" : { + "owner": "lramos15", "domains" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ @@ -192,6 +194,7 @@ export class WorkspaceTags implements IWorkbenchContribution { if (Object.keys(tags).length) { /* __GDPR__ "workspace.azure" : { + "owner": "lramos15", "${include}": [ "${AzureTags}" ] diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts index 168cbe2fbf285..9c53e0d00f6da 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTypeAheadAddon.ts @@ -1411,6 +1411,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { private _sendLatencyStats(stats: PredictionStats) { /* __GDPR__ "terminalLatencyStats" : { + "owner": "Tyriar", "min" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "max" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "median" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts index 799c6820bbad2..cb1fe2362b23f 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -181,7 +181,9 @@ export class WatermarkContribution extends Disposable implements IWorkbenchContr this.handleEditorPartSize(container, this.editorGroupsService.contentDimension); /* __GDPR__ - "watermark:open" : { } + "watermark:open" : { + "owner": "digitarald" + } */ this.telemetryService.publicLog('watermark:open'); } diff --git a/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts index dabd7358452dc..ea87ef0642a9f 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts @@ -612,6 +612,7 @@ export abstract class ElectronExtensionService extends AbstractExtensionService const sendTelemetry = (userReaction: 'install' | 'enable' | 'cancel') => { /* __GDPR__ "remoteExtensionRecommendations:popup" : { + "owner": "sandy081", "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } } diff --git a/src/vs/workbench/services/timer/browser/timerService.ts b/src/vs/workbench/services/timer/browser/timerService.ts index be619fd11888e..08723845be387 100644 --- a/src/vs/workbench/services/timer/browser/timerService.ts +++ b/src/vs/workbench/services/timer/browser/timerService.ts @@ -514,6 +514,7 @@ export abstract class AbstractTimerService implements ITimerService { // report IStartupMetrics as telemetry /* __GDPR__ "startupTimeVaried" : { + "owner": "jrieken", "${include}": [ "${IStartupMetrics}" ] From 0226ad3aa7ca779076f6d4e5bfd5bceec13170bf Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 24 May 2022 16:02:04 -0700 Subject: [PATCH 302/942] Fix drop data transfer overwriting everything with `uri-list` mime (#150319) --- .../dropIntoEditor/browser/dropIntoEditorContribution.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts index edbc29db56ee5..f45143e4e0ed6 100644 --- a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts +++ b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts @@ -121,10 +121,8 @@ export class DropIntoEditorController extends Disposable implements IEditorContr .map(input => input.resource!.toString()); if (editorData.length) { - const added = new VSDataTransfer(); const str = distinct(editorData).join('\n'); - added.setString(Mimes.uriList.toLowerCase(), str); - return added; + textEditorDataTransfer.setString(Mimes.uriList.toLowerCase(), str); } } From 7635c51d7fbee5fdc591f091ee23dc6c5b0e424e Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 24 May 2022 16:10:38 -0700 Subject: [PATCH 303/942] add telemetry comments (#150317) --- .../platform/terminal/common/xterm/shellIntegrationAddon.ts | 4 ++-- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 2 +- .../contrib/terminal/browser/terminalProcessManager.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 0c02dfbf90802..2e456109e9e53 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -144,7 +144,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private _handleVSCodeSequence(data: string): boolean { const didHandle = this._doHandleVSCodeSequence(data); if (!this._hasUpdatedTelemetry && didHandle) { - this._telemetryService?.publicLog2<{}, { owner: 'meganrogge' }>('terminal/shellIntegrationActivationSucceeded'); + this._telemetryService?.publicLog2<{}, { owner: 'meganrogge'; comment: 'Indicates shell integration was activated' }>('terminal/shellIntegrationActivationSucceeded'); this._hasUpdatedTelemetry = true; if (this._activationTimeout !== undefined) { clearTimeout(this._activationTimeout); @@ -232,7 +232,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private async _ensureCapabilitiesOrAddFailureTelemetry(): Promise { this._activationTimeout = setTimeout(() => { if (!this.capabilities.get(TerminalCapability.CommandDetection) && !this.capabilities.get(TerminalCapability.CwdDetection)) { - this._telemetryService?.publicLog2<{}, { owner: 'meganrogge' }>('terminal/shellIntegrationActivationTimeout'); + this._telemetryService?.publicLog2<{}, { owner: 'meganrogge'; comment: 'Indicates shell integration activation did not occur within 10 seconds' }>('terminal/shellIntegrationActivationTimeout'); this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); } this._hasUpdatedTelemetry = true; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 67614b1df92c2..5c841da10210c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1634,7 +1634,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } if (failedShellIntegrationInjection) { - this._telemetryService.publicLog2<{}, { owner: 'meganrogge' }>('terminal/shellIntegrationFailureProcessExit'); + this._telemetryService.publicLog2<{}, { owner: 'meganrogge'; comment: 'Indicates the process exited when created with shell integration args' }>('terminal/shellIntegrationFailureProcessExit'); } // First onExit to consumers, this can happen after the terminal has already been disposed. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index bf5ad2f3b6852..65d72db0196f0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -335,7 +335,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce this._hasChildProcesses = value; break; case ProcessPropertyType.FailedShellIntegrationActivation: - this._telemetryService?.publicLog2<{}, { owner: 'meganrogge' }>('terminal/shellIntegrationActivationFailureCustomArgs'); + this._telemetryService?.publicLog2<{}, { owner: 'meganrogge'; comment: 'Indicates shell integration was not activated because of custom args' }>('terminal/shellIntegrationActivationFailureCustomArgs'); break; } this._onDidChangeProperty.fire({ type, value }); From 61861d70439cd073fcec9c589cbb18d03400b2a6 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Tue, 24 May 2022 17:28:33 -0700 Subject: [PATCH 304/942] skip localization test until localized strings are present (#150326) --- test/smoke/src/areas/workbench/localization.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts index be81bb17d4c33..a6c86ba20d1d4 100644 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ b/test/smoke/src/areas/workbench/localization.test.ts @@ -9,11 +9,11 @@ import { installAllHandlers } from '../../utils'; export function setup(logger: Logger) { describe('Localization', () => { - // Shared before/after handling installAllHandlers(logger); - it('starts with "DE" locale and verifies title and viewlets text is in German', async function () { + // skipped until translations are available https://github.com/microsoft/vscode/issues/150324 + it.skip('starts with "DE" locale and verifies title and viewlets text is in German', async function () { const app = this.app as Application; await app.workbench.extensions.openExtensionsViewlet(); From d891b49fc0f8fc5e00591657ca225975952b09ca Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 24 May 2022 21:38:12 -0700 Subject: [PATCH 305/942] Enable file DataTransfer on tree views (#150328) For #147481 --- .../api/browser/mainThreadTreeViews.ts | 5 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostTreeViews.ts | 5 +- .../test/browser/mainThreadTreeViews.test.ts | 2 +- .../workbench/browser/parts/views/treeView.ts | 64 ++++++++----------- src/vs/workbench/common/views.ts | 1 + 6 files changed, 35 insertions(+), 44 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index f0923d957010e..56133dc4b206d 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -37,14 +37,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews); } - async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[]; dragMimeTypes: string[]; hasHandleDrag: boolean; hasHandleDrop: boolean }): Promise { + async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[]; dragMimeTypes: string[]; hasHandleDrag: boolean; hasHandleDrop: boolean; supportsFileDataTransfers: boolean }): Promise { this.logService.trace('MainThreadTreeViews#$registerTreeViewDataProvider', treeViewId, options); this.extensionService.whenInstalledExtensionsRegistered().then(() => { const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService); this._dataProviders.set(treeViewId, dataProvider); const dndController = (options.hasHandleDrag || options.hasHandleDrop) - ? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes, options.hasHandleDrag, this._proxy) : undefined; + ? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes, options.hasHandleDrag, options.supportsFileDataTransfers, this._proxy) : undefined; const viewer = this.getTreeView(treeViewId); if (viewer) { // Order is important here. The internal tree isn't created until the dataProvider is set. @@ -201,6 +201,7 @@ class TreeViewDragAndDropController implements ITreeViewDragAndDropController { readonly dropMimeTypes: string[], readonly dragMimeTypes: string[], readonly hasWillDrop: boolean, + readonly supportsFileDataTransfers: boolean, private readonly _proxy: ExtHostTreeViewsShape) { } async handleDrop(dataTransfer: VSDataTransfer, targetTreeItem: ITreeItem | undefined, token: CancellationToken, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index c7ebcd110f804..8975b45a79032 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -258,7 +258,7 @@ export interface MainThreadTextEditorsShape extends IDisposable { } export interface MainThreadTreeViewsShape extends IDisposable { - $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: readonly string[]; dragMimeTypes: readonly string[]; hasHandleDrag: boolean; hasHandleDrop: boolean }): Promise; + $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: readonly string[]; dragMimeTypes: readonly string[]; hasHandleDrag: boolean; hasHandleDrop: boolean; supportsFileDataTransfers: boolean }): Promise; $refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Promise; $reveal(treeViewId: string, itemInfo: { item: ITreeItem; parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise; $setMessage(treeViewId: string, message: string): void; diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index fdbb535e42292..8bdd508109cd4 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -24,7 +24,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Command } from 'vs/editor/common/languages'; import { ITreeViewsService, TreeviewsService } from 'vs/workbench/services/views/common/treeViewsService'; -import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; type TreeItemHandle = string; @@ -92,7 +92,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { const dragMimeTypes = options.dragAndDropController?.dragMimeTypes ?? []; const hasHandleDrag = !!options.dragAndDropController?.handleDrag; const hasHandleDrop = !!options.dragAndDropController?.handleDrop; - const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, dropMimeTypes, dragMimeTypes, hasHandleDrag, hasHandleDrop }); + const supportsFileDataTransfers = isProposedApiEnabled(extension, 'dataTransferFiles'); + const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, dropMimeTypes, dragMimeTypes, hasHandleDrag, hasHandleDrop, supportsFileDataTransfers }); const treeView = this.createExtHostTreeView(viewId, options, extension); return { get onDidCollapseElement() { return treeView.onDidCollapseElement; }, diff --git a/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts b/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts index 1386a01b2348e..4afba6e05e680 100644 --- a/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts @@ -75,7 +75,7 @@ suite('MainThreadHostTreeView', function () { } drain(): any { return null; } }, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService()); - mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false, dropMimeTypes: [], dragMimeTypes: [], hasHandleDrag: false, hasHandleDrop: false }); + mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false, dropMimeTypes: [], dragMimeTypes: [], hasHandleDrag: false, hasHandleDrop: false, supportsFileDataTransfers: false }); await testExtensionService.whenInstalledExtensionsRegistered(); }); diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 50da8ebb64764..051926ebcb402 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -66,7 +66,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; import { ThemeSettings } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITreeViewsService } from 'vs/workbench/services/views/browser/treeViewsService'; -import { CodeDataTransfers } from 'vs/platform/dnd/browser/dnd'; +import { CodeDataTransfers, FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd'; export class TreeViewPane extends ViewPane { @@ -1503,40 +1503,21 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { } const treeDataTransfer = new VSDataTransfer(); const uris: URI[] = []; - let itemsCount = Array.from(originalEvent.dataTransfer.items).reduce((previous, current) => { - if ((current.kind === 'string') || (current.kind === 'file')) { - return previous + 1; - } - return previous; - }, 0); let treeSourceInfo: TreeDragSourceInfo | undefined; let willDropUuid: string | undefined; if (this.treeItemsTransfer.hasData(DraggedTreeItemsIdentifier.prototype)) { willDropUuid = this.treeItemsTransfer.getData(DraggedTreeItemsIdentifier.prototype)![0].identifier; } - await new Promise(resolve => { - function decrementStringCount() { - itemsCount--; - if (itemsCount === 0) { - // Check if there are uris to add and add them - if (uris.length) { - treeDataTransfer.setString(Mimes.uriList, uris.map(uri => uri.toString()).join('\n')); - } - resolve(); - } - } - if (!originalEvent.dataTransfer) { - return; - } - for (const dataItem of originalEvent.dataTransfer.items) { - const type = dataItem.type; - const kind = dataItem.kind; - const convertedType = this.convertKnownMimes(type, kind).type; - if ((INTERNAL_MIME_TYPES.indexOf(convertedType) < 0) - && (convertedType === this.treeMimeType) || (dndController.dropMimeTypes.indexOf(convertedType) >= 0)) { - if (dataItem.kind === 'string') { + await Promise.all([...originalEvent.dataTransfer.items].map(async dataItem => { + const type = dataItem.type; + const kind = dataItem.kind; + const convertedType = this.convertKnownMimes(type, kind).type; + if ((INTERNAL_MIME_TYPES.indexOf(convertedType) < 0) + && (convertedType === this.treeMimeType) || (dndController.dropMimeTypes.indexOf(convertedType) >= 0)) { + if (dataItem.kind === 'string') { + await new Promise(resolve => dataItem.getAsString(dataValue => { if (convertedType === this.treeMimeType) { treeSourceInfo = JSON.parse(dataValue); @@ -1545,20 +1526,27 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { const converted = this.convertKnownMimes(type, kind, dataValue); treeDataTransfer.setString(converted.type, converted.value + ''); } - decrementStringCount(); - }); - } else if (dataItem.kind === 'file') { - const dataValue = dataItem.getAsFile(); - if (dataValue) { - uris.push(URI.file(dataValue.path)); + resolve(); + })); + } else if (dataItem.kind === 'file') { + const file = dataItem.getAsFile(); + if (file) { + uris.push(URI.file(file.path)); + const uri = (file as FileAdditionalNativeProperties).path ? URI.parse((file as FileAdditionalNativeProperties).path!) : undefined; + if (dndController.supportsFileDataTransfers) { + treeDataTransfer.setFile(type, file.name, uri, async () => { + return new Uint8Array(await file.arrayBuffer()); + }); } - decrementStringCount(); } - } else if (dataItem.kind === 'string' || dataItem.kind === 'file') { - decrementStringCount(); } } - }); + })); + + // Check if there are uris to add and add them + if (uris.length) { + treeDataTransfer.setString(Mimes.uriList, uris.map(uri => uri.toString()).join('\n')); + } const additionalWillDropPromise = this.treeViewsDragAndDropService.removeDragOperationTransfer(willDropUuid); if (!additionalWillDropPromise) { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index ac9b39c0c21e1..a255a45c0e26a 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -831,6 +831,7 @@ export interface ITreeViewDataProvider { export interface ITreeViewDragAndDropController { readonly dropMimeTypes: string[]; readonly dragMimeTypes: string[]; + readonly supportsFileDataTransfers: boolean; handleDrag(sourceTreeItemHandles: string[], operationUuid: string, token: CancellationToken): Promise; handleDrop(elements: VSDataTransfer, target: ITreeItem | undefined, token: CancellationToken, operationUuid?: string, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise; } From 9ade6a663662c5e4805038a6e3b3b4ba2f648472 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 25 May 2022 10:01:21 +0200 Subject: [PATCH 306/942] Engineering - Refactor cyclic dependency (#150343) * Refactor to avoid cyclic dependency * Pull request feedback + compilation errors --- .../contrib/debug/browser/debugTaskRunner.ts | 6 +- .../contrib/markers/browser/constants.ts | 33 ---- .../markers/browser/markers.contribution.ts | 143 ++++++++++-------- .../contrib/markers/browser/markers.ts | 29 +--- .../markers/browser/markersTreeViewer.ts | 5 +- .../contrib/markers/browser/markersView.ts | 17 +-- .../markers/browser/markersViewActions.ts | 4 +- .../contrib/markers/common/markers.ts | 39 +++++ .../tasks/browser/abstractTaskService.ts | 4 +- .../tasks/browser/terminalTaskSystem.ts | 8 +- 10 files changed, 142 insertions(+), 146 deletions(-) delete mode 100644 src/vs/workbench/contrib/markers/browser/constants.ts create mode 100644 src/vs/workbench/contrib/markers/common/markers.ts diff --git a/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts b/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts index 4667fc38a3e85..8f4255fb1d2cf 100644 --- a/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts +++ b/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import severity from 'vs/base/common/severity'; import { Event } from 'vs/base/common/event'; -import Constants from 'vs/workbench/contrib/markers/browser/constants'; +import { Markers } from 'vs/workbench/contrib/markers/common/markers'; import { ITaskService, ITaskSummary } from 'vs/workbench/contrib/tasks/common/taskService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; @@ -76,7 +76,7 @@ export class DebugTaskRunner { return TaskRunResult.Success; } if (onTaskErrors === 'showErrors') { - await this.viewsService.openView(Constants.MARKERS_VIEW_ID, true); + await this.viewsService.openView(Markers.MARKERS_VIEW_ID, true); return Promise.resolve(TaskRunResult.Failure); } if (onTaskErrors === 'abort') { @@ -113,7 +113,7 @@ export class DebugTaskRunner { return TaskRunResult.Success; } - await this.viewsService.openView(Constants.MARKERS_VIEW_ID, true); + await this.viewsService.openView(Markers.MARKERS_VIEW_ID, true); return Promise.resolve(TaskRunResult.Failure); } catch (err) { const taskConfigureAction = this.taskService.configureAction(); diff --git a/src/vs/workbench/contrib/markers/browser/constants.ts b/src/vs/workbench/contrib/markers/browser/constants.ts deleted file mode 100644 index a883b3bf14891..0000000000000 --- a/src/vs/workbench/contrib/markers/browser/constants.ts +++ /dev/null @@ -1,33 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { MarkersViewMode } from 'vs/workbench/contrib/markers/browser/markersView'; - -export default { - MARKERS_CONTAINER_ID: 'workbench.panel.markers', - MARKERS_VIEW_ID: 'workbench.panel.markers.view', - MARKERS_VIEW_STORAGE_ID: 'workbench.panel.markers', - MARKER_COPY_ACTION_ID: 'problems.action.copy', - MARKER_COPY_MESSAGE_ACTION_ID: 'problems.action.copyMessage', - RELATED_INFORMATION_COPY_MESSAGE_ACTION_ID: 'problems.action.copyRelatedInformationMessage', - FOCUS_PROBLEMS_FROM_FILTER: 'problems.action.focusProblemsFromFilter', - MARKERS_VIEW_FOCUS_FILTER: 'problems.action.focusFilter', - MARKERS_VIEW_CLEAR_FILTER_TEXT: 'problems.action.clearFilterText', - MARKERS_VIEW_SHOW_MULTILINE_MESSAGE: 'problems.action.showMultilineMessage', - MARKERS_VIEW_SHOW_SINGLELINE_MESSAGE: 'problems.action.showSinglelineMessage', - MARKER_OPEN_ACTION_ID: 'problems.action.open', - MARKER_OPEN_SIDE_ACTION_ID: 'problems.action.openToSide', - MARKER_SHOW_PANEL_ID: 'workbench.action.showErrorsWarnings', - MARKER_SHOW_QUICK_FIX: 'problems.action.showQuickFixes', - TOGGLE_MARKERS_VIEW_ACTION_ID: 'workbench.actions.view.toggleProblems', - - MarkersViewModeContextKey: new RawContextKey('problemsViewMode', MarkersViewMode.Tree), - MarkersViewSmallLayoutContextKey: new RawContextKey(`problemsView.smallLayout`, false), - MarkersTreeVisibilityContextKey: new RawContextKey('problemsVisibility', false), - MarkerFocusContextKey: new RawContextKey('problemFocus', false), - MarkerViewFilterFocusContextKey: new RawContextKey('problemsFilterFocus', false), - RelatedInformationFocusContextKey: new RawContextKey('relatedInformationFocus', false) -}; diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index ffd153246576a..66e8b574bc68d 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -11,16 +11,16 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; import { Marker, RelatedInformation, ResourceMarkers } from 'vs/workbench/contrib/markers/browser/markersModel'; -import { MarkersView, MarkersViewMode } from 'vs/workbench/contrib/markers/browser/markersView'; +import { MarkersView } from 'vs/workbench/contrib/markers/browser/markersView'; import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; -import Constants from 'vs/workbench/contrib/markers/browser/constants'; +import { MarkersViewMode, Markers, MarkersContextKeys } from 'vs/workbench/contrib/markers/common/markers'; import Messages from 'vs/workbench/contrib/markers/browser/messages'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { ActivityUpdater, IMarkersView } from 'vs/workbench/contrib/markers/browser/markers'; +import { IMarkersView } from 'vs/workbench/contrib/markers/browser/markers'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment, IStatusbarEntry } from 'vs/workbench/services/statusbar/browser/statusbar'; import { IMarkerService, MarkerStatistics } from 'vs/platform/markers/common/markers'; import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewsRegistry, IViewsService } from 'vs/workbench/common/views'; @@ -31,53 +31,54 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; +import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: Constants.MARKER_OPEN_ACTION_ID, + id: Markers.MARKER_OPEN_ACTION_ID, weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(Constants.MarkerFocusContextKey), + when: ContextKeyExpr.and(MarkersContextKeys.MarkerFocusContextKey), primary: KeyCode.Enter, mac: { primary: KeyCode.Enter, secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow] }, handler: (accessor, args: any) => { - const markersView = accessor.get(IViewsService).getActiveViewWithId(Constants.MARKERS_VIEW_ID)!; + const markersView = accessor.get(IViewsService).getActiveViewWithId(Markers.MARKERS_VIEW_ID)!; markersView.openFileAtElement(markersView.getFocusElement(), false, false, true); } }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: Constants.MARKER_OPEN_SIDE_ACTION_ID, + id: Markers.MARKER_OPEN_SIDE_ACTION_ID, weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(Constants.MarkerFocusContextKey), + when: ContextKeyExpr.and(MarkersContextKeys.MarkerFocusContextKey), primary: KeyMod.CtrlCmd | KeyCode.Enter, mac: { primary: KeyMod.WinCtrl | KeyCode.Enter }, handler: (accessor, args: any) => { - const markersView = accessor.get(IViewsService).getActiveViewWithId(Constants.MARKERS_VIEW_ID)!; + const markersView = accessor.get(IViewsService).getActiveViewWithId(Markers.MARKERS_VIEW_ID)!; markersView.openFileAtElement(markersView.getFocusElement(), false, true, true); } }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: Constants.MARKER_SHOW_PANEL_ID, + id: Markers.MARKER_SHOW_PANEL_ID, weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: undefined, handler: async (accessor, args: any) => { - await accessor.get(IViewsService).openView(Constants.MARKERS_VIEW_ID); + await accessor.get(IViewsService).openView(Markers.MARKERS_VIEW_ID); } }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: Constants.MARKER_SHOW_QUICK_FIX, + id: Markers.MARKER_SHOW_QUICK_FIX, weight: KeybindingWeight.WorkbenchContrib, - when: Constants.MarkerFocusContextKey, + when: MarkersContextKeys.MarkerFocusContextKey, primary: KeyMod.CtrlCmd | KeyCode.Period, handler: (accessor, args: any) => { - const markersView = accessor.get(IViewsService).getActiveViewWithId(Constants.MARKERS_VIEW_ID)!; + const markersView = accessor.get(IViewsService).getActiveViewWithId(Markers.MARKERS_VIEW_ID)!; const focusedElement = markersView.getFocusElement(); if (focusedElement instanceof Marker) { markersView.showQuickFixes(focusedElement); @@ -125,17 +126,17 @@ const markersViewIcon = registerIcon('markers-view-icon', Codicon.warning, local // markers view container const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ - id: Constants.MARKERS_CONTAINER_ID, + id: Markers.MARKERS_CONTAINER_ID, title: Messages.MARKERS_PANEL_TITLE_PROBLEMS, icon: markersViewIcon, hideIfEmpty: true, order: 0, - ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [Constants.MARKERS_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), - storageId: Constants.MARKERS_VIEW_STORAGE_ID, + ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [Markers.MARKERS_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]), + storageId: Markers.MARKERS_VIEW_STORAGE_ID, }, ViewContainerLocation.Panel, { donotRegisterOpenCommand: true }); Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews([{ - id: Constants.MARKERS_VIEW_ID, + id: Markers.MARKERS_VIEW_ID, containerIcon: markersViewIcon, name: Messages.MARKERS_PANEL_TITLE_PROBLEMS, canToggleVisibility: false, @@ -151,22 +152,21 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews // workbench const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(ActivityUpdater, LifecyclePhase.Restored); // actions registerAction2(class extends ViewAction { constructor() { super({ - id: `workbench.actions.table.${Constants.MARKERS_VIEW_ID}.viewAsTree`, + id: `workbench.actions.table.${Markers.MARKERS_VIEW_ID}.viewAsTree`, title: localize('viewAsTree', "View as Tree"), menu: { id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Constants.MARKERS_VIEW_ID), Constants.MarkersViewModeContextKey.isEqualTo(MarkersViewMode.Table)), + when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Markers.MARKERS_VIEW_ID), MarkersContextKeys.MarkersViewModeContextKey.isEqualTo(MarkersViewMode.Table)), group: 'navigation', order: 3 }, icon: Codicon.listTree, - viewId: Constants.MARKERS_VIEW_ID + viewId: Markers.MARKERS_VIEW_ID }); } @@ -178,16 +178,16 @@ registerAction2(class extends ViewAction { registerAction2(class extends ViewAction { constructor() { super({ - id: `workbench.actions.table.${Constants.MARKERS_VIEW_ID}.viewAsTable`, + id: `workbench.actions.table.${Markers.MARKERS_VIEW_ID}.viewAsTable`, title: localize('viewAsTable', "View as Table"), menu: { id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Constants.MARKERS_VIEW_ID), Constants.MarkersViewModeContextKey.isEqualTo(MarkersViewMode.Tree)), + when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Markers.MARKERS_VIEW_ID), MarkersContextKeys.MarkersViewModeContextKey.isEqualTo(MarkersViewMode.Tree)), group: 'navigation', order: 3 }, icon: Codicon.listFlat, - viewId: Constants.MARKERS_VIEW_ID + viewId: Markers.MARKERS_VIEW_ID }); } @@ -206,15 +206,15 @@ registerAction2(class extends Action2 { }); } async run(accessor: ServicesAccessor): Promise { - accessor.get(IViewsService).openView(Constants.MARKERS_VIEW_ID, true); + accessor.get(IViewsService).openView(Markers.MARKERS_VIEW_ID, true); } }); registerAction2(class extends ViewAction { constructor() { - const when = ContextKeyExpr.and(FocusedViewContext.isEqualTo(Constants.MARKERS_VIEW_ID), Constants.MarkersTreeVisibilityContextKey, Constants.RelatedInformationFocusContextKey.toNegated()); + const when = ContextKeyExpr.and(FocusedViewContext.isEqualTo(Markers.MARKERS_VIEW_ID), MarkersContextKeys.MarkersTreeVisibilityContextKey, MarkersContextKeys.RelatedInformationFocusContextKey.toNegated()); super({ - id: Constants.MARKER_COPY_ACTION_ID, + id: Markers.MARKER_COPY_ACTION_ID, title: { value: localize('copyMarker', "Copy"), original: 'Copy' }, menu: { id: MenuId.ProblemsPanelContext, @@ -226,7 +226,7 @@ registerAction2(class extends ViewAction { primary: KeyMod.CtrlCmd | KeyCode.KeyC, when }, - viewId: Constants.MARKERS_VIEW_ID + viewId: Markers.MARKERS_VIEW_ID }); } async runInView(serviceAccessor: ServicesAccessor, markersView: IMarkersView): Promise { @@ -254,14 +254,14 @@ registerAction2(class extends ViewAction { registerAction2(class extends ViewAction { constructor() { super({ - id: Constants.MARKER_COPY_MESSAGE_ACTION_ID, + id: Markers.MARKER_COPY_MESSAGE_ACTION_ID, title: { value: localize('copyMessage', "Copy Message"), original: 'Copy Message' }, menu: { id: MenuId.ProblemsPanelContext, - when: Constants.MarkerFocusContextKey, + when: MarkersContextKeys.MarkerFocusContextKey, group: 'navigation' }, - viewId: Constants.MARKERS_VIEW_ID + viewId: Markers.MARKERS_VIEW_ID }); } async runInView(serviceAccessor: ServicesAccessor, markersView: IMarkersView): Promise { @@ -276,14 +276,14 @@ registerAction2(class extends ViewAction { registerAction2(class extends ViewAction { constructor() { super({ - id: Constants.RELATED_INFORMATION_COPY_MESSAGE_ACTION_ID, + id: Markers.RELATED_INFORMATION_COPY_MESSAGE_ACTION_ID, title: { value: localize('copyMessage', "Copy Message"), original: 'Copy Message' }, menu: { id: MenuId.ProblemsPanelContext, - when: Constants.RelatedInformationFocusContextKey, + when: MarkersContextKeys.RelatedInformationFocusContextKey, group: 'navigation' }, - viewId: Constants.MARKERS_VIEW_ID + viewId: Markers.MARKERS_VIEW_ID }); } async runInView(serviceAccessor: ServicesAccessor, markersView: IMarkersView): Promise { @@ -298,14 +298,14 @@ registerAction2(class extends ViewAction { registerAction2(class extends ViewAction { constructor() { super({ - id: Constants.FOCUS_PROBLEMS_FROM_FILTER, + id: Markers.FOCUS_PROBLEMS_FROM_FILTER, title: localize('focusProblemsList', "Focus problems view"), keybinding: { - when: Constants.MarkerViewFilterFocusContextKey, + when: MarkersContextKeys.MarkerViewFilterFocusContextKey, weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.DownArrow }, - viewId: Constants.MARKERS_VIEW_ID + viewId: Markers.MARKERS_VIEW_ID }); } async runInView(serviceAccessor: ServicesAccessor, markersView: IMarkersView): Promise { @@ -316,14 +316,14 @@ registerAction2(class extends ViewAction { registerAction2(class extends ViewAction { constructor() { super({ - id: Constants.MARKERS_VIEW_FOCUS_FILTER, + id: Markers.MARKERS_VIEW_FOCUS_FILTER, title: localize('focusProblemsFilter', "Focus problems filter"), keybinding: { - when: FocusedViewContext.isEqualTo(Constants.MARKERS_VIEW_ID), + when: FocusedViewContext.isEqualTo(Markers.MARKERS_VIEW_ID), weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.KeyF }, - viewId: Constants.MARKERS_VIEW_ID + viewId: Markers.MARKERS_VIEW_ID }); } async runInView(serviceAccessor: ServicesAccessor, markersView: IMarkersView): Promise { @@ -334,14 +334,14 @@ registerAction2(class extends ViewAction { registerAction2(class extends ViewAction { constructor() { super({ - id: Constants.MARKERS_VIEW_SHOW_MULTILINE_MESSAGE, + id: Markers.MARKERS_VIEW_SHOW_MULTILINE_MESSAGE, title: { value: localize('show multiline', "Show message in multiple lines"), original: 'Problems: Show message in multiple lines' }, category: localize('problems', "Problems"), menu: { id: MenuId.CommandPalette, - when: ContextKeyExpr.has(getVisbileViewContextKey(Constants.MARKERS_VIEW_ID)) + when: ContextKeyExpr.has(getVisbileViewContextKey(Markers.MARKERS_VIEW_ID)) }, - viewId: Constants.MARKERS_VIEW_ID + viewId: Markers.MARKERS_VIEW_ID }); } async runInView(serviceAccessor: ServicesAccessor, markersView: IMarkersView): Promise { @@ -352,14 +352,14 @@ registerAction2(class extends ViewAction { registerAction2(class extends ViewAction { constructor() { super({ - id: Constants.MARKERS_VIEW_SHOW_SINGLELINE_MESSAGE, + id: Markers.MARKERS_VIEW_SHOW_SINGLELINE_MESSAGE, title: { value: localize('show singleline', "Show message in single line"), original: 'Problems: Show message in single line' }, category: localize('problems', "Problems"), menu: { id: MenuId.CommandPalette, - when: ContextKeyExpr.has(getVisbileViewContextKey(Constants.MARKERS_VIEW_ID)) + when: ContextKeyExpr.has(getVisbileViewContextKey(Markers.MARKERS_VIEW_ID)) }, - viewId: Constants.MARKERS_VIEW_ID + viewId: Markers.MARKERS_VIEW_ID }); } async runInView(serviceAccessor: ServicesAccessor, markersView: IMarkersView): Promise { @@ -370,15 +370,15 @@ registerAction2(class extends ViewAction { registerAction2(class extends ViewAction { constructor() { super({ - id: Constants.MARKERS_VIEW_CLEAR_FILTER_TEXT, + id: Markers.MARKERS_VIEW_CLEAR_FILTER_TEXT, title: localize('clearFiltersText', "Clear filters text"), category: localize('problems', "Problems"), keybinding: { - when: Constants.MarkerViewFilterFocusContextKey, + when: MarkersContextKeys.MarkerViewFilterFocusContextKey, weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.Escape }, - viewId: Constants.MARKERS_VIEW_ID + viewId: Markers.MARKERS_VIEW_ID }); } async runInView(serviceAccessor: ServicesAccessor, markersView: IMarkersView): Promise { @@ -389,16 +389,16 @@ registerAction2(class extends ViewAction { registerAction2(class extends ViewAction { constructor() { super({ - id: `workbench.actions.treeView.${Constants.MARKERS_VIEW_ID}.collapseAll`, + id: `workbench.actions.treeView.${Markers.MARKERS_VIEW_ID}.collapseAll`, title: localize('collapseAll', "Collapse All"), menu: { id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Constants.MARKERS_VIEW_ID), Constants.MarkersViewModeContextKey.isEqualTo(MarkersViewMode.Tree)), + when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Markers.MARKERS_VIEW_ID), MarkersContextKeys.MarkersViewModeContextKey.isEqualTo(MarkersViewMode.Tree)), group: 'navigation', order: 2, }, icon: Codicon.collapseAll, - viewId: Constants.MARKERS_VIEW_ID + viewId: Markers.MARKERS_VIEW_ID }); } async runInView(serviceAccessor: ServicesAccessor, view: IMarkersView): Promise { @@ -409,11 +409,11 @@ registerAction2(class extends ViewAction { registerAction2(class extends Action2 { constructor() { super({ - id: `workbench.actions.treeView.${Constants.MARKERS_VIEW_ID}.filter`, + id: `workbench.actions.treeView.${Markers.MARKERS_VIEW_ID}.filter`, title: localize('filter', "Filter"), menu: { id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Constants.MARKERS_VIEW_ID), Constants.MarkersViewSmallLayoutContextKey.negate()), + when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Markers.MARKERS_VIEW_ID), MarkersContextKeys.MarkersViewSmallLayoutContextKey.negate()), group: 'navigation', order: 1, }, @@ -425,16 +425,16 @@ registerAction2(class extends Action2 { registerAction2(class extends Action2 { constructor() { super({ - id: Constants.TOGGLE_MARKERS_VIEW_ACTION_ID, + id: Markers.TOGGLE_MARKERS_VIEW_ACTION_ID, title: Messages.MARKERS_PANEL_TOGGLE_LABEL, }); } async run(accessor: ServicesAccessor): Promise { const viewsService = accessor.get(IViewsService); - if (viewsService.isViewVisible(Constants.MARKERS_VIEW_ID)) { - viewsService.closeView(Constants.MARKERS_VIEW_ID); + if (viewsService.isViewVisible(Markers.MARKERS_VIEW_ID)) { + viewsService.closeView(Markers.MARKERS_VIEW_ID); } else { - viewsService.openView(Constants.MARKERS_VIEW_ID, true); + viewsService.openView(Markers.MARKERS_VIEW_ID, true); } } }); @@ -514,3 +514,26 @@ class MarkersStatusBarContributions extends Disposable implements IWorkbenchCont } workbenchRegistry.registerWorkbenchContribution(MarkersStatusBarContributions, LifecyclePhase.Restored); + +class ActivityUpdater extends Disposable implements IWorkbenchContribution { + + private readonly activity = this._register(new MutableDisposable()); + + constructor( + @IActivityService private readonly activityService: IActivityService, + @IMarkerService private readonly markerService: IMarkerService + ) { + super(); + this._register(this.markerService.onMarkerChanged(() => this.updateBadge())); + this.updateBadge(); + } + + private updateBadge(): void { + const { errors, warnings, infos } = this.markerService.getStatistics(); + const total = errors + warnings + infos; + const message = localize('totalProblems', 'Total {0} Problems', total); + this.activity.value = this.activityService.showViewActivity(Markers.MARKERS_VIEW_ID, { badge: new NumberBadge(total, () => message) }); + } +} + +workbenchRegistry.registerWorkbenchContribution(ActivityUpdater, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/markers/browser/markers.ts b/src/vs/workbench/contrib/markers/browser/markers.ts index c65df45418ac7..0969177554d7e 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.ts @@ -3,17 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, MutableDisposable, IDisposable } from 'vs/base/common/lifecycle'; -import { IMarkerService } from 'vs/platform/markers/common/markers'; -import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; -import { localize } from 'vs/nls'; -import Constants from './constants'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { MarkersFilters } from 'vs/workbench/contrib/markers/browser/markersViewActions'; import { Event } from 'vs/base/common/event'; import { IView } from 'vs/workbench/common/views'; import { MarkerElement, ResourceMarkers } from 'vs/workbench/contrib/markers/browser/markersModel'; -import { MarkersViewMode } from 'vs/workbench/contrib/markers/browser/markersView'; +import { MarkersViewMode } from 'vs/workbench/contrib/markers/common/markers'; export interface IMarkersView extends IView { @@ -33,24 +27,3 @@ export interface IMarkersView extends IView { setMultiline(multiline: boolean): void; setViewMode(viewMode: MarkersViewMode): void; } - -export class ActivityUpdater extends Disposable implements IWorkbenchContribution { - - private readonly activity = this._register(new MutableDisposable()); - - constructor( - @IActivityService private readonly activityService: IActivityService, - @IMarkerService private readonly markerService: IMarkerService - ) { - super(); - this._register(this.markerService.onMarkerChanged(() => this.updateBadge())); - this.updateBadge(); - } - - private updateBadge(): void { - const { errors, warnings, infos } = this.markerService.getStatistics(); - const total = errors + warnings + infos; - const message = localize('totalProblems', 'Total {0} Problems', total); - this.activity.value = this.activityService.showViewActivity(Constants.MARKERS_VIEW_ID, { badge: new NumberBadge(total, () => message) }); - } -} diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index f6e04799b34cd..92a2abd857bcb 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -48,9 +48,8 @@ import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { Link } from 'vs/platform/opener/browser/link'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { MarkersViewMode } from 'vs/workbench/contrib/markers/browser/markersView'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import Constants from 'vs/workbench/contrib/markers/browser/constants'; +import { MarkersContextKeys, MarkersViewMode } from 'vs/workbench/contrib/markers/common/markers'; interface IResourceMarkersTemplateData { resourceLabel: IResourceLabel; @@ -718,7 +717,7 @@ export class MarkersViewModel extends Disposable { this._multiline = multiline; this._viewMode = viewMode; - this.viewModeContextKey = Constants.MarkersViewModeContextKey.bindTo(this.contextKeyService); + this.viewModeContextKey = MarkersContextKeys.MarkersViewModeContextKey.bindTo(this.contextKeyService); this.viewModeContextKey.set(viewMode); } diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index b20df6c492a34..b3cdba4c228f3 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -10,7 +10,6 @@ import * as dom from 'vs/base/browser/dom'; import { IAction, Action, Separator } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import Constants from 'vs/workbench/contrib/markers/browser/constants'; import { Marker, ResourceMarkers, RelatedInformation, MarkerChangesEvent, MarkersModel, compareMarkersByUri, MarkerElement, MarkerTableItem } from 'vs/workbench/contrib/markers/browser/markersModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MarkersFilterActionViewItem, MarkersFilters, IMarkersFiltersChangeEvent } from 'vs/workbench/contrib/markers/browser/markersViewActions'; @@ -59,6 +58,7 @@ import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/m import { ResourceListDnDHandler } from 'vs/workbench/browser/dnd'; import { ITableContextMenuEvent, ITableEvent } from 'vs/base/browser/ui/table/table'; import { MarkersTable } from 'vs/workbench/contrib/markers/browser/markersTable'; +import { Markers, MarkersContextKeys, MarkersViewMode } from 'vs/workbench/contrib/markers/common/markers'; function createResourceMarkersIterator(resourceMarkers: ResourceMarkers): Iterable> { return Iterable.map(resourceMarkers.markers, m => { @@ -69,11 +69,6 @@ function createResourceMarkersIterator(resourceMarkers: ResourceMarkers): Iterab }); } -export const enum MarkersViewMode { - Table = 'table', - Tree = 'tree' -} - export interface IProblemsWidget { get contextKeyService(): IContextKeyService; @@ -162,8 +157,8 @@ export class MarkersView extends ViewPane implements IMarkersView { @IThemeService themeService: IThemeService, ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); - this.smallLayoutContextKey = Constants.MarkersViewSmallLayoutContextKey.bindTo(this.contextKeyService); - this.panelState = new Memento(Constants.MARKERS_VIEW_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE, StorageTarget.USER); + this.smallLayoutContextKey = MarkersContextKeys.MarkersViewSmallLayoutContextKey.bindTo(this.contextKeyService); + this.panelState = new Memento(Markers.MARKERS_VIEW_STORAGE_ID, storageService).getMemento(StorageScope.WORKSPACE, StorageTarget.USER); this.markersModel = this._register(instantiationService.createInstance(MarkersModel)); this.markersViewModel = this._register(instantiationService.createInstance(MarkersViewModel, this.panelState['multiline'], this.panelState['viewMode'] ?? this.getDefaultViewMode())); @@ -419,8 +414,8 @@ export class MarkersView extends ViewPane implements IMarkersView { this.widget = this.markersViewModel.viewMode === MarkersViewMode.Table ? this.createTable(parent) : this.createTree(parent); this.widgetDisposables.add(this.widget); - const markerFocusContextKey = Constants.MarkerFocusContextKey.bindTo(this.widget.contextKeyService); - const relatedInformationFocusContextKey = Constants.RelatedInformationFocusContextKey.bindTo(this.widget.contextKeyService); + const markerFocusContextKey = MarkersContextKeys.MarkerFocusContextKey.bindTo(this.widget.contextKeyService); + const relatedInformationFocusContextKey = MarkersContextKeys.RelatedInformationFocusContextKey.bindTo(this.widget.contextKeyService); this.widgetDisposables.add(this.widget.onDidChangeFocus(focus => { markerFocusContextKey.set(focus.elements.some(e => e instanceof Marker)); relatedInformationFocusContextKey.set(focus.elements.some(e => e instanceof RelatedInformation)); @@ -915,7 +910,7 @@ class MarkersTree extends WorkbenchObjectTree impleme @IAccessibilityService accessibilityService: IAccessibilityService ) { super(user, container, delegate, renderers, options, contextKeyService, listService, themeService, configurationService, keybindingService, accessibilityService); - this.visibilityContextKey = Constants.MarkersTreeVisibilityContextKey.bindTo(contextKeyService); + this.visibilityContextKey = MarkersContextKeys.MarkersTreeVisibilityContextKey.bindTo(contextKeyService); } collapseMarkers(): void { diff --git a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts index 63dc3458879ab..a885796864786 100644 --- a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts @@ -11,7 +11,6 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import Messages from 'vs/workbench/contrib/markers/browser/messages'; -import Constants from 'vs/workbench/contrib/markers/browser/constants'; import { IThemeService, registerThemingParticipant, ICssStyleCollector, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; @@ -31,6 +30,7 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { IMarkersView } from 'vs/workbench/contrib/markers/browser/markers'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { showHistoryKeybindingHint } from 'vs/platform/history/browser/historyWidgetKeybindingHint'; +import { MarkersContextKeys } from 'vs/workbench/contrib/markers/common/markers'; export interface IMarkersFiltersChangeEvent { filterText?: boolean; @@ -260,7 +260,7 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { ) { super(null, action); this.keybindingService = keybindingService; - this.focusContextKey = Constants.MarkerViewFilterFocusContextKey.bindTo(contextKeyService); + this.focusContextKey = MarkersContextKeys.MarkerViewFilterFocusContextKey.bindTo(contextKeyService); this.delayedFilterUpdate = new Delayer(400); this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); this._register(markersView.onDidFocusFilter(() => this.focus())); diff --git a/src/vs/workbench/contrib/markers/common/markers.ts b/src/vs/workbench/contrib/markers/common/markers.ts new file mode 100644 index 0000000000000..6f3efbbeec615 --- /dev/null +++ b/src/vs/workbench/contrib/markers/common/markers.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export const enum MarkersViewMode { + Table = 'table', + Tree = 'tree' +} + +export namespace Markers { + export const MARKERS_CONTAINER_ID = 'workbench.panel.markers'; + export const MARKERS_VIEW_ID = 'workbench.panel.markers.view'; + export const MARKERS_VIEW_STORAGE_ID = 'workbench.panel.markers'; + export const MARKER_COPY_ACTION_ID = 'problems.action.copy'; + export const MARKER_COPY_MESSAGE_ACTION_ID = 'problems.action.copyMessage'; + export const RELATED_INFORMATION_COPY_MESSAGE_ACTION_ID = 'problems.action.copyRelatedInformationMessage'; + export const FOCUS_PROBLEMS_FROM_FILTER = 'problems.action.focusProblemsFromFilter'; + export const MARKERS_VIEW_FOCUS_FILTER = 'problems.action.focusFilter'; + export const MARKERS_VIEW_CLEAR_FILTER_TEXT = 'problems.action.clearFilterText'; + export const MARKERS_VIEW_SHOW_MULTILINE_MESSAGE = 'problems.action.showMultilineMessage'; + export const MARKERS_VIEW_SHOW_SINGLELINE_MESSAGE = 'problems.action.showSinglelineMessage'; + export const MARKER_OPEN_ACTION_ID = 'problems.action.open'; + export const MARKER_OPEN_SIDE_ACTION_ID = 'problems.action.openToSide'; + export const MARKER_SHOW_PANEL_ID = 'workbench.action.showErrorsWarnings'; + export const MARKER_SHOW_QUICK_FIX = 'problems.action.showQuickFixes'; + export const TOGGLE_MARKERS_VIEW_ACTION_ID = 'workbench.actions.view.toggleProblems'; +} + +export namespace MarkersContextKeys { + export const MarkersViewModeContextKey = new RawContextKey('problemsViewMode', MarkersViewMode.Tree); + export const MarkersViewSmallLayoutContextKey = new RawContextKey(`problemsView.smallLayout`, false); + export const MarkersTreeVisibilityContextKey = new RawContextKey('problemsVisibility', false); + export const MarkerFocusContextKey = new RawContextKey('problemFocus', false); + export const MarkerViewFilterFocusContextKey = new RawContextKey('problemsFilterFocus', false); + export const RelatedInformationFocusContextKey = new RawContextKey('relatedInformationFocus', false); +} diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index dbd096e791cfa..42f69229199ff 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -36,7 +36,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IModelService } from 'vs/editor/common/services/model'; -import Constants from 'vs/workbench/contrib/markers/browser/constants'; +import { Markers } from 'vs/workbench/contrib/markers/common/markers'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -430,7 +430,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } }); - CommandsRegistry.registerCommand('workbench.action.tasks.toggleProblems', () => this.commandService.executeCommand(Constants.TOGGLE_MARKERS_VIEW_ACTION_ID)); + CommandsRegistry.registerCommand('workbench.action.tasks.toggleProblems', () => this.commandService.executeCommand(Markers.TOGGLE_MARKERS_VIEW_ACTION_ID)); CommandsRegistry.registerCommand('workbench.action.tasks.openUserTasks', async () => { const resource = this.getResourceForKind(TaskSourceKind.User); diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 6cb1d3b7f8659..2c06dbc87176b 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -22,7 +22,7 @@ import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/marke import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IModelService } from 'vs/editor/common/services/model'; import { ProblemMatcher, ProblemMatcherRegistry /*, ProblemPattern, getResource */ } from 'vs/workbench/contrib/tasks/common/problemMatcher'; -import Constants from 'vs/workbench/contrib/markers/browser/constants'; +import { Markers } from 'vs/workbench/contrib/markers/common/markers'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { ITerminalProfileResolverService, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -813,7 +813,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { let reveal = task.command.presentation!.reveal; let revealProblems = task.command.presentation!.revealProblems; if (revealProblems === RevealProblemKind.OnProblem) { - this.viewsService.openView(Constants.MARKERS_VIEW_ID, true); + this.viewsService.openView(Markers.MARKERS_VIEW_ID, true); } else if (reveal === RevealKind.Silent) { this.terminalService.setActiveInstance(terminal!); this.terminalGroupService.showPanel(false); @@ -956,7 +956,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { let revealProblems = task.command.presentation!.revealProblems; let revealProblemPanel = terminal && (revealProblems === RevealProblemKind.OnProblem) && (startStopProblemMatcher.numberOfMatches > 0); if (revealProblemPanel) { - this.viewsService.openView(Constants.MARKERS_VIEW_ID); + this.viewsService.openView(Markers.MARKERS_VIEW_ID); } else if (terminal && (reveal === RevealKind.Silent) && ((exitCode !== 0) || (startStopProblemMatcher.numberOfMatches > 0) && startStopProblemMatcher.maxMarkerSeverity && (startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { try { @@ -991,7 +991,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { let showProblemPanel = task.command.presentation && (task.command.presentation.revealProblems === RevealProblemKind.Always); if (showProblemPanel) { - this.viewsService.openView(Constants.MARKERS_VIEW_ID); + this.viewsService.openView(Markers.MARKERS_VIEW_ID); } else if (task.command.presentation && (task.command.presentation.reveal === RevealKind.Always)) { this.terminalService.setActiveInstance(terminal); this.terminalGroupService.showPanel(task.command.presentation.focus); From a6724dcc109871736cb025064470ee3fdfdb61aa Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 25 May 2022 02:50:44 -0700 Subject: [PATCH 307/942] Bump default JS target for scripts in html (#150240) * Bump default JS target for scripts in html For JS/TS files, we target ES2020 by default. However in html files we're still stuck targetting es6 This change aligns the two * use es2020.full (includes dom), fix web, avoid searching in node_modules Co-authored-by: Martin Aeschlimann --- .../server/build/javaScriptLibraryLoader.js | 2 +- .../server/src/modes/javascriptLibs.ts | 2 +- .../server/src/modes/javascriptMode.ts | 13 +++++++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/extensions/html-language-features/server/build/javaScriptLibraryLoader.js b/extensions/html-language-features/server/build/javaScriptLibraryLoader.js index 85792138ba4ac..51e74618042ed 100644 --- a/extensions/html-language-features/server/build/javaScriptLibraryLoader.js +++ b/extensions/html-language-features/server/build/javaScriptLibraryLoader.js @@ -31,7 +31,7 @@ module.exports = function () { queue.push(name); }; - enqueue('es6'); + enqueue('es2020.full'); var result = []; while (queue.length > 0) { diff --git a/extensions/html-language-features/server/src/modes/javascriptLibs.ts b/extensions/html-language-features/server/src/modes/javascriptLibs.ts index bdca89be362c1..7abf94edf2233 100644 --- a/extensions/html-language-features/server/src/modes/javascriptLibs.ts +++ b/extensions/html-language-features/server/src/modes/javascriptLibs.ts @@ -24,7 +24,7 @@ export function loadLibrary(name: string) { try { content = readFileSync(libPath).toString(); } catch (e) { - console.log(`Unable to load library ${name} at ${libPath}: ${e.message}`); + console.log(`Unable to load library ${name} at ${libPath}`); content = ''; } contents[name] = content; diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index 7f9e3f1d85a07..a119a9248ae48 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -19,7 +19,7 @@ import { getSemanticTokens, getSemanticTokenLegend } from './javascriptSemanticT const JS_WORD_REGEX = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g; function getLanguageServiceHost(scriptKind: ts.ScriptKind) { - const compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es6.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic, experimentalDecorators: false }; + const compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es2020.full.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic, experimentalDecorators: false }; let currentTextDocument = TextDocument.create('init', 'javascript', 1, ''); const jsLanguageService = import(/* webpackChunkName: "javascriptLibs" */ './javascriptLibs').then(libs => { @@ -52,7 +52,7 @@ function getLanguageServiceHost(scriptKind: ts.ScriptKind) { }; }, getCurrentDirectory: () => '', - getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es6', + getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es2020.full', readFile: (path: string, _encoding?: string | undefined): string | undefined => { if (path === currentTextDocument.uri) { return currentTextDocument.getText(); @@ -66,6 +66,15 @@ function getLanguageServiceHost(scriptKind: ts.ScriptKind) { } else { return !!libs.loadLibrary(path); } + }, + directoryExists: (path: string): boolean => { + // typescript tries to first find libraries in node_modules/@types and node_modules/@typescript + // there's no node_modules in our setup + if (path.startsWith('node_modules')) { + return false; + } + return true; + } }; return ts.createLanguageService(host); From 1ed660b17e3c01e57937bbfb53aadf21407ceb11 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 25 May 2022 11:59:53 +0200 Subject: [PATCH 308/942] editor gutter accept buttons bug-fixing. --- .../contrib/mergeEditor/browser/editorGutter.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts b/src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts index c439048920920..429457d5a5086 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts @@ -86,13 +86,17 @@ export class EditorGutter< } const top = - this._editor.getTopForLineNumber(gutterItem.range.startLineNumber - 1) - - scrollTop + lineHeight; + (gutterItem.range.startLineNumber === 1 + ? -lineHeight + : this._editor.getTopForLineNumber( + gutterItem.range.startLineNumber - 1 + )) - + scrollTop + + lineHeight; - // +1 line and -lineHeight makes the height to cover view zones at the end of the range. const bottom = - this._editor.getTopForLineNumber(gutterItem.range.endLineNumberExclusive + 1) - - scrollTop - lineHeight; + this._editor.getTopForLineNumber(gutterItem.range.endLineNumberExclusive) - + scrollTop; const height = bottom - top; From e4f7f6a9da6cdc06c5d4f425bb03424cfb604536 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 25 May 2022 03:27:58 -0700 Subject: [PATCH 309/942] Add PasteEditProvider (#107283) For #30066 This adds a new `documentPaste` api proposal that lets extensions hook into copy and paste. This can be used to do things such as: - Create link when pasting an image - Bring along imports when copy and pasting code --- .../markdown-language-features/package.json | 9 +- .../package.nls.json | 3 +- .../src/extension.ts | 4 +- .../src/languageFeatures/copyPaste.ts | 26 +++ .../src/languageFeatures/dropIntoEditor.ts | 64 +++--- .../markdown-language-features/tsconfig.json | 3 +- src/vs/base/common/dataTransfer.ts | 4 + src/vs/editor/browser/dnd.ts | 28 +++ src/vs/editor/common/languages.ts | 9 + .../common/services/languageFeatures.ts | 4 +- .../services/languageFeaturesService.ts | 3 +- .../browser/copyPasteContribution.ts | 25 +++ .../copyPaste/browser/copyPasteController.ts | 195 ++++++++++++++++++ .../browser/dropIntoEditorContribution.ts | 1 + src/vs/editor/editor.all.ts | 1 + .../api/browser/mainThreadLanguageFeatures.ts | 47 ++++- .../workbench/api/common/extHost.api.impl.ts | 4 + .../workbench/api/common/extHost.protocol.ts | 3 + .../api/common/extHostLanguageFeatures.ts | 69 ++++++- .../common/extensionsApiProposals.ts | 1 + .../vscode.proposed.documentPaste.d.ts | 46 +++++ 21 files changed, 505 insertions(+), 44 deletions(-) create mode 100644 extensions/markdown-language-features/src/languageFeatures/copyPaste.ts create mode 100644 src/vs/editor/browser/dnd.ts create mode 100644 src/vs/editor/contrib/copyPaste/browser/copyPasteContribution.ts create mode 100644 src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts create mode 100644 src/vscode-dts/vscode.proposed.documentPaste.d.ts diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 4ef07a0dd65cd..f2ad883243f3b 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -16,7 +16,8 @@ "Programming Languages" ], "enabledApiProposals": [ - "textEditorDrop" + "textEditorDrop", + "documentPaste" ], "activationEvents": [ "onLanguage:markdown", @@ -414,6 +415,12 @@ "markdownDescription": "%configuration.markdown.editor.drop.enabled%", "scope": "resource" }, + "markdown.experimental.editor.pasteLinks.enabled": { + "type": "boolean", + "default": false, + "markdownDescription": "%configuration.markdown.editor.pasteLinks.enabled%", + "scope": "resource" + }, "markdown.experimental.validate.enabled": { "type": "boolean", "scope": "resource", diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index 2c8977cf2e015..1f3598e3fd857 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -28,7 +28,8 @@ "configuration.markdown.links.openLocation.currentGroup": "Open links in the active editor group.", "configuration.markdown.links.openLocation.beside": "Open links beside the active editor.", "configuration.markdown.suggest.paths.enabled.description": "Enable/disable path suggestions for markdown links", - "configuration.markdown.editor.drop.enabled": "Enable/disable dropping into the markdown editor to insert shift. Requires enabling `#workbench.experimental.editor.dropIntoEditor.enabled#`.", + "configuration.markdown.editor.drop.enabled": "Enable/disable dropping into the markdown editor to insert shift. Requires enabling `#workbenck.experimental.editor.dropIntoEditor.enabled#`.", + "configuration.markdown.editor.pasteLinks.enabled": "Enable/disable pasting files into a Markdown editor inserts Markdown links.", "configuration.markdown.experimental.validate.enabled.description": "Enable/disable all error reporting in Markdown files.", "configuration.markdown.experimental.validate.referenceLinks.enabled.description": "Validate reference links in Markdown files, e.g. `[link][ref]`. Requires enabling `#markdown.experimental.validate.enabled#`.", "configuration.markdown.experimental.validate.headerLinks.enabled.description": "Validate links to headers in Markdown files, e.g. `[link](#header)`. Requires enabling `#markdown.experimental.validate.enabled#`.", diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index f088f91391a72..0f2399692e987 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -6,8 +6,9 @@ import * as vscode from 'vscode'; import { CommandManager } from './commandManager'; import * as commands from './commands/index'; -import { register as registerDiagnostics } from './languageFeatures/diagnostics'; +import { registerPasteProvider } from './languageFeatures/copyPaste'; import { MdDefinitionProvider } from './languageFeatures/definitionProvider'; +import { register as registerDiagnostics } from './languageFeatures/diagnostics'; import { MdLinkProvider } from './languageFeatures/documentLinkProvider'; import { MdDocumentSymbolProvider } from './languageFeatures/documentSymbolProvider'; import { registerDropIntoEditor } from './languageFeatures/dropIntoEditor'; @@ -78,6 +79,7 @@ function registerMarkdownLanguageFeatures( MdPathCompletionProvider.register(selector, engine, linkProvider), registerDiagnostics(selector, engine, workspaceContents, linkProvider, commandManager), registerDropIntoEditor(selector), + registerPasteProvider(selector), registerFindFileReferences(commandManager, referencesProvider), ); } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts b/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts new file mode 100644 index 0000000000000..d9e939b463cdc --- /dev/null +++ b/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { tryInsertUriList } from './dropIntoEditor'; + +export function registerPasteProvider(selector: vscode.DocumentSelector) { + return vscode.languages.registerDocumentPasteEditProvider(selector, new class implements vscode.DocumentPasteEditProvider { + + async provideDocumentPasteEdits( + document: vscode.TextDocument, + range: vscode.Range, + dataTransfer: vscode.DataTransfer, + token: vscode.CancellationToken, + ): Promise { + const enabled = vscode.workspace.getConfiguration('markdown', document).get('experimental.editor.pasteLinks.enabled', false); + if (!enabled) { + return; + } + + return tryInsertUriList(document, range, dataTransfer, token); + } + }); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts index d5d7c4d9d2916..2ad71ec0516f0 100644 --- a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts +++ b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts @@ -32,45 +32,45 @@ export function registerDropIntoEditor(selector: vscode.DocumentSelector) { } const replacementRange = new vscode.Range(position, position); - return this.tryInsertUriList(document, replacementRange, dataTransfer, token); + return tryInsertUriList(document, replacementRange, dataTransfer, token); } + }); +} - private async tryInsertUriList(document: vscode.TextDocument, replacementRange: vscode.Range, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { - const urlList = await dataTransfer.get('text/uri-list')?.asString(); - if (!urlList || token.isCancellationRequested) { - return undefined; - } - - const uris: vscode.Uri[] = []; - for (const resource of urlList.split('\n')) { - try { - uris.push(vscode.Uri.parse(resource)); - } catch { - // noop - } - } +export async function tryInsertUriList(document: vscode.TextDocument, replacementRange: vscode.Range, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { + const urlList = await dataTransfer.get('text/uri-list')?.asString(); + if (!urlList || token.isCancellationRequested) { + return undefined; + } - if (!uris.length) { - return; - } + const uris: vscode.Uri[] = []; + for (const resource of urlList.split('\n')) { + try { + uris.push(vscode.Uri.parse(resource)); + } catch { + // noop + } + } - const snippet = new vscode.SnippetString(); - uris.forEach((uri, i) => { - const mdPath = document.uri.scheme === uri.scheme - ? encodeURI(path.relative(URI.Utils.dirname(document.uri).fsPath, uri.fsPath).replace(/\\/g, '/')) - : uri.toString(false); + if (!uris.length) { + return; + } - const ext = URI.Utils.extname(uri).toLowerCase(); - snippet.appendText(imageFileExtensions.has(ext) ? '![' : '['); - snippet.appendTabstop(); - snippet.appendText(`](${mdPath})`); + const snippet = new vscode.SnippetString(); + uris.forEach((uri, i) => { + const mdPath = document.uri.scheme === uri.scheme + ? encodeURI(path.relative(URI.Utils.dirname(document.uri).fsPath, uri.fsPath).replace(/\\/g, '/')) + : uri.toString(false); - if (i <= uris.length - 1 && uris.length > 1) { - snippet.appendText(' '); - } - }); + const ext = URI.Utils.extname(uri).toLowerCase(); + snippet.appendText(imageFileExtensions.has(ext) ? '![' : '['); + snippet.appendTabstop(); + snippet.appendText(`](${mdPath})`); - return new vscode.SnippetTextEdit(replacementRange, snippet); + if (i <= uris.length - 1 && uris.length > 1) { + snippet.appendText(' '); } }); + + return new vscode.SnippetTextEdit(replacementRange, snippet); } diff --git a/extensions/markdown-language-features/tsconfig.json b/extensions/markdown-language-features/tsconfig.json index 775eaa7c0a8d2..7c1d4a7fca897 100644 --- a/extensions/markdown-language-features/tsconfig.json +++ b/extensions/markdown-language-features/tsconfig.json @@ -7,6 +7,7 @@ "src/**/*", "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.textEditorDrop.d.ts", - "../../src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts" + "../../src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts", + "../../src/vscode-dts/vscode.proposed.documentPaste.d.ts" ] } diff --git a/src/vs/base/common/dataTransfer.ts b/src/vs/base/common/dataTransfer.ts index 1f56e79f75190..d5d99bd283b32 100644 --- a/src/vs/base/common/dataTransfer.ts +++ b/src/vs/base/common/dataTransfer.ts @@ -37,6 +37,10 @@ export class VSDataTransfer { this._data.set(mimeType, value); } + public delete(mimeType: string) { + this._data.delete(mimeType); + } + public setString(mimeType: string, stringOrPromise: string | Promise) { this.set(mimeType, { asString: async () => stringOrPromise, diff --git a/src/vs/editor/browser/dnd.ts b/src/vs/editor/browser/dnd.ts new file mode 100644 index 0000000000000..08a3f55878640 --- /dev/null +++ b/src/vs/editor/browser/dnd.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { URI } from 'vs/base/common/uri'; + + +export function toVSDataTransfer(dataTransfer: DataTransfer) { + const vsDataTransfer = new VSDataTransfer(); + for (const item of dataTransfer.items) { + const type = item.type; + if (item.kind === 'string') { + const asStringValue = new Promise(resolve => item.getAsString(resolve)); + vsDataTransfer.setString(type, asStringValue); + } else if (item.kind === 'file') { + const file = item.getAsFile() as null | (File & { path?: string }); + if (file) { + const uri = file.path ? URI.parse(file.path) : undefined; + vsDataTransfer.setFile(type, file.name, uri, async () => { + return new Uint8Array(await file.arrayBuffer()); + }); + } + } + } + return vsDataTransfer; +} diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 647dc1fa131de..6ad570fcd545d 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -721,6 +721,15 @@ export interface CodeActionProvider { _getAdditionalMenuItems?(context: CodeActionContext, actions: readonly CodeAction[]): Command[]; } +/** + * @internal + */ +export interface DocumentPasteEditProvider { + prepareDocumentPaste?(model: model.ITextModel, selection: Selection, dataTransfer: VSDataTransfer, token: CancellationToken): Promise; + + provideDocumentPasteEdits(model: model.ITextModel, selection: Selection, dataTransfer: VSDataTransfer, token: CancellationToken): Promise; +} + /** * Represents a parameter of a callable-signature. A parameter can * have a label and a doc-comment. diff --git a/src/vs/editor/common/services/languageFeatures.ts b/src/vs/editor/common/services/languageFeatures.ts index e0ba241658228..2c80887df1a54 100644 --- a/src/vs/editor/common/services/languageFeatures.ts +++ b/src/vs/editor/common/services/languageFeatures.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { LanguageFeatureRegistry, NotebookInfoResolver } from 'vs/editor/common/languageFeatureRegistry'; -import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages'; +import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentPasteEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const ILanguageFeaturesService = createDecorator('ILanguageFeaturesService'); @@ -25,6 +25,8 @@ export interface ILanguageFeaturesService { readonly codeActionProvider: LanguageFeatureRegistry; + readonly documentPasteEditProvider: LanguageFeatureRegistry; + readonly renameProvider: LanguageFeatureRegistry; readonly documentFormattingEditProvider: LanguageFeatureRegistry; diff --git a/src/vs/editor/common/services/languageFeaturesService.ts b/src/vs/editor/common/services/languageFeaturesService.ts index 2ed7cebcf62e2..4059a90b37098 100644 --- a/src/vs/editor/common/services/languageFeaturesService.ts +++ b/src/vs/editor/common/services/languageFeaturesService.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { LanguageFeatureRegistry, NotebookInfo, NotebookInfoResolver } from 'vs/editor/common/languageFeatureRegistry'; -import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages'; +import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DocumentPasteEditProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -41,6 +41,7 @@ export class LanguageFeaturesService implements ILanguageFeaturesService { readonly documentRangeSemanticTokensProvider = new LanguageFeatureRegistry(this._score.bind(this)); readonly documentSemanticTokensProvider = new LanguageFeatureRegistry(this._score.bind(this)); readonly documentOnDropEditProvider = new LanguageFeatureRegistry(this._score.bind(this)); + readonly documentPasteEditProvider = new LanguageFeatureRegistry(this._score.bind(this)); private _notebookTypeResolver?: NotebookInfoResolver; diff --git a/src/vs/editor/contrib/copyPaste/browser/copyPasteContribution.ts b/src/vs/editor/contrib/copyPaste/browser/copyPasteContribution.ts new file mode 100644 index 0000000000000..55c120d060f65 --- /dev/null +++ b/src/vs/editor/contrib/copyPaste/browser/copyPasteContribution.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { editorConfigurationBaseNode } from 'vs/editor/common/config/editorConfigurationSchema'; +import { CopyPasteController } from 'vs/editor/contrib/copyPaste/browser/copyPasteController'; +import * as nls from 'vs/nls'; +import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; + +registerEditorContribution(CopyPasteController.ID, CopyPasteController); + +Registry.as(Extensions.Configuration).registerConfiguration({ + ...editorConfigurationBaseNode, + properties: { + 'editor.experimental.pasteActions.enabled': { + type: 'boolean', + scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, + description: nls.localize('pasteActions', "Enable/disable running edits from extensions on paste."), + default: false, + }, + } +}); diff --git a/src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts b/src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts new file mode 100644 index 0000000000000..6c203af56cd52 --- /dev/null +++ b/src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts @@ -0,0 +1,195 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { addDisposableListener } from 'vs/base/browser/dom'; +import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Mimes } from 'vs/base/common/mime'; +import { generateUuid } from 'vs/base/common/uuid'; +import { toVSDataTransfer } from 'vs/editor/browser/dnd'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IBulkEditService, ResourceEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; +import { Selection } from 'vs/editor/common/core/selection'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { DocumentPasteEditProvider, SnippetTextEdit, WorkspaceEdit } from 'vs/editor/common/languages'; +import { ITextModel } from 'vs/editor/common/model'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState'; +import { performSnippetEdit } from 'vs/editor/contrib/snippet/browser/snippetController2'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +const vscodeClipboardMime = 'application/vnd.code.copyId'; + +class DefaultPasteEditProvider implements DocumentPasteEditProvider { + + async provideDocumentPasteEdits(model: ITextModel, selection: Selection, dataTransfer: VSDataTransfer, _token: CancellationToken): Promise { + const textDataTransfer = dataTransfer.get(Mimes.text) ?? dataTransfer.get('text'); + if (textDataTransfer) { + const text = await textDataTransfer.asString(); + return { + edits: [new ResourceTextEdit(model.uri, { range: selection, text })] + }; + } + + return undefined; + } +} + +export class CopyPasteController extends Disposable implements IEditorContribution { + + public static readonly ID = 'editor.contrib.copyPasteActionController'; + + public static get(editor: ICodeEditor): CopyPasteController { + return editor.getContribution(CopyPasteController.ID)!; + } + + private readonly _editor: ICodeEditor; + + private _currentClipboardItem: undefined | { + readonly handle: string; + readonly dataTransferPromise: CancelablePromise; + }; + + constructor( + editor: ICodeEditor, + @IBulkEditService private readonly _bulkEditService: IBulkEditService, + @IClipboardService private readonly _clipboardService: IClipboardService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, + ) { + super(); + + this._editor = editor; + + this._languageFeaturesService.documentPasteEditProvider.register('*', new DefaultPasteEditProvider()); + + const container = editor.getContainerDomNode(); + + this._register(addDisposableListener(container, 'copy', (e: ClipboardEvent) => { + if (!e.clipboardData) { + return; + } + + const model = editor.getModel(); + const selection = this._editor.getSelection(); + if (!model || !selection) { + return; + } + + if (!this.arePasteActionsEnabled(model)) { + return; + } + + const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model).filter(x => !!x.prepareDocumentPaste); + if (!providers.length) { + return; + } + + const dataTransfer = toVSDataTransfer(e.clipboardData); + + // Save off a handle pointing to data that VS Code maintains. + const handle = generateUuid(); + e.clipboardData.setData(vscodeClipboardMime, handle); + + const promise = createCancelablePromise(async token => { + const results = await Promise.all(providers.map(provider => { + return provider.prepareDocumentPaste!(model, selection, dataTransfer, token); + })); + + for (const result of results) { + result?.forEach((value, key) => { + dataTransfer.set(key, value); + }); + } + + return dataTransfer; + }); + + this._currentClipboardItem?.dataTransferPromise.cancel(); + this._currentClipboardItem = { handle: handle, dataTransferPromise: promise }; + })); + + this._register(addDisposableListener(container, 'paste', async (e: ClipboardEvent) => { + const selection = this._editor.getSelection(); + if (!e.clipboardData || !selection || !editor.hasModel()) { + return; + } + + const model = editor.getModel(); + if (!this.arePasteActionsEnabled(model)) { + return; + } + + const originalDocVersion = model.getVersionId(); + + const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model); + if (!providers.length) { + return; + } + + const handle = e.clipboardData?.getData(vscodeClipboardMime); + if (typeof handle !== 'string') { + return; + } + + e.preventDefault(); + e.stopImmediatePropagation(); + + const tokenSource = new EditorStateCancellationTokenSource(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection); + + try { + const dataTransfer = toVSDataTransfer(e.clipboardData); + + if (handle && this._currentClipboardItem?.handle === handle) { + const toMergeDataTransfer = await this._currentClipboardItem.dataTransferPromise; + toMergeDataTransfer.forEach((value, key) => { + dataTransfer.set(key, value); + }); + } + + if (!dataTransfer.has(Mimes.uriList)) { + const resources = await this._clipboardService.readResources(); + if (resources.length) { + const value = resources.join('\n'); + dataTransfer.set(Mimes.uriList, { + value, + asString: async () => value, + asFile: () => undefined, + }); + } + } + + dataTransfer.delete(vscodeClipboardMime); + + for (const provider of providers) { + const edit = await provider.provideDocumentPasteEdits(model, selection, dataTransfer, tokenSource.token); + if (originalDocVersion !== model.getVersionId()) { + return; + } + + if (edit) { + if ((edit as WorkspaceEdit).edits) { + await this._bulkEditService.apply(ResourceEdit.convert(edit as WorkspaceEdit), { editor }); + } else { + performSnippetEdit(editor, edit as SnippetTextEdit); + } + return; + } + } + } finally { + tokenSource.dispose(); + } + }, true)); + } + + public arePasteActionsEnabled(model: ITextModel): boolean { + return this._configurationService.getValue('editor.experimental.pasteActions.enabled', { + resource: model.uri + }); + } +} diff --git a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts index f45143e4e0ed6..b713b71a7dd16 100644 --- a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts +++ b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts @@ -186,3 +186,4 @@ class DefaultOnDropProvider implements DocumentOnDropEditProvider { registerEditorContribution(DropIntoEditorController.ID, DropIntoEditorController); + diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index aba1e89aa1db6..1168ef7bb16da 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -15,6 +15,7 @@ import 'vs/editor/contrib/clipboard/browser/clipboard'; import 'vs/editor/contrib/codeAction/browser/codeActionContributions'; import 'vs/editor/contrib/codelens/browser/codelensController'; import 'vs/editor/contrib/colorPicker/browser/colorContributions'; +import 'vs/editor/contrib/copyPaste/browser/copyPasteContribution'; import 'vs/editor/contrib/comment/browser/comment'; import 'vs/editor/contrib/contextmenu/browser/contextmenu'; import 'vs/editor/contrib/cursorUndo/browser/cursorUndo'; diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index ff3bd15f82a46..f89e0698b9d95 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -5,6 +5,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { CancellationError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { combinedDisposable, Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -23,14 +24,13 @@ import { ITextModel } from 'vs/editor/common/model'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; import { DataTransferCache } from 'vs/workbench/api/common/shared/dataTransferCache'; import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import * as search from 'vs/workbench/contrib/search/common/search'; import * as typeh from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { ExtHostContext, ExtHostLanguageFeaturesShape, ICallHierarchyItemDto, ICodeActionDto, ICodeActionProviderMetadataDto, IdentifiableInlineCompletion, IdentifiableInlineCompletions, IDocumentFilterDto, IIndentationRuleDto, IInlayHintDto, ILanguageConfigurationDto, ILanguageWordDefinitionDto, ILinkDto, ILocationDto, ILocationLinkDto, IOnEnterRuleDto, IRegExpDto, ISignatureHelpProviderMetadataDto, ISuggestDataDto, ISuggestDataDtoField, ISuggestResultDtoField, ITypeHierarchyItemDto, IWorkspaceSymbolDto, MainContext, MainThreadLanguageFeaturesShape, reviveWorkspaceEditDto } from '../common/extHost.protocol'; -import { VSDataTransfer } from 'vs/base/common/dataTransfer'; -import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; +import { ExtHostContext, ExtHostLanguageFeaturesShape, ICallHierarchyItemDto, ICodeActionDto, ICodeActionProviderMetadataDto, IdentifiableInlineCompletion, IdentifiableInlineCompletions, IDocumentFilterDto, IIndentationRuleDto, IInlayHintDto, ILanguageConfigurationDto, ILanguageWordDefinitionDto, ILinkDto, ILocationDto, ILocationLinkDto, IOnEnterRuleDto, IRegExpDto, ISignatureHelpProviderMetadataDto, ISuggestDataDto, ISuggestDataDtoField, ISuggestResultDtoField, ITypeHierarchyItemDto, IWorkspaceEditDto, IWorkspaceSymbolDto, MainContext, MainThreadLanguageFeaturesShape, reviveWorkspaceEditDto } from '../common/extHost.protocol'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) export class MainThreadLanguageFeatures extends Disposable implements MainThreadLanguageFeaturesShape { @@ -366,6 +366,47 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread this._registrations.set(handle, this._languageFeaturesService.codeActionProvider.register(selector, provider)); } + // --- copy paste action provider + + $registerPasteEditProvider(handle: number, selector: IDocumentFilterDto[], supportsCopy: boolean): void { + const provider: languages.DocumentPasteEditProvider = { + prepareDocumentPaste: supportsCopy + ? async (model: ITextModel, selection: Selection, dataTransfer: VSDataTransfer, token: CancellationToken): Promise => { + const dataTransferDto = await typeConvert.DataTransfer.toDataTransferDTO(dataTransfer); + if (token.isCancellationRequested) { + return undefined; + } + + const result = await this._proxy.$prepareDocumentPaste(handle, model.uri, selection, dataTransferDto, token); + if (!result) { + return undefined; + } + + + const dataTransferOut = new VSDataTransfer(); + result.items.forEach(([type, item]) => { + dataTransferOut.setString(type, item.asString); + }); + return dataTransferOut; + } + : undefined, + + provideDocumentPasteEdits: async (model: ITextModel, selection: Selection, dataTransfer: VSDataTransfer, token: CancellationToken) => { + const d = await typeConvert.DataTransfer.toDataTransferDTO(dataTransfer); + const result = await this._proxy.$providePasteEdits(handle, model.uri, selection, d, token); + if (!result) { + return; + } else if ((result as IWorkspaceEditDto).edits) { + return reviveWorkspaceEditDto(result as IWorkspaceEditDto); + } else { + return result as languages.SnippetTextEdit; + } + } + }; + + this._registrations.set(handle, this._languageFeaturesService.documentPasteEditProvider.register(selector, provider)); + } + // --- formatting $registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 364272a3ecd45..9c1a1e66eed12 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -457,6 +457,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { return extHostLanguageFeatures.registerCodeActionProvider(extension, checkSelector(selector), provider, metadata); }, + registerDocumentPasteEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentPasteEditProvider): vscode.Disposable { + checkProposedApiEnabled(extension, 'documentPaste'); + return extHostLanguageFeatures.registerDocumentPasteEditProvider(extension, checkSelector(selector), provider); + }, registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider); }, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 8975b45a79032..3b29ae9143251 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -372,6 +372,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void; $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string, supportsResolve: boolean): void; + $registerPasteEditProvider(handle: number, selector: IDocumentFilterDto[], supportsCopy: boolean): void; $registerDocumentFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void; $registerRangeFormattingSupport(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void; $registerOnTypeFormattingSupport(handle: number, selector: IDocumentFilterDto[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void; @@ -1730,6 +1731,8 @@ export interface ExtHostLanguageFeaturesShape { $provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: languages.CodeActionContext, token: CancellationToken): Promise; $resolveCodeAction(handle: number, id: ChainedCacheId, token: CancellationToken): Promise; $releaseCodeActions(handle: number, cacheId: number): void; + $prepareDocumentPaste(handle: number, uri: UriComponents, range: IRange, dataTransfer: DataTransferDTO, token: CancellationToken): Promise; + $providePasteEdits(handle: number, uri: UriComponents, range: IRange, dataTransfer: DataTransferDTO, token: CancellationToken): Promise | undefined>; $provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: languages.FormattingOptions, token: CancellationToken): Promise; $provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: languages.FormattingOptions, token: CancellationToken): Promise; $provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: languages.FormattingOptions, token: CancellationToken): Promise; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 53f5ad0808164..4f06e60d82c4d 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { mixin } from 'vs/base/common/objects'; import type * as vscode from 'vscode'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; -import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, Location, InlineCompletionTriggerKindNew, InlineCompletionTriggerKind } from 'vs/workbench/api/common/extHostTypes'; +import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol, SemanticTokensEdits, SemanticTokens, SemanticTokensEdit, Location, InlineCompletionTriggerKindNew, InlineCompletionTriggerKind, WorkspaceEdit } from 'vs/workbench/api/common/extHostTypes'; import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import * as languages from 'vs/editor/common/languages'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; @@ -31,7 +31,7 @@ import { IdGenerator } from 'vs/base/common/idGenerator'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { Cache } from './cache'; import { StopWatch } from 'vs/base/common/stopwatch'; -import { isCancellationError } from 'vs/base/common/errors'; +import { isCancellationError, NotImplementedError } from 'vs/base/common/errors'; import { raceCancellationError } from 'vs/base/common/async'; import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier'; @@ -485,6 +485,52 @@ class CodeActionAdapter { } } +class DocumentPasteEditProvider { + + constructor( + private readonly _proxy: extHostProtocol.MainThreadLanguageFeaturesShape, + private readonly _documents: ExtHostDocuments, + private readonly _provider: vscode.DocumentPasteEditProvider, + private readonly _handle: number, + ) { } + + async prepareDocumentPaste(resource: URI, range: IRange, dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise { + if (!this._provider.prepareDocumentPaste) { + return undefined; + } + + const doc = this._documents.getDocument(resource); + const vscodeRange = typeConvert.Range.to(range); + + const dataTransfer = typeConvert.DataTransfer.toDataTransfer(dataTransferDto, () => { + throw new NotImplementedError(); + }); + await this._provider.prepareDocumentPaste(doc, vscodeRange, dataTransfer, token); + + return typeConvert.DataTransfer.toDataTransferDTO(dataTransfer); + } + + async providePasteEdits(requestId: number, resource: URI, range: IRange, dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise> { + const doc = this._documents.getDocument(resource); + const vscodeRange = typeConvert.Range.to(range); + + const dataTransfer = typeConvert.DataTransfer.toDataTransfer(dataTransferDto, async (index) => { + return (await this._proxy.$resolveDocumentOnDropFileData(this._handle, requestId, index)).buffer; + }); + + const edit = await this._provider.provideDocumentPasteEdits(doc, vscodeRange, dataTransfer, token); + if (!edit) { + return; + } + + if (edit instanceof WorkspaceEdit) { + return typeConvert.WorkspaceEdit.from(edit); + } else { + return typeConvert.SnippetTextEdit.from(edit as vscode.SnippetTextEdit); + } + } +} + class DocumentFormattingAdapter { constructor( @@ -1769,7 +1815,7 @@ class DocumentOnDropEditAdapter { } type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter - | DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter + | DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentPasteEditProvider | DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter | CompletionsAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter @@ -2413,6 +2459,23 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF Promise.resolve(adapter.provideDocumentOnDropEdits(requestId, URI.revive(resource), position, dataTransferDto, token)), undefined, undefined); } + // --- copy/paste actions + + registerDocumentPasteEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentPasteEditProvider): vscode.Disposable { + const handle = this._nextHandle(); + this._adapter.set(handle, new AdapterData(new DocumentPasteEditProvider(this._proxy, this._documents, provider, handle), extension)); + this._proxy.$registerPasteEditProvider(handle, this._transformDocumentSelector(selector), !!provider.prepareDocumentPaste); + return this._createDisposable(handle); + } + + $prepareDocumentPaste(handle: number, resource: UriComponents, range: IRange, dataTransfer: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise { + return this._withAdapter(handle, DocumentPasteEditProvider, adapter => adapter.prepareDocumentPaste(URI.revive(resource), range, dataTransfer, token), undefined, token); + } + + $providePasteEdits(handle: number, resource: UriComponents, range: IRange, dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise | undefined> { + return this._withAdapter(handle, DocumentPasteEditProvider, adapter => adapter.providePasteEdits(0, URI.revive(resource), range, dataTransferDto, token), undefined, token); + } + // --- configuration private static _serializeRegExp(regExp: RegExp): extHostProtocol.IRegExpDto { diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 3c4bf725a84a3..e2f86dc13a96b 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -18,6 +18,7 @@ export const allApiProposals = Object.freeze({ dataTransferFiles: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts', diffCommand: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffCommand.d.ts', documentFiltersExclusive: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts', + documentPaste: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentPaste.d.ts', editorInsets: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editorInsets.d.ts', extensionRuntime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts', extensionsAny: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionsAny.d.ts', diff --git a/src/vscode-dts/vscode.proposed.documentPaste.d.ts b/src/vscode-dts/vscode.proposed.documentPaste.d.ts new file mode 100644 index 0000000000000..1777c8d4145c0 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.documentPaste.d.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/30066/ + + /** + * Provider invoked when the user copies and pastes code. + */ + interface DocumentPasteEditProvider { + + /** + * Optional method invoked after the user copies text in a file. + * + * During {@link prepareDocumentPaste}, an extension can compute metadata that is attached to + * a {@link DataTransfer} and is passed back to the provider in {@link provideDocumentPasteEdits}. + * + * @param document Document where the copy took place. + * @param range Range being copied in the `document`. + * @param dataTransfer The data transfer associated with the copy. You can store additional values on this for later use in {@link provideDocumentPasteEdits}. + * @param token A cancellation token. + */ + prepareDocumentPaste?(document: TextDocument, range: Range, dataTransfer: DataTransfer, token: CancellationToken): void | Thenable; + + /** + * Invoked before the user pastes into a document. + * + * In this method, extensions can return a workspace edit that replaces the standard pasting behavior. + * + * @param document Document being pasted into + * @param range Currently selected range in the document. + * @param dataTransfer The data transfer associated with the paste. + * @param token A cancellation token. + * + * @return Optional workspace edit that applies the paste. Return undefined to use standard pasting. + */ + provideDocumentPasteEdits(document: TextDocument, range: Range, dataTransfer: DataTransfer, token: CancellationToken): ProviderResult; + } + + namespace languages { + export function registerDocumentPasteEditProvider(selector: DocumentSelector, provider: DocumentPasteEditProvider): Disposable; + } +} From 52c5c31139826f8c3448c22e6c9472376ea40ba8 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 25 May 2022 12:31:56 +0200 Subject: [PATCH 310/942] use fake timers (#150356) --- .../test/common/userDataSyncStoreService.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts index 4790464ce4a82..96031d04737ef 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts @@ -484,14 +484,14 @@ suite('UserDataSyncRequestsSession', () => { assert.fail('Should fail with limit exceeded'); }); - test('requests are handled after session is expired', async () => { + test('requests are handled after session is expired', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject = new RequestsSession(1, 100, requestService, new NullLogService()); await testObject.request('url', {}, CancellationToken.None); await timeout(125); await testObject.request('url', {}, CancellationToken.None); - }); + })); - test('too many requests are thrown after session is expired', async () => { + test('too many requests are thrown after session is expired', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject = new RequestsSession(1, 100, requestService, new NullLogService()); await testObject.request('url', {}, CancellationToken.None); await timeout(125); @@ -505,6 +505,6 @@ suite('UserDataSyncRequestsSession', () => { return; } assert.fail('Should fail with limit exceeded'); - }); + })); }); From 73dda0c06add139307f6be753cea50028bcde05b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 25 May 2022 14:42:17 +0200 Subject: [PATCH 311/942] remove UpdateMode policy (#150357) --- build/azure-pipelines/win32/product-build-win32.yml | 7 ------- build/gulpfile.vscode.js | 3 --- .../platform/update/common/update.config.contribution.ts | 6 +----- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 4b746afc2a108..7501869b57d63 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -136,13 +136,6 @@ steps: displayName: Download Electron condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { node build\lib\policies } - displayName: Generate Group Policy definitions - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 94b22026a0771..7dff164521031 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -330,9 +330,6 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op result = es.merge(result, gulp.src('resources/win32/VisualElementsManifest.xml', { base: 'resources/win32' }) .pipe(rename(product.nameShort + '.VisualElementsManifest.xml'))); - result = es.merge(result, gulp.src('.build/policies/win32/**', { base: '.build/policies/win32' }) - .pipe(rename(f => f.dirname = `policies/${f.dirname}`))); - } else if (platform === 'linux') { result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' }) .pipe(replace('@@PRODNAME@@', product.nameLong)) diff --git a/src/vs/platform/update/common/update.config.contribution.ts b/src/vs/platform/update/common/update.config.contribution.ts index 4134233def670..5ceb95727b510 100644 --- a/src/vs/platform/update/common/update.config.contribution.ts +++ b/src/vs/platform/update/common/update.config.contribution.ts @@ -27,11 +27,7 @@ configurationRegistry.registerConfiguration({ localize('manual', "Disable automatic background update checks. Updates will be available if you manually check for updates."), localize('start', "Check for updates only on startup. Disable automatic background update checks."), localize('default', "Enable automatic update checks. Code will check for updates automatically and periodically.") - ], - policy: { - name: 'UpdateMode', - minimumVersion: '1.67', - } + ] }, 'update.channel': { type: 'string', From 986ef1c76d5c6a031c272c4172d87e6220011834 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 25 May 2022 15:43:11 +0200 Subject: [PATCH 312/942] Disable Terrapin for OSS builds (#150374) --- build/azure-pipelines/product-build-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/azure-pipelines/product-build-pr.yml b/build/azure-pipelines/product-build-pr.yml index 5b09f8ee77e0d..3c45858413e3a 100644 --- a/build/azure-pipelines/product-build-pr.yml +++ b/build/azure-pipelines/product-build-pr.yml @@ -21,7 +21,7 @@ variables: - name: skipComponentGovernanceDetection value: true - name: ENABLE_TERRAPIN - value: true + value: false - name: VSCODE_PUBLISH value: false - name: VSCODE_QUALITY From d3f47f47c643f491f4120b6623da509b325878c1 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 25 May 2022 16:08:54 +0200 Subject: [PATCH 313/942] titlebar with CC has same height as tabs bar --- src/vs/platform/windows/electron-main/window.ts | 2 +- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 2 +- .../workbench/electron-sandbox/parts/titlebar/titlebarPart.ts | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index ced1af3fa1d16..5b8b66794e679 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -277,7 +277,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { const trafficLightUpdater = () => { const on = this.configurationService.getValue(ccConfigKey); if (on) { - this._win.setTrafficLightPosition({ x: 7, y: 8 }); + this._win.setTrafficLightPosition({ x: 7, y: 9 }); } else { this._win.setTrafficLightPosition({ x: 7, y: 6 }); } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index b69113cd97e67..b966ddd27ce67 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -46,7 +46,7 @@ export class TitlebarPart extends Part implements ITitleService { readonly minimumWidth: number = 0; readonly maximumWidth: number = Number.POSITIVE_INFINITY; get minimumHeight(): number { - const ccPadding = this.isCommandCenterVisible ? 4 : 0; + const ccPadding = this.isCommandCenterVisible ? 5 : 0; return (30 + ccPadding) / (this.currentMenubarVisibility === 'hidden' || getZoomFactor() < 1 ? getZoomFactor() : 1); } get maximumHeight(): number { return this.minimumHeight; } diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index f5962e8f33d72..6f1b202254dda 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -41,8 +41,7 @@ export class TitlebarPart extends BrowserTitleBarPart { if (!isMacintosh) { return super.minimumHeight; } - const ccPadding = this.isCommandCenterVisible ? 4 : 0; - return (this.getMacTitlebarSize() + ccPadding) / getZoomFactor(); + return (this.isCommandCenterVisible ? 35 : this.getMacTitlebarSize()) / getZoomFactor(); } override get maximumHeight(): number { return this.minimumHeight; } From 07655f3a23b9b168e7ed351a754fc77bd7faf3ff Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 25 May 2022 16:21:16 +0200 Subject: [PATCH 314/942] use remote cli when in remote terminal (#150372) --- build/gulpfile.vscode.js | 3 ++- resources/darwin/bin/code.sh | 9 +++++++++ resources/linux/bin/code.sh | 15 ++++++++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 7dff164521031..268650959cdc8 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -288,6 +288,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op all = es.merge(all, gulp.src('resources/linux/code.png', { base: '.' })); } else if (platform === 'darwin') { const shortcut = gulp.src('resources/darwin/bin/code.sh') + .pipe(replace('@@APPNAME@@', product.applicationName)) .pipe(rename('bin/code')); all = es.merge(all, shortcut); @@ -333,7 +334,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op } else if (platform === 'linux') { result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' }) .pipe(replace('@@PRODNAME@@', product.nameLong)) - .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(replace('@@APPNAME@@', product.applicationName)) .pipe(rename('bin/' + product.applicationName))); } diff --git a/resources/darwin/bin/code.sh b/resources/darwin/bin/code.sh index eecdf9c68b5b6..8c05872707185 100755 --- a/resources/darwin/bin/code.sh +++ b/resources/darwin/bin/code.sh @@ -3,6 +3,15 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. +# when run in remote terminal, use the remote cli +if [ -n "$VSCODE_IPC_HOOK_CLI" ]; then + REMOTE_CLI="$(which -a '@@APPNAME@@' | grep /remote-cli/)" + if [ -n "$REMOTE_CLI" ]; then + "$REMOTE_CLI" "$@" + exit $? + fi +fi + function app_realpath() { SOURCE=$1 while [ -h "$SOURCE" ]; do diff --git a/resources/linux/bin/code.sh b/resources/linux/bin/code.sh index bfebec1aa8e6b..5fe68cb4f3e08 100755 --- a/resources/linux/bin/code.sh +++ b/resources/linux/bin/code.sh @@ -3,9 +3,18 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. +# when run in remote terminal, use the remote cli +if [ -n "$VSCODE_IPC_HOOK_CLI" ]; then + REMOTE_CLI="$(which -a '@@APPNAME@@' | grep /remote-cli/)" + if [ -n "$REMOTE_CLI" ]; then + "$REMOTE_CLI" "$@" + exit $? + fi +fi + # test that VSCode wasn't installed inside WSL if grep -qi Microsoft /proc/version && [ -z "$DONT_PROMPT_WSL_INSTALL" ]; then - echo "To use @@PRODNAME@@ with the Windows Subsystem for Linux, please install @@PRODNAME@@ in Windows and uninstall the Linux version in WSL. You can then use the \`@@NAME@@\` command in a WSL terminal just as you would in a normal command prompt." 1>&2 + echo "To use @@PRODNAME@@ with the Windows Subsystem for Linux, please install @@PRODNAME@@ in Windows and uninstall the Linux version in WSL. You can then use the \`@@APPNAME@@\` command in a WSL terminal just as you would in a normal command prompt." 1>&2 printf "Do you want to continue anyway? [y/N] " 1>&2 read -r YN YN=$(printf '%s' "$YN" | tr '[:upper:]' '[:lower:]') @@ -44,11 +53,11 @@ else VSCODE_PATH="$(dirname "$(readlink -f "$0")")/.." else # else use the standard install location - VSCODE_PATH="/usr/share/@@NAME@@" + VSCODE_PATH="/usr/share/@@APPNAME@@" fi fi -ELECTRON="$VSCODE_PATH/@@NAME@@" +ELECTRON="$VSCODE_PATH/@@APPNAME@@" CLI="$VSCODE_PATH/resources/app/out/cli.js" ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --ms-enable-electron-run-as-node "$@" exit $? From 38931b6a3db467bf1c5ba51f053ddedbeeed6acd Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 25 May 2022 17:08:15 +0200 Subject: [PATCH 315/942] add `git.experimental.mergeEditor` setting to enable/disable merge editor for conflicting files --- extensions/git/package.json | 6 ++++++ extensions/git/package.nls.json | 1 + extensions/git/src/repository.ts | 37 +++++++++++++++++++++----------- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index bb077b6f23d6a..b5ad659a01055 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -2445,6 +2445,12 @@ ], "markdownDescription": "%config.logLevel%", "scope": "window" + }, + "git.experimental.mergeEditor": { + "type": "boolean", + "default": false, + "markdownDescription": "%config.experimental.mergeEditor%", + "scope": "window" } } }, diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index e43df92114fb1..e1a8c6a9fafcc 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -236,6 +236,7 @@ "config.logLevel.error": "Log only error, and critical information", "config.logLevel.critical": "Log only critical information", "config.logLevel.off": "Log nothing", + "config.experimental.mergeEditor": "Open the _experimental_ merge editor for files that are currently under conflict.", "submenu.explorer": "Git", "submenu.commit": "Commit", "submenu.commit.amend": "Amend", diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index ebe1105ab94e3..61548892e7cc1 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -296,6 +296,10 @@ export class Resource implements SourceControlResourceState { const command = this._commandResolver.resolveChangeCommand(this); await commands.executeCommand(command.command, ...(command.arguments || [])); } + + clone() { + return new Resource(this._commandResolver, this._resourceGroupType, this._resourceUri, this._type, this._useIcons, this._renameResourceUri); + } } export const enum Operation { @@ -602,18 +606,21 @@ class ResourceCommandResolver { resolveChangeCommand(resource: Resource): Command { const title = this.getTitle(resource); - if (!resource.leftUri && resource.rightUri && resource.type === Status.BOTH_MODIFIED) { - return { - command: '_git.openMergeEditor', - title: localize('open.merge', "Open Merge"), - arguments: [resource.rightUri] - }; - } else if (!resource.leftUri) { - return { - command: 'vscode.open', - title: localize('open', "Open"), - arguments: [resource.rightUri, { override: resource.type === Status.BOTH_MODIFIED ? false : undefined }, title] - }; + if (!resource.leftUri) { + const bothModified = resource.type === Status.BOTH_MODIFIED; + if (resource.rightUri && bothModified && workspace.getConfiguration('git').get('experimental.mergeEditor', false)) { + return { + command: '_git.openMergeEditor', + title: localize('open.merge', "Open Merge"), + arguments: [resource.rightUri] + }; + } else { + return { + command: 'vscode.open', + title: localize('open', "Open"), + arguments: [resource.rightUri, { override: bothModified ? false : undefined }, title] + }; + } } else { return { command: 'vscode.diff', @@ -918,6 +925,12 @@ export class Repository implements Disposable { onConfigListener(updateIndexGroupVisibility, this, this.disposables); updateIndexGroupVisibility(); + workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('git.experimental.mergeEditor')) { + this.mergeGroup.resourceStates = this.mergeGroup.resourceStates.map(r => r.clone()); + } + }, undefined, this.disposables); + filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.branchSortOrder', root) || e.affectsConfiguration('git.untrackedChanges', root) From 97f8e66d74b7b2d9cefbcb8db8589aa55c0e9cb9 Mon Sep 17 00:00:00 2001 From: Jonas Dellinger Date: Wed, 25 May 2022 17:16:10 +0200 Subject: [PATCH 316/942] A full editor can be used as git commit message editor (#95266) Co-authored-by: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> --- extensions/git/extension.webpack.config.js | 3 +- extensions/git/package.json | 62 +++++++++++++----- extensions/git/package.nls.json | 2 + extensions/git/src/api/git.d.ts | 3 + extensions/git/src/askpass.ts | 14 +--- extensions/git/src/commands.ts | 39 +++++++++-- extensions/git/src/git.ts | 34 ++++++++-- extensions/git/src/gitEditor/gitEditor.ts | 64 +++++++++++++++++++ extensions/git/src/gitEditor/main.ts | 21 ++++++ .../gitEditor/scripts/git-editor-empty.bat | 1 + .../src/gitEditor/scripts/git-editor-empty.sh | 1 + .../git/src/gitEditor/scripts/git-editor.bat | 4 ++ .../git/src/gitEditor/scripts/git-editor.sh | 4 ++ extensions/git/src/main.ts | 17 ++++- extensions/git/src/repository.ts | 7 ++ extensions/git/tsconfig.json | 2 + src/vs/workbench/contrib/scm/browser/util.ts | 2 +- 17 files changed, 235 insertions(+), 45 deletions(-) create mode 100644 extensions/git/src/gitEditor/gitEditor.ts create mode 100644 extensions/git/src/gitEditor/main.ts create mode 100644 extensions/git/src/gitEditor/scripts/git-editor-empty.bat create mode 100755 extensions/git/src/gitEditor/scripts/git-editor-empty.sh create mode 100644 extensions/git/src/gitEditor/scripts/git-editor.bat create mode 100755 extensions/git/src/gitEditor/scripts/git-editor.sh diff --git a/extensions/git/extension.webpack.config.js b/extensions/git/extension.webpack.config.js index 5efa2052e88e5..ee6203af47e45 100644 --- a/extensions/git/extension.webpack.config.js +++ b/extensions/git/extension.webpack.config.js @@ -13,6 +13,7 @@ module.exports = withDefaults({ context: __dirname, entry: { main: './src/main.ts', - ['askpass-main']: './src/askpass-main.ts' + ['askpass-main']: './src/askpass-main.ts', + ['git-editor-main']: './src/gitEditor/main.ts' } }); diff --git a/extensions/git/package.json b/extensions/git/package.json index de341a095ee61..1c6ca2f4aff84 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -12,7 +12,9 @@ "enabledApiProposals": [ "diffCommand", "contribViewsWelcome", + "resolvers", "scmActionButton", + "scmInput", "scmSelectedProvider", "scmValidation", "timeline" @@ -211,83 +213,99 @@ "command": "git.commit", "title": "%command.commit%", "category": "Git", - "icon": "$(check)" + "icon": "$(check)", + "enablement": "!commitInProgress" }, { "command": "git.commitStaged", "title": "%command.commitStaged%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitEmpty", "title": "%command.commitEmpty%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedSigned", "title": "%command.commitStagedSigned%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedAmend", "title": "%command.commitStagedAmend%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAll", "title": "%command.commitAll%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllSigned", "title": "%command.commitAllSigned%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllAmend", "title": "%command.commitAllAmend%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitNoVerify", "title": "%command.commitNoVerify%", "category": "Git", - "icon": "$(check)" + "icon": "$(check)", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedNoVerify", "title": "%command.commitStagedNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitEmptyNoVerify", "title": "%command.commitEmptyNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedSignedNoVerify", "title": "%command.commitStagedSignedNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitStagedAmendNoVerify", "title": "%command.commitStagedAmendNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllNoVerify", "title": "%command.commitAllNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllSignedNoVerify", "title": "%command.commitAllSignedNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.commitAllAmendNoVerify", "title": "%command.commitAllAmendNoVerify%", - "category": "Git" + "category": "Git", + "enablement": "!commitInProgress" }, { "command": "git.restoreCommitTemplate", @@ -1986,6 +2004,18 @@ "scope": "machine", "description": "%config.defaultCloneDirectory%" }, + "git.useEditorAsCommitInput": { + "type": "boolean", + "scope": "resource", + "description": "%config.useEditorAsCommitInput%", + "default": false + }, + "git.verboseCommit": { + "type": "boolean", + "scope": "resource", + "markdownDescription": "%config.verboseCommit%", + "default": false + }, "git.enableSmartCommit": { "type": "boolean", "scope": "resource", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index ec6911305082f..d70493951b6c8 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -135,6 +135,8 @@ "config.ignoreLimitWarning": "Ignores the warning when there are too many changes in a repository.", "config.ignoreRebaseWarning": "Ignores the warning when it looks like the branch might have been rebased when pulling.", "config.defaultCloneDirectory": "The default location to clone a git repository.", + "config.useEditorAsCommitInput": "Use an editor to author the commit message.", + "config.verboseCommit": "Enable verbose output when `#git.useEditorAsCommitInput#` is enabled.", "config.enableSmartCommit": "Commit all changes when there are no staged changes.", "config.smartCommitChanges": "Control which changes are automatically staged by Smart Commit.", "config.smartCommitChanges.all": "Automatically stage all changes.", diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 7ba58a0e6073a..e552f03f25d6d 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -137,6 +137,8 @@ export interface CommitOptions { empty?: boolean; noVerify?: boolean; requireUserConfig?: boolean; + useEditor?: boolean; + verbose?: boolean; } export interface FetchOptions { @@ -336,4 +338,5 @@ export const enum GitErrorCodes { PatchDoesNotApply = 'PatchDoesNotApply', NoPathFound = 'NoPathFound', UnknownPath = 'UnknownPath', + EmptyCommitMessage = 'EmptyCommitMessage' } diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index 81895a0e0d6dc..ffbd7e48a0e2b 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -6,9 +6,8 @@ import { window, InputBoxOptions, Uri, Disposable, workspace } from 'vscode'; import { IDisposable, EmptyDisposable, toDisposable } from './util'; import * as path from 'path'; -import { IIPCHandler, IIPCServer, createIPCServer } from './ipc/ipcServer'; +import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; import { CredentialsProvider, Credentials } from './api/git'; -import { OutputChannelLogger } from './log'; export class Askpass implements IIPCHandler { @@ -16,16 +15,7 @@ export class Askpass implements IIPCHandler { private cache = new Map(); private credentialsProviders = new Set(); - static async create(outputChannelLogger: OutputChannelLogger, context?: string): Promise { - try { - return new Askpass(await createIPCServer(context)); - } catch (err) { - outputChannelLogger.logError(`Failed to create git askpass IPC: ${err}`); - return new Askpass(); - } - } - - private constructor(private ipc?: IIPCServer) { + constructor(private ipc?: IIPCServer) { if (ipc) { this.disposable = ipc.registerHandler('askpass', this); } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 6852639fcda4d..3597d62a52b13 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1443,6 +1443,14 @@ export class CommandCenter { opts.signoff = true; } + if (config.get('useEditorAsCommitInput')) { + opts.useEditor = true; + + if (config.get('verboseCommit')) { + opts.verbose = true; + } + } + const smartCommitChanges = config.get<'all' | 'tracked'>('smartCommitChanges'); if ( @@ -1490,7 +1498,7 @@ export class CommandCenter { let message = await getCommitMessage(); - if (!message && !opts.amend) { + if (!message && !opts.amend && !opts.useEditor) { return false; } @@ -1550,10 +1558,13 @@ export class CommandCenter { private async commitWithAnyInput(repository: Repository, opts?: CommitOptions): Promise { const message = repository.inputBox.value; + const root = Uri.file(repository.root); + const config = workspace.getConfiguration('git', root); + const getCommitMessage = async () => { let _message: string | undefined = message; - if (!_message) { + if (!_message && !config.get('useEditorAsCommitInput')) { let value: string | undefined = undefined; if (opts && opts.amend && repository.HEAD && repository.HEAD.commit) { @@ -2937,7 +2948,7 @@ export class CommandCenter { }; let message: string; - let type: 'error' | 'warning' = 'error'; + let type: 'error' | 'warning' | 'information' = 'error'; const choices = new Map void>(); const openOutputChannelChoice = localize('open git log', "Open Git Log"); @@ -3000,6 +3011,12 @@ export class CommandCenter { message = localize('missing user info', "Make sure you configure your 'user.name' and 'user.email' in git."); choices.set(localize('learn more', "Learn More"), () => commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-setup-git'))); break; + case GitErrorCodes.EmptyCommitMessage: + message = localize('empty commit', "Commit operation was cancelled due to empty commit message."); + choices.clear(); + type = 'information'; + options.modal = false; + break; default: { const hint = (err.stderr || err.message || String(err)) .replace(/^error: /mi, '') @@ -3021,10 +3038,20 @@ export class CommandCenter { return; } + let result: string | undefined; const allChoices = Array.from(choices.keys()); - const result = type === 'error' - ? await window.showErrorMessage(message, options, ...allChoices) - : await window.showWarningMessage(message, options, ...allChoices); + + switch (type) { + case 'error': + result = await window.showErrorMessage(message, options, ...allChoices); + break; + case 'warning': + result = await window.showWarningMessage(message, options, ...allChoices); + break; + case 'information': + result = await window.showInformationMessage(message, options, ...allChoices); + break; + } if (result) { const resultFn = choices.get(result); diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 8fdab7f659b87..21fa0f0bf8ec5 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1397,20 +1397,37 @@ export class Repository { } async commit(message: string | undefined, opts: CommitOptions = Object.create(null)): Promise { - const args = ['commit', '--quiet', '--allow-empty-message']; + const args = ['commit', '--quiet']; + const options: SpawnOptions = {}; + + if (message) { + options.input = message; + args.push('--file', '-'); + } + + if (opts.verbose) { + args.push('--verbose'); + } if (opts.all) { args.push('--all'); } - if (opts.amend && message) { + if (opts.amend) { args.push('--amend'); } - if (opts.amend && !message) { - args.push('--amend', '--no-edit'); - } else { - args.push('--file', '-'); + if (!opts.useEditor) { + if (!message) { + if (opts.amend) { + args.push('--no-edit'); + } else { + options.input = ''; + args.push('--file', '-'); + } + } + + args.push('--allow-empty-message'); } if (opts.signoff) { @@ -1435,7 +1452,7 @@ export class Repository { } try { - await this.exec(args, !opts.amend || message ? { input: message || '' } : {}); + await this.exec(args, options); } catch (commitErr) { await this.handleCommitError(commitErr); } @@ -1459,6 +1476,9 @@ export class Repository { if (/not possible because you have unmerged files/.test(commitErr.stderr || '')) { commitErr.gitErrorCode = GitErrorCodes.UnmergedChanges; throw commitErr; + } else if (/Aborting commit due to empty commit message/.test(commitErr.stderr || '')) { + commitErr.gitErrorCode = GitErrorCodes.EmptyCommitMessage; + throw commitErr; } try { diff --git a/extensions/git/src/gitEditor/gitEditor.ts b/extensions/git/src/gitEditor/gitEditor.ts new file mode 100644 index 0000000000000..34f8ee9f20fca --- /dev/null +++ b/extensions/git/src/gitEditor/gitEditor.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as path from 'path'; +import { TabInputText, Uri, window, workspace } from 'vscode'; +import { IIPCHandler, IIPCServer } from '../ipc/ipcServer'; +import { EmptyDisposable, IDisposable } from '../util'; + +interface GitEditorRequest { + commitMessagePath?: string; +} + +export class GitEditor implements IIPCHandler { + + private disposable: IDisposable = EmptyDisposable; + + constructor(private ipc?: IIPCServer) { + if (ipc) { + this.disposable = ipc.registerHandler('git-editor', this); + } + } + + async handle({ commitMessagePath }: GitEditorRequest): Promise { + if (commitMessagePath) { + const uri = Uri.file(commitMessagePath); + const doc = await workspace.openTextDocument(uri); + await window.showTextDocument(doc, { preview: false }); + + return new Promise((c) => { + const onDidClose = window.tabGroups.onDidChangeTabs(async (tabs) => { + if (tabs.closed.some(t => t.input instanceof TabInputText && t.input.uri.toString() === uri.toString())) { + onDidClose.dispose(); + return c(true); + } + }); + }); + } + } + + getEnv(): { [key: string]: string } { + if (!this.ipc) { + const fileType = process.platform === 'win32' ? 'bat' : 'sh'; + const gitEditor = path.join(__dirname, `scripts/git-editor-empty.${fileType}`); + + return { + GIT_EDITOR: `'${gitEditor}'` + }; + } + + const fileType = process.platform === 'win32' ? 'bat' : 'sh'; + const gitEditor = path.join(__dirname, `scripts/git-editor.${fileType}`); + + return { + GIT_EDITOR: `'${gitEditor}'`, + VSCODE_GIT_EDITOR_NODE: process.execPath, + VSCODE_GIT_EDITOR_MAIN: path.join(__dirname, 'main.js') + }; + } + + dispose(): void { + this.disposable.dispose(); + } +} diff --git a/extensions/git/src/gitEditor/main.ts b/extensions/git/src/gitEditor/main.ts new file mode 100644 index 0000000000000..661540e003005 --- /dev/null +++ b/extensions/git/src/gitEditor/main.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { IPCClient } from '../ipc/ipcClient'; + +function fatal(err: any): void { + console.error(err); + process.exit(1); +} + +function main(argv: string[]): void { + const ipcClient = new IPCClient('git-editor'); + const commitMessagePath = argv[2]; + + ipcClient.call({ commitMessagePath }).then(() => { + setTimeout(() => process.exit(0), 0); + }).catch(err => fatal(err)); +} + +main(process.argv); diff --git a/extensions/git/src/gitEditor/scripts/git-editor-empty.bat b/extensions/git/src/gitEditor/scripts/git-editor-empty.bat new file mode 100644 index 0000000000000..56eeb8a5801a4 --- /dev/null +++ b/extensions/git/src/gitEditor/scripts/git-editor-empty.bat @@ -0,0 +1 @@ +@ECHO off diff --git a/extensions/git/src/gitEditor/scripts/git-editor-empty.sh b/extensions/git/src/gitEditor/scripts/git-editor-empty.sh new file mode 100755 index 0000000000000..1a2485251c33a --- /dev/null +++ b/extensions/git/src/gitEditor/scripts/git-editor-empty.sh @@ -0,0 +1 @@ +#!/bin/sh diff --git a/extensions/git/src/gitEditor/scripts/git-editor.bat b/extensions/git/src/gitEditor/scripts/git-editor.bat new file mode 100644 index 0000000000000..ce28807cae149 --- /dev/null +++ b/extensions/git/src/gitEditor/scripts/git-editor.bat @@ -0,0 +1,4 @@ +@ECHO off + +set ELECTRON_RUN_AS_NODE=1 +"%VSCODE_GIT_EDITOR_NODE%" "%VSCODE_GIT_EDITOR_MAIN%" %* diff --git a/extensions/git/src/gitEditor/scripts/git-editor.sh b/extensions/git/src/gitEditor/scripts/git-editor.sh new file mode 100755 index 0000000000000..f4ba2e4fd73d2 --- /dev/null +++ b/extensions/git/src/gitEditor/scripts/git-editor.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +ELECTRON_RUN_AS_NODE="1" \ +"$VSCODE_GIT_EDITOR_NODE" "$VSCODE_GIT_EDITOR_MAIN" $@ diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index ee60d2cb1b1cc..e5d99158a4ea1 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -25,6 +25,8 @@ import { GitTimelineProvider } from './timelineProvider'; import { registerAPICommands } from './api/api1'; import { TerminalEnvironmentManager } from './terminal'; import { OutputChannelLogger } from './log'; +import { createIPCServer, IIPCServer } from './ipc/ipcServer'; +import { GitEditor } from './gitEditor/gitEditor'; const deactivateTasks: { (): Promise }[] = []; @@ -60,10 +62,21 @@ async function createModel(context: ExtensionContext, outputChannelLogger: Outpu return !skip; }); - const askpass = await Askpass.create(outputChannelLogger, context.storagePath); + let ipc: IIPCServer | undefined = undefined; + + try { + ipc = await createIPCServer(context.storagePath); + } catch (err) { + outputChannelLogger.logError(`Failed to create git IPC: ${err}`); + } + + const askpass = new Askpass(ipc); disposables.push(askpass); - const environment = askpass.getEnv(); + const gitEditor = new GitEditor(ipc); + disposables.push(gitEditor); + + const environment = { ...askpass.getEnv(), ...gitEditor.getEnv() }; const terminalEnvironmentManager = new TerminalEnvironmentManager(context, environment); disposables.push(terminalEnvironmentManager); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index c337f91a7f530..b5ed89ac94b19 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -450,6 +450,13 @@ class ProgressManager { const onDidChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git', Uri.file(this.repository.root))); onDidChange(_ => this.updateEnablement()); this.updateEnablement(); + + this.repository.onDidChangeOperations(() => { + const commitInProgress = this.repository.operations.isRunning(Operation.Commit); + + this.repository.sourceControl.inputBox.enabled = !commitInProgress; + commands.executeCommand('setContext', 'commitInProgress', commitInProgress); + }); } private updateEnablement(): void { diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index 13997275056b0..899f8c9c33dcf 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -11,7 +11,9 @@ "src/**/*", "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.diffCommand.d.ts", + "../../src/vscode-dts/vscode.proposed.resolvers.d.ts", "../../src/vscode-dts/vscode.proposed.scmActionButton.d.ts", + "../../src/vscode-dts/vscode.proposed.scmInput.d.ts", "../../src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts", "../../src/vscode-dts/vscode.proposed.scmValidation.d.ts", "../../src/vscode-dts/vscode.proposed.tabs.d.ts", diff --git a/src/vs/workbench/contrib/scm/browser/util.ts b/src/vs/workbench/contrib/scm/browser/util.ts index 80d5d19302d5d..c8f4524d017ba 100644 --- a/src/vs/workbench/contrib/scm/browser/util.ts +++ b/src/vs/workbench/contrib/scm/browser/util.ts @@ -37,7 +37,7 @@ export function isSCMResource(element: any): element is ISCMResource { return !!(element as ISCMResource).sourceUri && isSCMResourceGroup((element as ISCMResource).resourceGroup); } -const compareActions = (a: IAction, b: IAction) => a.id === b.id; +const compareActions = (a: IAction, b: IAction) => a.id === b.id && a.enabled === b.enabled; export function connectPrimaryMenu(menu: IMenu, callback: (primary: IAction[], secondary: IAction[]) => void, primaryGroup?: string): IDisposable { let cachedDisposable: IDisposable = Disposable.None; From ce01702bbec4419cfb76ab10d2b04ce09d3f6600 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 25 May 2022 17:43:02 +0200 Subject: [PATCH 317/942] make git "accept from merge editor" command save the document and close the merge editor (uses heuristic to identify merge editor tab) --- extensions/git/src/commands.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index da619d4feb53c..2805dcab76840 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -6,7 +6,7 @@ import * as os from 'os'; import * as path from 'path'; import * as picomatch from 'picomatch'; -import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity } from 'vscode'; +import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator'; @@ -1082,15 +1082,30 @@ export class CommandCenter { @command('git.acceptMerge') async acceptMerge(uri: Uri | unknown): Promise { - // TODO@jrieken make this proper, needs help from SCM folks if (!(uri instanceof Uri)) { return; } const repository = this.model.getRepository(uri); if (!repository) { + console.log(`FAILED to accept merge because uri ${uri.toString()} doesn't belong to any repository`); return; } + + const doc = workspace.textDocuments.find(doc => doc.uri.toString() === uri.toString()); + if (!doc) { + console.log(`FAILED to accept merge because uri ${uri.toString()} doesn't match a document`); + return; + } + + await doc.save(); await repository.add([uri]); + + // TODO@jrieken there isn't a `TabInputTextMerge` instance yet, till now the merge editor + // uses the `TabInputText` for the out-resource and we use that to identify and CLOSE the tab + const { activeTab } = window.tabGroups.activeTabGroup; + if (activeTab && activeTab?.input instanceof TabInputText && activeTab.input.uri.toString() === uri.toString()) { + await window.tabGroups.close(activeTab, true); + } } private async _stageChanges(textEditor: TextEditor, changes: LineChange[]): Promise { From cdeaf55221397893f30ea52473d6f057476f9586 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 25 May 2022 17:55:57 +0200 Subject: [PATCH 318/942] Implements synchronized scrolling. --- .../contrib/audioCues/browser/observable.ts | 10 ++ .../mergeEditor/browser/mergeEditor.ts | 97 +++++++++++++++++-- .../mergeEditor/browser/mergeEditorModel.ts | 6 +- .../contrib/mergeEditor/browser/model.ts | 83 ++++++++++++++++ 4 files changed, 187 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/audioCues/browser/observable.ts b/src/vs/workbench/contrib/audioCues/browser/observable.ts index 937f4b2f9cfc5..c3974606057cb 100644 --- a/src/vs/workbench/contrib/audioCues/browser/observable.ts +++ b/src/vs/workbench/contrib/audioCues/browser/observable.ts @@ -612,3 +612,13 @@ export function wasEventTriggeredRecently(event: Event, timeoutMs: number, return observable; } + +/** + * This ensures the observable is kept up-to-date. + * This is useful when the observables `get` method is used. +*/ +export function keepAlive(observable: IObservable): IDisposable { + return autorun(reader => { + observable.read(reader); + }, 'keep-alive'); +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 0f0ef7c4cba35..184d081277042 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -9,6 +9,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview'; import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { IAction } from 'vs/base/common/actions'; +import { findLast } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; @@ -40,9 +41,10 @@ import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorControl, IEditorOpenContext } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { autorun, derivedObservable, IObservable, ITransaction, ObservableValue } from 'vs/workbench/contrib/audioCues/browser/observable'; +import { autorun, derivedObservable, IObservable, ITransaction, keepAlive, ObservableValue } from 'vs/workbench/contrib/audioCues/browser/observable'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; +import { LineRange, ModifiedBaseRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; import { applyObservableDecorations, n, ReentrancyBarrier, setStyle } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { EditorGutter, IGutterItemInfo, IGutterItemView } from './editorGutter'; @@ -82,27 +84,67 @@ export class MergeEditor extends EditorPane { const reentrancyBarrier = new ReentrancyBarrier(); + const input1ResultMapping = derivedObservable('input1ResultMapping', reader => { + const model = this.input1View.model.read(reader); + if (!model) { + return undefined; + } + const resultDiffs = model.resultDiffs.read(reader); + const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input1, model.input1LinesDiffs, model.result, resultDiffs); + return modifiedBaseRanges; + }); + const input2ResultMapping = derivedObservable('input2ResultMapping', reader => { + const model = this.input2View.model.read(reader); + if (!model) { + return undefined; + } + const resultDiffs = model.resultDiffs.read(reader); + const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input2, model.input2LinesDiffs, model.result, resultDiffs); + return modifiedBaseRanges; + }); + + this._register(keepAlive(input1ResultMapping)); + this._register(keepAlive(input2ResultMapping)); + this._store.add(this.input1View.editor.onDidScrollChange(c => { if (c.scrollTopChanged) { reentrancyBarrier.runExclusively(() => { + const mapping = input1ResultMapping.get(); + if (!mapping) { + return; + } + synchronizeScrolling(this.input1View.editor, this.inputResultView.editor, mapping, 1); this.input2View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); - this.inputResultView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); }); } })); + this._store.add(this.input2View.editor.onDidScrollChange(c => { if (c.scrollTopChanged) { reentrancyBarrier.runExclusively(() => { + const mapping = input2ResultMapping.get(); + if (!mapping) { + return; + } + synchronizeScrolling(this.input2View.editor, this.inputResultView.editor, mapping, 1); this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); - this.inputResultView.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); }); } })); this._store.add(this.inputResultView.editor.onDidScrollChange(c => { if (c.scrollTopChanged) { reentrancyBarrier.runExclusively(() => { - this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); - this.input2View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); + const mapping = input1ResultMapping.get(); + if (!mapping) { + return; + } + synchronizeScrolling(this.inputResultView.editor, this.input1View.editor, mapping, 2); + + const mapping2 = input2ResultMapping.get(); + if (!mapping2) { + return; + } + synchronizeScrolling(this.inputResultView.editor, this.input2View.editor, mapping2, 2); }); } })); @@ -265,6 +307,49 @@ export class MergeEditor extends EditorPane { } } +function flip(value: 1 | 2): 1 | 2 { + return value === 1 ? 2 : 1; +} + +function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: CodeEditorWidget, mapping: ModifiedBaseRange[], sourceNumber: 1 | 2) { + const visibleRanges = scrollingEditor.getVisibleRanges(); + if (visibleRanges.length === 0) { + return; + } + const topLineNumber = visibleRanges[0].startLineNumber - 1; + + const firstBefore = findLast(mapping, r => r.getInputRange(sourceNumber).startLineNumber <= topLineNumber); + let sourceRange: LineRange; + let targetRange: LineRange; + + const targetNumber = flip(sourceNumber); + + if (firstBefore && firstBefore.getInputRange(sourceNumber).contains(topLineNumber)) { + sourceRange = firstBefore.getInputRange(sourceNumber); + targetRange = firstBefore.getInputRange(targetNumber); + } else if (firstBefore && firstBefore.getInputRange(sourceNumber).isEmpty && firstBefore.getInputRange(sourceNumber).startLineNumber === topLineNumber) { + sourceRange = firstBefore.getInputRange(sourceNumber).deltaEnd(1); + targetRange = firstBefore.getInputRange(targetNumber).deltaEnd(1); + } else { + const delta = firstBefore ? firstBefore.getInputRange(targetNumber).endLineNumberExclusive - firstBefore.getInputRange(sourceNumber).endLineNumberExclusive : 0; + sourceRange = new LineRange(topLineNumber, 1); + targetRange = new LineRange(topLineNumber + delta, 1); + } + + // sourceRange is not empty! + + const resultStartTopPx = targetEditor.getTopForLineNumber(targetRange.startLineNumber); + const resultEndPx = targetEditor.getTopForLineNumber(targetRange.endLineNumberExclusive); + + const sourceStartTopPx = scrollingEditor.getTopForLineNumber(sourceRange.startLineNumber); + const sourceEndPx = scrollingEditor.getTopForLineNumber(sourceRange.endLineNumberExclusive); + + const factor = (scrollingEditor.getScrollTop() - sourceStartTopPx) / (sourceEndPx - sourceStartTopPx); + const resultScrollPosition = resultStartTopPx + (resultEndPx - resultStartTopPx) * factor; + + targetEditor.setScrollTop(resultScrollPosition, ScrollType.Immediate); +} + interface ICodeEditorViewOptions { readonly: boolean; } @@ -272,7 +357,7 @@ interface ICodeEditorViewOptions { abstract class CodeEditorView extends Disposable { private readonly _model = new ObservableValue(undefined, 'model'); - protected readonly model: IObservable = this._model; + readonly model: IObservable = this._model; protected readonly htmlElements = n('div.code-view', [ n('div.title', { $: 'title' }), diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts index 024aa737d426a..2c23f66b5020a 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -101,8 +101,8 @@ export class MergeEditorModel extends EditorModel { readonly input2Detail: string | undefined, readonly input2Description: string | undefined, readonly result: ITextModel, - private readonly input1LinesDiffs: readonly LineDiff[], - private readonly input2LinesDiffs: readonly LineDiff[], + public readonly input1LinesDiffs: readonly LineDiff[], + public readonly input2LinesDiffs: readonly LineDiff[], resultDiffs: LineDiff[], private readonly editorWorkerService: IEditorWorkerService ) { @@ -164,7 +164,7 @@ export class MergeEditorModel extends EditorModel { this.input2LinesDiffs ); - private readonly modifiedBaseRangeStateStores = new Map>( + private readonly modifiedBaseRangeStateStores: ReadonlyMap> = new Map( this.modifiedBaseRanges.map(s => ([s, new ObservableValue(ModifiedBaseRangeState.default, 'State')])) ); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model.ts b/src/vs/workbench/contrib/mergeEditor/browser/model.ts index 9a045854182c7..9ee3176fc92f0 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model.ts @@ -102,6 +102,14 @@ export class LineRange { public equals(originalRange: LineRange) { return this.startLineNumber === originalRange.startLineNumber && this.lineCount === originalRange.lineCount; } + + public contains(lineNumber: number): boolean { + return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; + } + + public deltaEnd(delta: number): LineRange { + return new LineRange(this.startLineNumber, this.lineCount + delta); + } } export class LineDiff { @@ -263,6 +271,9 @@ export class ModifiedBaseRange { const result = new Array(); function pushAndReset() { + if (currentDiffs[0].length === 0 && currentDiffs[1].length === 0) { + return; + } result.push(new ModifiedBaseRange( baseTextModel, input1TextModel, @@ -416,3 +427,75 @@ export class ModifiedBaseRangeState { return arr.join(','); } } + +/* +export class LineMappings { + public static fromDiffs( + diffs1: readonly LineDiff[], + diffs2: readonly LineDiff[], + inputLineCount: number, + ): LineMappings { + const compareByStartLineNumber = compareBy( + (d) => d.originalRange.startLineNumber, + numberComparator + ); + + const diffs = diffs1 + .map((diff) => ({ source: 0 as 0 | 1, diff })) + .concat(diffs2.map((diff) => ({ source: 1 as const, diff }))); + + diffs.sort(compareBy(d => d.diff, compareByStartLineNumber)); + + const currentDiffs = [ + new Array(), + new Array(), + ]; + let deltaFromBaseToInput = [0, 0]; + + const result = new Array(); + + function pushAndReset() { + result.push(LineMapping.create( + baseTextModel, + input1TextModel, + currentDiffs[0], + deltaFromBaseToInput[0], + input2TextModel, + currentDiffs[1], + deltaFromBaseToInput[1], + )); + currentDiffs[0] = []; + currentDiffs[1] = []; + } + + let currentRange: LineRange | undefined; + + for (const diff of diffs) { + const range = diff.diff.originalRange; + if (currentRange && !currentRange.touches(range)) { + pushAndReset(); + } + deltaFromBaseToInput[diff.source] = diff.diff.resultingDeltaFromOriginalToModified; + currentRange = currentRange ? currentRange.join(range) : range; + currentDiffs[diff.source].push(diff.diff); + } + pushAndReset(); + + return result; + } + + constructor(private readonly lineMappings: LineMapping[]) {} +} + +// A lightweight ModifiedBaseRange. Maybe they can be united? +export class LineMapping { + public static create(input: LineDiff, ): LineMapping { + + } + + constructor( + public readonly inputRange: LineRange, + public readonly resultRange: LineRange + ) { } +} +*/ From 63121f8039cb8dd6485655488a10252cdcaa64e5 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 25 May 2022 18:02:31 +0200 Subject: [PATCH 319/942] Code polishing --- .../workbench/contrib/mergeEditor/browser/icons.ts | 10 ---------- .../mergeEditor/browser/media/mergeEditor.css | 2 +- .../contrib/mergeEditor/browser/mergeEditor.ts | 12 +++++++----- 3 files changed, 8 insertions(+), 16 deletions(-) delete mode 100644 src/vs/workbench/contrib/mergeEditor/browser/icons.ts diff --git a/src/vs/workbench/contrib/mergeEditor/browser/icons.ts b/src/vs/workbench/contrib/mergeEditor/browser/icons.ts deleted file mode 100644 index a37f0e66faf87..0000000000000 --- a/src/vs/workbench/contrib/mergeEditor/browser/icons.ts +++ /dev/null @@ -1,10 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Codicon } from 'vs/base/common/codicons'; -import { localize } from 'vs/nls'; -import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; - -export const acceptConflictIcon = registerIcon('merge-accept-conflict-icon', Codicon.check, localize('acceptConflictIcon', 'TODO.')); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css index abae99ac16ca2..363dffaaf6a31 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css +++ b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css @@ -33,7 +33,7 @@ cursor: pointer; } -.merge-accept-foo { +.merge-base-range-projection { background-color: rgb(170 175 170 / 15%); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 184d081277042..8c95edcc1fa95 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -443,8 +443,8 @@ class InputCodeEditorView extends CodeEditorView { range: new Range(range.startLineNumber, 1, range.endLineNumberExclusive - 1, 1), options: { isWholeLine: true, - className: 'merge-accept-foo', - description: 'foo2' + className: 'merge-base-range-projection', + description: 'Base Range Projection' } }); } @@ -509,7 +509,8 @@ class MergeConflictGutterItemView extends Disposable implements IGutterItemView< target.classList.add('merge-accept-gutter-marker'); target.classList.add(item.range.lineCount > 1 ? 'multi-line' : 'single-line'); - const checkBox = new Toggle({ isChecked: false, title: 'TODO', icon: Codicon.check, actionClassName: 'monaco-checkbox' }); + // TODO: Tri-State-Toggle, localized title + const checkBox = new Toggle({ isChecked: false, title: 'Accept Merge', icon: Codicon.check, actionClassName: 'monaco-checkbox' }); checkBox.domNode.classList.add('accept-conflict-group'); this._register( @@ -550,8 +551,9 @@ class ResultCodeEditorView extends CodeEditorView { range: new Range(range.startLineNumber, 1, range.endLineNumberExclusive - 1, 1), options: { isWholeLine: true, - className: 'merge-accept-foo', - description: 'foo2' + // TODO + className: 'merge-base-range-projection', + description: 'Result Diff' } }); } From 4cf4438fc001e0c925905af51577cfc1db72f42c Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 25 May 2022 09:33:13 -0700 Subject: [PATCH 320/942] Ensure execution and kernel status visible when there are source commands contributed. --- .../browser/contrib/editorStatusBar/editorStatusBar.ts | 6 +++--- .../contrib/notebook/browser/controller/coreActions.ts | 4 ++-- .../notebook/browser/controller/executeActions.ts | 3 ++- .../browser/viewParts/notebookEditorWidgetContextKeys.ts | 9 ++++++++- .../contrib/notebook/common/notebookContextKeys.ts | 1 + 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index 20c8c594f12fe..4de014d82baee 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -22,7 +22,7 @@ import { ViewContainerLocation } from 'vs/workbench/common/views'; import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSION_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { CENTER_ACTIVE_CELL } from 'vs/workbench/contrib/notebook/browser/contrib/navigation/arrow'; import { NOTEBOOK_ACTIONS_CATEGORY, SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; -import { NOTEBOOK_MISSING_KERNEL_EXTENSION, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { NOTEBOOK_MISSING_KERNEL_EXTENSION, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_KERNEL_SOURCE_COUNT } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { getNotebookEditorFromEditorPane, INotebookEditor, KERNEL_EXTENSIONS } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { configureKernelIcon, selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; @@ -48,7 +48,7 @@ registerAction2(class extends Action2 { id: MenuId.EditorTitle, when: ContextKeyExpr.and( NOTEBOOK_IS_ACTIVE_EDITOR, - ContextKeyExpr.or(NOTEBOOK_KERNEL_COUNT.notEqualsTo(0), NOTEBOOK_MISSING_KERNEL_EXTENSION), + ContextKeyExpr.or(NOTEBOOK_KERNEL_COUNT.notEqualsTo(0), NOTEBOOK_KERNEL_SOURCE_COUNT.notEqualsTo(0), NOTEBOOK_MISSING_KERNEL_EXTENSION), ContextKeyExpr.notEquals('config.notebook.globalToolbar', true) ), group: 'navigation', @@ -56,7 +56,7 @@ registerAction2(class extends Action2 { }, { id: MenuId.NotebookToolbar, when: ContextKeyExpr.and( - ContextKeyExpr.or(NOTEBOOK_KERNEL_COUNT.notEqualsTo(0), NOTEBOOK_MISSING_KERNEL_EXTENSION), + ContextKeyExpr.or(NOTEBOOK_KERNEL_COUNT.notEqualsTo(0), NOTEBOOK_KERNEL_SOURCE_COUNT.notEqualsTo(0), NOTEBOOK_MISSING_KERNEL_EXTENSION), ContextKeyExpr.equals('config.notebook.globalToolbar', true) ), group: 'status', diff --git a/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts index 1c601fbf36f12..f7b01782aa0ec 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts @@ -10,7 +10,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { getNotebookEditorFromEditorPane, IActiveNotebookEditor, ICellViewModel, cellRangeToViewCells } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_KERNEL_SOURCE_COUNT } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { ICellRange, isICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorCommandsContext } from 'vs/workbench/common/editor'; @@ -276,7 +276,7 @@ export abstract class NotebookCellAction extends abstract override runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise; } -export const executeNotebookCondition = ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0); +export const executeNotebookCondition = ContextKeyExpr.or(ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), ContextKeyExpr.greater(NOTEBOOK_KERNEL_SOURCE_COUNT.key, 0)); interface IMultiCellArgs { ranges: ICellRange[]; diff --git a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts index 351b57c8befe5..58227467a6624 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts @@ -15,7 +15,7 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { EditorsOrder } from 'vs/workbench/common/editor'; import { insertCell } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations'; import { cellExecutionArgs, CellToolbarOrder, CELL_TITLE_CELL_GROUP_ID, executeNotebookCondition, getContextFromActiveEditor, getContextFromUri, INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext, INotebookCommandContext, NotebookAction, NotebookCellAction, NotebookMultiCellAction, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, parseMultiCellExecutionArgs } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; -import { NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_HAS_RUNNING_CELL, NOTEBOOK_INTERRUPTIBLE_KERNEL, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_MISSING_KERNEL_EXTENSION } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_HAS_RUNNING_CELL, NOTEBOOK_INTERRUPTIBLE_KERNEL, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_KERNEL_SOURCE_COUNT, NOTEBOOK_MISSING_KERNEL_EXTENSION } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { CellKind, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -41,6 +41,7 @@ export const executeCondition = ContextKeyExpr.and( NOTEBOOK_CELL_TYPE.isEqualTo('code'), ContextKeyExpr.or( ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0), + ContextKeyExpr.greater(NOTEBOOK_KERNEL_SOURCE_COUNT.key, 0), NOTEBOOK_MISSING_KERNEL_EXTENSION )); diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts index a90477db542a2..3702ac26261f5 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts @@ -6,7 +6,7 @@ import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ICellViewModel, INotebookEditorDelegate, KERNEL_EXTENSIONS } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { NOTEBOOK_CELL_TOOLBAR_LOCATION, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_HAS_RUNNING_CELL, NOTEBOOK_INTERRUPTIBLE_KERNEL, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_MISSING_KERNEL_EXTENSION, NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON, NOTEBOOK_VIEW_TYPE } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { NOTEBOOK_CELL_TOOLBAR_LOCATION, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_HAS_RUNNING_CELL, NOTEBOOK_INTERRUPTIBLE_KERNEL, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL_SOURCE_COUNT, NOTEBOOK_MISSING_KERNEL_EXTENSION, NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON, NOTEBOOK_VIEW_TYPE } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -15,6 +15,7 @@ export class NotebookEditorContextKeys { private readonly _notebookKernel: IContextKey; private readonly _notebookKernelCount: IContextKey; + private readonly _notebookKernelSourceCount: IContextKey; private readonly _notebookKernelSelected: IContextKey; private readonly _interruptibleKernel: IContextKey; private readonly _someCellRunning: IContextKey; @@ -44,6 +45,7 @@ export class NotebookEditorContextKeys { this._hasOutputs = NOTEBOOK_HAS_OUTPUTS.bindTo(contextKeyService); this._viewType = NOTEBOOK_VIEW_TYPE.bindTo(contextKeyService); this._missingKernelExtension = NOTEBOOK_MISSING_KERNEL_EXTENSION.bindTo(contextKeyService); + this._notebookKernelSourceCount = NOTEBOOK_KERNEL_SOURCE_COUNT.bindTo(contextKeyService); this._cellToolbarLocation = NOTEBOOK_CELL_TOOLBAR_LOCATION.bindTo(contextKeyService); this._handleDidChangeModel(); @@ -52,6 +54,7 @@ export class NotebookEditorContextKeys { this._disposables.add(_editor.onDidChangeModel(this._handleDidChangeModel, this)); this._disposables.add(_notebookKernelService.onDidAddKernel(this._updateKernelContext, this)); this._disposables.add(_notebookKernelService.onDidChangeSelectedNotebooks(this._updateKernelContext, this)); + this._disposables.add(_notebookKernelService.onDidChangeSourceActions(this._updateKernelContext, this)); this._disposables.add(_editor.notebookOptions.onDidChangeOptions(this._updateForNotebookOptions, this)); this._disposables.add(_extensionService.onDidChangeExtensions(this._updateForInstalledExtension, this)); this._disposables.add(_notebookExecutionStateService.onDidChangeCellExecution(this._updateForCellExecution, this)); @@ -61,6 +64,7 @@ export class NotebookEditorContextKeys { this._disposables.dispose(); this._viewModelDisposables.dispose(); this._notebookKernelCount.reset(); + this._notebookKernelSourceCount.reset(); this._interruptibleKernel.reset(); this._someCellRunning.reset(); this._viewType.reset(); @@ -142,12 +146,15 @@ export class NotebookEditorContextKeys { private _updateKernelContext(): void { if (!this._editor.hasModel()) { this._notebookKernelCount.reset(); + this._notebookKernelSourceCount.reset(); this._interruptibleKernel.reset(); return; } const { selected, all } = this._notebookKernelService.getMatchingKernel(this._editor.textModel); + const sourceActions = this._notebookKernelService.getSourceActions(); this._notebookKernelCount.set(all.length); + this._notebookKernelSourceCount.set(sourceActions.length); this._interruptibleKernel.set(selected?.implementsInterrupt ?? false); this._notebookKernelSelected.set(Boolean(selected)); this._notebookKernel.set(selected?.id ?? ''); diff --git a/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts b/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts index 689f9ca995eaa..882a76d6092ed 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts @@ -43,6 +43,7 @@ export const NOTEBOOK_CELL_RESOURCE = new RawContextKey('notebookCellRes // Kernels export const NOTEBOOK_KERNEL = new RawContextKey('notebookKernel', undefined); export const NOTEBOOK_KERNEL_COUNT = new RawContextKey('notebookKernelCount', 0); +export const NOTEBOOK_KERNEL_SOURCE_COUNT = new RawContextKey('notebookKernelSourceCount', 0); export const NOTEBOOK_KERNEL_SELECTED = new RawContextKey('notebookKernelSelected', false); export const NOTEBOOK_INTERRUPTIBLE_KERNEL = new RawContextKey('notebookInterruptibleKernel', false); export const NOTEBOOK_MISSING_KERNEL_EXTENSION = new RawContextKey('notebookMissingKernelExtension', false); From 3837e55e31f1e44c514516873a4457c292607d58 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 25 May 2022 09:38:54 -0700 Subject: [PATCH 321/942] update bash status correctly (#150322) --- .../contrib/terminal/browser/media/shellIntegration-bash.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh index ec96e7d3fd380..3c0cb9cfb8741 100755 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh @@ -96,6 +96,7 @@ if [[ -n "$__vsc_original_trap" ]]; then fi __vsc_preexec() { + __vsc_status="$?" eval ${__vsc_original_trap} PS1="$__vsc_prior_prompt" if [ -z "${__vsc_in_command_execution-}" ]; then @@ -110,7 +111,6 @@ __vsc_prompt_cmd_original() { if [[ ${IFS+set} ]]; then __vsc_original_ifs="$IFS" fi - __vsc_status="$?" if [[ "$__vsc_original_prompt_command" =~ .+\;.+ ]]; then IFS=';' else @@ -124,7 +124,7 @@ __vsc_prompt_cmd_original() { unset IFS fi for ((i = 0; i < ${#ADDR[@]}; i++)); do - # unset IFS + (exit ${__vsc_status}) builtin eval ${ADDR[i]} done __vsc_precmd From 3e8d674698b814e2d65bc206b9c19926655e4519 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 25 May 2022 11:09:59 -0700 Subject: [PATCH 322/942] Clicking outline in notebook should reveal cell near the top of the editor (#150336) Fixes #123828 --- .../contrib/outline/notebookOutline.ts | 8 ++++-- .../notebook/browser/notebookBrowser.ts | 7 +++++ .../notebook/browser/notebookEditorWidget.ts | 8 +++++- .../notebook/browser/view/notebookCellList.ts | 26 +++++++++++++++---- .../browser/view/notebookRenderingCommon.ts | 1 + 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts index 473ff42494ff5..da58d351ae4f6 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts @@ -8,7 +8,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; import { combinedDisposable, IDisposable, Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { IActiveNotebookEditor, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellRevealType, IActiveNotebookEditor, ICellViewModel, INotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IOutline, IOutlineComparator, IOutlineCreator, IOutlineListConfig, IOutlineService, IQuickPickDataSource, IQuickPickOutlineElement, OutlineChangeEvent, OutlineConfigKeys, OutlineTarget } from 'vs/workbench/services/outline/browser/outline'; @@ -583,7 +583,11 @@ export class NotebookCellOutline extends Disposable implements IOutline { await this._editorService.openEditor({ resource: entry.cell.uri, - options: { ...options, override: this._editor.input?.editorId }, + options: { + ...options, + override: this._editor.input?.editorId, + cellRevealType: CellRevealType.NearTopIfOutsideViewport + } as INotebookEditorOptions, }, sideBySide ? SIDE_GROUP : undefined); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 75452af0bdeb4..780ff8fc21a75 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -277,8 +277,15 @@ export interface INotebookDeltaCellStatusBarItems { items: INotebookCellStatusBarItem[]; } + +export enum CellRevealType { + NearTopIfOutsideViewport, + CenterIfOutsideViewport +} + export interface INotebookEditorOptions extends ITextEditorOptions { readonly cellOptions?: ITextResourceEditorInput; + readonly cellRevealType?: CellRevealType; readonly cellSelections?: ICellRange[]; readonly isReadOnly?: boolean; readonly viewState?: INotebookEditorViewState; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 2f1bf44324cfd..51c50c5d6a385 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -49,7 +49,7 @@ import { contrastBorder, diffInserted, diffRemoved, editorBackground, errorForeg import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { PANEL_BORDER, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugColors'; -import { CellEditState, CellFindMatchWithIndex, CellFocusMode, CellLayoutContext, IActiveNotebookEditorDelegate, IBaseCellEditorOptions, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IInsetRenderOutput, IModelDecorationsChangeAccessor, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorDelegate, INotebookEditorMouseEvent, INotebookEditorOptions, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookWebviewMessage, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFindMatchWithIndex, CellFocusMode, CellLayoutContext, CellRevealType, IActiveNotebookEditorDelegate, IBaseCellEditorOptions, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IInsetRenderOutput, IModelDecorationsChangeAccessor, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorDelegate, INotebookEditorMouseEvent, INotebookEditorOptions, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookWebviewMessage, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService'; import { notebookDebug } from 'vs/workbench/contrib/notebook/browser/notebookLogger'; @@ -1244,6 +1244,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD const selection = cellOptions.options?.selection; if (selection) { await this.revealLineInCenterIfOutsideViewportAsync(cell, selection.startLineNumber); + } else if (options?.cellRevealType === CellRevealType.NearTopIfOutsideViewport) { + await this.revealNearTopIfOutsideViewportAync(cell); } else { await this.revealInCenterIfOutsideViewportAsync(cell); } @@ -2028,6 +2030,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._listViewInfoAccessor.revealInCenter(cell); } + revealNearTopIfOutsideViewportAync(cell: ICellViewModel) { + return this._listViewInfoAccessor.revealNearTopIfOutsideViewportAync(cell); + } + async revealLineInViewAsync(cell: ICellViewModel, line: number): Promise { return this._listViewInfoAccessor.revealLineInViewAsync(cell, line); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 0318aa6a2888b..c68b97e7df572 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -39,7 +39,8 @@ const enum CellRevealType { const enum CellRevealPosition { Top, Center, - Bottom + Bottom, + NearTop } function getVisibleCells(cells: CellViewModel[], hiddenRanges: ICellRange[]) { @@ -861,7 +862,15 @@ export class NotebookCellList extends WorkbenchList implements ID const index = this._getViewIndexUpperBound(cell); if (index >= 0) { - return this._revealInCenterIfOutsideViewportAsync(index); + return this._revealIfOutsideViewportAsync(index, CellRevealPosition.Center); + } + } + + async revealNearTopIfOutsideViewportAync(cell: ICellViewModel): Promise { + const index = this._getViewIndexUpperBound(cell); + + if (index >= 0) { + return this._revealIfOutsideViewportAsync(index, CellRevealPosition.NearTop); } } @@ -1200,8 +1209,8 @@ export class NotebookCellList extends WorkbenchList implements ID } } - private async _revealInCenterIfOutsideViewportAsync(viewIndex: number): Promise { - this._revealInternal(viewIndex, true, CellRevealPosition.Center); + private async _revealIfOutsideViewportAsync(viewIndex: number, revealPosition: CellRevealPosition): Promise { + this._revealInternal(viewIndex, true, revealPosition); const element = this.view.element(viewIndex); // wait for the editor to be created only if the cell is in editing mode (meaning it has an editor and will focus the editor) @@ -1249,6 +1258,7 @@ export class NotebookCellList extends WorkbenchList implements ID this.view.setScrollTop(this.view.elementTop(viewIndex)); break; case CellRevealPosition.Center: + case CellRevealPosition.NearTop: { // reveal the cell top in the viewport center initially this.view.setScrollTop(elementTop - this.view.renderHeight / 2); @@ -1259,8 +1269,10 @@ export class NotebookCellList extends WorkbenchList implements ID if (newElementHeight >= renderHeight) { // cell is larger than viewport, reveal top this.view.setScrollTop(newElementTop); - } else { + } else if (revealPosition === CellRevealPosition.Center) { this.view.setScrollTop(newElementTop + (newElementHeight / 2) - (renderHeight / 2)); + } else if (revealPosition === CellRevealPosition.NearTop) { + this.view.setScrollTop(newElementTop - (renderHeight / 5)); } } break; @@ -1497,6 +1509,10 @@ export class ListViewInfoAccessor extends Disposable { this.list.revealElementInCenter(cell); } + async revealNearTopIfOutsideViewportAync(cell: ICellViewModel) { + return this.list.revealNearTopIfOutsideViewportAync(cell); + } + async revealLineInViewAsync(cell: ICellViewModel, line: number): Promise { return this.list.revealElementLineInViewAsync(cell, line); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 0bdb4ff330f4e..ceabb8d6de9f2 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -66,6 +66,7 @@ export interface INotebookCellList { revealElementInCenterIfOutsideViewport(element: ICellViewModel): void; revealElementInCenter(element: ICellViewModel): void; revealElementInCenterIfOutsideViewportAsync(element: ICellViewModel): Promise; + revealNearTopIfOutsideViewportAync(element: ICellViewModel): Promise; revealElementLineInViewAsync(element: ICellViewModel, line: number): Promise; revealElementLineInCenterAsync(element: ICellViewModel, line: number): Promise; revealElementLineInCenterIfOutsideViewportAsync(element: ICellViewModel, line: number): Promise; From 8eeed3dd6c140da47b326d99e5a0579465c4f62e Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 25 May 2022 11:27:58 -0700 Subject: [PATCH 323/942] Fix bad layout of search input (#150405) Fix #150381 --- src/vs/workbench/contrib/search/browser/media/searchview.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/media/searchview.css b/src/vs/workbench/contrib/search/browser/media/searchview.css index f9e5c40fa0df0..f59811de9a2df 100644 --- a/src/vs/workbench/contrib/search/browser/media/searchview.css +++ b/src/vs/workbench/contrib/search/browser/media/searchview.css @@ -47,8 +47,7 @@ .search-view .search-widget .monaco-inputbox > .ibwrapper > .mirror, .search-view .search-widget .monaco-inputbox > .ibwrapper > textarea.input { - padding: 3px; - padding-left: 4px; + padding: 3px 0px 3px 4px; } /* NOTE: height is also used in searchWidget.ts as a constant*/ From 149e2b7674ba0888cbb7cf8bbd70f45db7393fa5 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 25 May 2022 20:51:08 +0200 Subject: [PATCH 324/942] Fix #149831 (#150276) --- extensions/git/src/repository.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index e6bd193aa0c53..c6fa51b54971a 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1408,15 +1408,15 @@ export class Repository implements Disposable { } @throttle - async fetchAll(): Promise { - await this._fetch({ all: true }); + async fetchAll(cancellationToken?: CancellationToken): Promise { + await this._fetch({ all: true, cancellationToken }); } async fetch(options: FetchOptions): Promise { await this._fetch(options); } - private async _fetch(options: { remote?: string; ref?: string; all?: boolean; prune?: boolean; depth?: number; silent?: boolean } = {}): Promise { + private async _fetch(options: { remote?: string; ref?: string; all?: boolean; prune?: boolean; depth?: number; silent?: boolean; cancellationToken?: CancellationToken } = {}): Promise { if (!options.prune) { const config = workspace.getConfiguration('git', Uri.file(this.root)); const prune = config.get('pruneOnFetch'); @@ -1461,7 +1461,7 @@ export class Repository implements Disposable { // When fetchOnPull is enabled, fetch all branches when pulling if (fetchOnPull) { - await this.repository.fetch({ all: true }); + await this.fetchAll(); } if (await this.checkIfMaybeRebased(this.HEAD?.name)) { @@ -1532,7 +1532,7 @@ export class Repository implements Disposable { const fn = async (cancellationToken?: CancellationToken) => { // When fetchOnPull is enabled, fetch all branches when pulling if (fetchOnPull) { - await this.repository.fetch({ all: true, cancellationToken }); + await this.fetchAll(cancellationToken); } if (await this.checkIfMaybeRebased(this.HEAD?.name)) { From b118105bf28d773fbbce683f7230d058be2f89a7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 25 May 2022 12:05:19 -0700 Subject: [PATCH 325/942] Prevent work for really long links - We no longer get all xterm.js content for a full wrapped line but limit it to a certain context, this context depends on the type of link detector. - There is also a sanity check when calling the low level helper function just in case. Fixes #150401 --- .../contrib/terminal/browser/links/links.ts | 6 ++++++ .../browser/links/terminalExternalLinkDetector.ts | 11 +++-------- .../browser/links/terminalLinkDetectorAdapter.ts | 11 +++++++++-- .../terminal/browser/links/terminalLinkHelpers.ts | 4 ++++ .../browser/links/terminalLocalLinkDetector.ts | 6 ++++++ .../browser/links/terminalUriLinkDetector.ts | 13 +++++-------- .../browser/links/terminalWordLinkDetector.ts | 4 ++++ .../browser/links/terminalUriLinkDetector.test.ts | 14 +++++++------- 8 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/links.ts b/src/vs/workbench/contrib/terminal/browser/links/links.ts index 012b95f3214dd..a93a42c4b495d 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/links.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/links.ts @@ -17,6 +17,12 @@ export interface ITerminalLinkDetector { */ readonly xterm: Terminal; + /** + * The maximum link length possible for this detector, this puts a cap on how much of a wrapped + * line to consider to prevent performance problems. + */ + readonly maxLinkLength: number; + /** * Detects links within the _wrapped_ line range provided and returns them as an array. * diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkDetector.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkDetector.ts index 9a633e8956a7e..521a7b2ee44b7 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkDetector.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkDetector.ts @@ -8,14 +8,9 @@ import { convertLinkRangeToBuffer, getXtermLineContent } from 'vs/workbench/cont import { ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IBufferLine, Terminal } from 'xterm'; -const enum Constants { - /** - * The max line length to try extract word links from. - */ - MaxLineLength = 2000 -} - export class TerminalExternalLinkDetector implements ITerminalLinkDetector { + readonly maxLinkLength = 2000; + constructor( readonly id: string, readonly xterm: Terminal, @@ -26,7 +21,7 @@ export class TerminalExternalLinkDetector implements ITerminalLinkDetector { async detect(lines: IBufferLine[], startLine: number, endLine: number): Promise { // Get the text representation of the wrapped line const text = getXtermLineContent(this.xterm.buffer.active, startLine, endLine, this.xterm.cols); - if (text === '' || text.length > Constants.MaxLineLength) { + if (text === '' || text.length > this.maxLinkLength) { return []; } diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkDetectorAdapter.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkDetectorAdapter.ts index 868bf9600e882..43d8923d29111 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkDetectorAdapter.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkDetectorAdapter.ts @@ -59,12 +59,19 @@ export class TerminalLinkDetectorAdapter extends Disposable implements ILinkProv this._detector.xterm.buffer.active.getLine(startLine)! ]; - while (startLine >= 0 && this._detector.xterm.buffer.active.getLine(startLine)?.isWrapped) { + // Cap the maximum context on either side of the line being provided, by taking the context + // around the line being provided for this ensures the line the pointer is on will have + // links provided. + const maxLineContext = Math.max(this._detector.maxLinkLength / this._detector.xterm.cols); + const minStartLine = Math.max(startLine - maxLineContext, 0); + const maxEndLine = Math.min(endLine + maxLineContext, this._detector.xterm.buffer.active.length); + + while (startLine >= minStartLine && this._detector.xterm.buffer.active.getLine(startLine)?.isWrapped) { lines.unshift(this._detector.xterm.buffer.active.getLine(startLine - 1)!); startLine--; } - while (endLine < this._detector.xterm.buffer.active.length && this._detector.xterm.buffer.active.getLine(endLine + 1)?.isWrapped) { + while (endLine < maxEndLine && this._detector.xterm.buffer.active.getLine(endLine + 1)?.isWrapped) { lines.push(this._detector.xterm.buffer.active.getLine(endLine + 1)!); endLine++; } diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts index 202ba173cc06b..bb6042ce80abb 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkHelpers.ts @@ -122,6 +122,10 @@ export function convertBufferRangeToViewport(bufferRange: IBufferRange, viewport } export function getXtermLineContent(buffer: IBuffer, lineStart: number, lineEnd: number, cols: number): string { + // Cap the maximum number of lines generated to prevent potential performance problems. This is + // more of a sanity check as the wrapped line should already be trimmed down at this point. + const maxLineLength = Math.max(2048 / cols * 2); + lineEnd = Math.min(lineEnd, lineStart + maxLineLength); let content = ''; for (let i = lineStart; i <= lineEnd; i++) { // Make sure only 0 to cols are considered as resizing when windows mode is enabled will diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLocalLinkDetector.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLocalLinkDetector.ts index 936af397072db..48e5c0659bcbf 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLocalLinkDetector.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLocalLinkDetector.ts @@ -67,6 +67,12 @@ export const lineAndColumnClauseGroupCount = 6; export class TerminalLocalLinkDetector implements ITerminalLinkDetector { static id = 'local'; + // This was chosen as a reasonable maximum line length given the tradeoff between performance + // and how likely it is to encounter such a large line length. Some useful reference points: + // - Window old max length: 260 ($MAX_PATH) + // - Linux max length: 4096 ($PATH_MAX) + readonly maxLinkLength = 500; + constructor( readonly xterm: Terminal, private readonly _capabilities: ITerminalCapabilityStore, diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalUriLinkDetector.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalUriLinkDetector.ts index 948b257d8ab08..446a730540529 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalUriLinkDetector.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalUriLinkDetector.ts @@ -17,18 +17,15 @@ const enum Constants { * The maximum number of links in a line to resolve against the file system. This limit is put * in place to avoid sending excessive data when remote connections are in place. */ - MaxResolvedLinksInLine = 10, - - /** - * The maximum length of a link to resolve against the file system. This limit is put in place - * to avoid sending excessive data when remote connections are in place. - */ - MaxResolvedLinkLength = 1024, + MaxResolvedLinksInLine = 10 } export class TerminalUriLinkDetector implements ITerminalLinkDetector { static id = 'uri'; + // 2048 is the maximum URL length + readonly maxLinkLength = 2048; + constructor( readonly xterm: Terminal, private readonly _resolvePath: (link: string, uri?: URI) => Promise, @@ -70,7 +67,7 @@ export class TerminalUriLinkDetector implements ITerminalLinkDetector { } // Don't try resolve any links of excessive length - if (text.length > Constants.MaxResolvedLinkLength) { + if (text.length > this.maxLinkLength) { continue; } diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkDetector.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkDetector.ts index 6bfa63c50384f..637ff8dad9c58 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkDetector.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkDetector.ts @@ -25,6 +25,10 @@ interface Word { export class TerminalWordLinkDetector implements ITerminalLinkDetector { static id = 'word'; + // Word links typically search the workspace so it makes sense that their maximum link length is + // quite small. + readonly maxLinkLength = 100; + constructor( readonly xterm: Terminal, @IConfigurationService private readonly _configurationService: IConfigurationService, diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalUriLinkDetector.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalUriLinkDetector.test.ts index 448f145fda297..bc867788d4b86 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalUriLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalUriLinkDetector.test.ts @@ -91,13 +91,13 @@ suite('Workbench - TerminalUriLinkDetector', () => { text: `https://${'foobarbaz/'.repeat(102)}` }]); }); - test('should filter out file:// links that exceed 1024 characters', async () => { - // 8 + 101 * 10 = 1018 characters - await assertLink(TerminalBuiltinLinkType.LocalFile, `file:///${'foobarbaz/'.repeat(101)}`, [{ - text: `file:///${'foobarbaz/'.repeat(101)}`, - range: [[1, 1], [58, 13]] + test('should filter out file:// links that exceed 4096 characters', async () => { + // 8 + 200 * 10 = 2008 characters + await assertLink(TerminalBuiltinLinkType.LocalFile, `file:///${'foobarbaz/'.repeat(200)}`, [{ + text: `file:///${'foobarbaz/'.repeat(200)}`, + range: [[1, 1], [8, 26]] }]); - // 8 + 102 * 10 = 1028 characters - await assertLink(TerminalBuiltinLinkType.LocalFile, `file:///${'foobarbaz/'.repeat(102)}`, []); + // 8 + 450 * 10 = 4508 characters + await assertLink(TerminalBuiltinLinkType.LocalFile, `file:///${'foobarbaz/'.repeat(450)}`, []); }); }); From 9d18042706a81afd0a92e86a8cbbdd0abb74c922 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 25 May 2022 12:06:51 -0700 Subject: [PATCH 326/942] fix #150390 (#150395) fix #150392 --- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 5c841da10210c..b7edef134d574 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1369,6 +1369,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Send it to the process await this._processManager.write(text); this._onDidInputData.fire(this); + this.xterm?.scrollToBottom(); } async sendPath(originalPath: string, addNewLine: boolean): Promise { From 4d166fc71a5c8f5f1c33aa2a10609f5ec87754d6 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 25 May 2022 12:07:16 -0700 Subject: [PATCH 327/942] don't add telemetry for feature terminals etc (#150224) --- .../common/xterm/shellIntegrationAddon.ts | 24 +++++++++++-------- src/vs/platform/terminal/node/ptyService.ts | 2 +- .../terminal/browser/terminalInstance.ts | 3 ++- .../terminal/browser/xterm/xtermTerminal.ts | 3 ++- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 2e456109e9e53..8119de5aa5234 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -128,6 +128,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private _activationTimeout: any; constructor( + private readonly _disableTelemetry: boolean | undefined, private readonly _telemetryService: ITelemetryService | undefined, @ILogService private readonly _logService: ILogService ) { @@ -154,6 +155,19 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati return didHandle; } + private async _ensureCapabilitiesOrAddFailureTelemetry(): Promise { + if (!this._telemetryService || this._disableTelemetry) { + return; + } + this._activationTimeout = setTimeout(() => { + if (!this.capabilities.get(TerminalCapability.CommandDetection) && !this.capabilities.get(TerminalCapability.CwdDetection)) { + this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivationTimeout'); + this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); + } + this._hasUpdatedTelemetry = true; + }, 10000); + } + private _doHandleVSCodeSequence(data: string): boolean { if (!this._terminal) { return false; @@ -229,16 +243,6 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati return false; } - private async _ensureCapabilitiesOrAddFailureTelemetry(): Promise { - this._activationTimeout = setTimeout(() => { - if (!this.capabilities.get(TerminalCapability.CommandDetection) && !this.capabilities.get(TerminalCapability.CwdDetection)) { - this._telemetryService?.publicLog2<{}, { owner: 'meganrogge'; comment: 'Indicates shell integration activation did not occur within 10 seconds' }>('terminal/shellIntegrationActivationTimeout'); - this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); - } - this._hasUpdatedTelemetry = true; - }, 10000); - } - serialize(): ISerializedCommandDetectionCapability { if (!this._terminal || !this.capabilities.has(TerminalCapability.CommandDetection)) { return { diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index d9f8ae2b092a8..397fee9a49308 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -742,7 +742,7 @@ class XtermSerializer implements ITerminalSerializer { this._xterm.writeln(reviveBuffer); } this.setUnicodeVersion(unicodeVersion); - this._shellIntegrationAddon = new ShellIntegrationAddon(undefined, logService); + this._shellIntegrationAddon = new ShellIntegrationAddon(true, undefined, logService); this._xterm.loadAddon(this._shellIntegrationAddon); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index b7edef134d574..82088ea5347b6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -657,7 +657,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { throw new ErrorNoTelemetry('Terminal disposed of during xterm.js creation'); } - const xterm = this._instantiationService.createInstance(XtermTerminal, Terminal, this._configHelper, this._cols, this._rows, this.target || TerminalLocation.Panel, this.capabilities); + const disableShellIntegrationTelemetry = this.shellLaunchConfig.isFeatureTerminal || this.shellLaunchConfig.hideFromUser || this.shellLaunchConfig.executable === undefined; + const xterm = this._instantiationService.createInstance(XtermTerminal, Terminal, this._configHelper, this._cols, this._rows, this.target || TerminalLocation.Panel, this.capabilities, disableShellIntegrationTelemetry); this.xterm = xterm; const lineDataEventAddon = new LineDataEventAddon(); this.xterm.raw.loadAddon(lineDataEventAddon); diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 3285ae2c363b9..56fc30dfdaaa9 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -99,6 +99,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { rows: number, location: TerminalLocation, private readonly _capabilities: ITerminalCapabilityStore, + disableShellIntegrationTelemetry: boolean, @IConfigurationService private readonly _configurationService: IConfigurationService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILogService private readonly _logService: ILogService, @@ -175,7 +176,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal { this._updateUnicodeVersion(); this._commandNavigationAddon = this._instantiationService.createInstance(CommandNavigationAddon, _capabilities); this.raw.loadAddon(this._commandNavigationAddon); - this._shellIntegrationAddon = this._instantiationService.createInstance(ShellIntegrationAddon, this._telemetryService); + this._shellIntegrationAddon = this._instantiationService.createInstance(ShellIntegrationAddon, disableShellIntegrationTelemetry, this._telemetryService); this.raw.loadAddon(this._shellIntegrationAddon); this._updateShellIntegrationAddons(); } From 280f1be7ba9a60d32d0a1949b38d212b99870906 Mon Sep 17 00:00:00 2001 From: Ian Huff Date: Wed, 25 May 2022 12:19:28 -0700 Subject: [PATCH 328/942] quick draft PR for not throwing when looking for a chunked password (#150402) * quick draft PR for not throwing when looking for a chunked password * update comment with more info Co-authored-by: Ian Huff --- .../common/credentialsMainService.ts | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/credentials/common/credentialsMainService.ts b/src/vs/platform/credentials/common/credentialsMainService.ts index 12a591de763c7..c9615fe27d8e5 100644 --- a/src/vs/platform/credentials/common/credentialsMainService.ts +++ b/src/vs/platform/credentials/common/credentialsMainService.ts @@ -150,19 +150,28 @@ export abstract class BaseCredentialsMainService extends Disposable implements I return false; } const didDelete = await keytar.deletePassword(service, account); - let { content, hasNextChunk }: ChunkedPassword = JSON.parse(password); - if (content && hasNextChunk) { - // need to delete additional chunks - let index = 1; - while (hasNextChunk) { - const accountWithIndex = `${account}-${index}`; - const nextChunk = await keytar.getPassword(service, accountWithIndex); - await keytar.deletePassword(service, accountWithIndex); + try { + let { content, hasNextChunk }: ChunkedPassword = JSON.parse(password); + if (content && hasNextChunk) { + // need to delete additional chunks + let index = 1; + while (hasNextChunk) { + const accountWithIndex = `${account}-${index}`; + const nextChunk = await keytar.getPassword(service, accountWithIndex); + await keytar.deletePassword(service, accountWithIndex); - const result: ChunkedPassword = JSON.parse(nextChunk!); - hasNextChunk = result.hasNextChunk; - index++; + const result: ChunkedPassword = JSON.parse(nextChunk!); + hasNextChunk = result.hasNextChunk; + index++; + } } + } catch { + // When the password is saved the entire JSON payload is encrypted then stored, thus the result from getPassword might not be valid JSON + // https://github.com/microsoft/vscode/blob/c22cb87311b5eb1a3bf5600d18733f7485355dc0/src/vs/workbench/api/browser/mainThreadSecretState.ts#L83 + // However in the chunked case we JSONify each chunk after encryption so for the chunked case we do expect valid JSON here + // https://github.com/microsoft/vscode/blob/708cb0c507d656b760f9d08115b8ebaf8964fd73/src/vs/platform/credentials/common/credentialsMainService.ts#L128 + // Empty catch here just as in getPassword because we expect to handle both JSON cases and non JSON cases here it's not an error case to fail to parse + // https://github.com/microsoft/vscode/blob/708cb0c507d656b760f9d08115b8ebaf8964fd73/src/vs/platform/credentials/common/credentialsMainService.ts#L76 } if (didDelete) { From 5f7983c8a56149771132cb1a6c60ba5e374d93af Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 25 May 2022 12:30:19 -0700 Subject: [PATCH 329/942] Fix tests to work with new link filtering --- .../browser/links/terminalUriLinkDetector.ts | 10 +++++----- .../links/terminalUriLinkDetector.test.ts | 17 +++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalUriLinkDetector.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalUriLinkDetector.ts index 446a730540529..921164f5de384 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalUriLinkDetector.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalUriLinkDetector.ts @@ -55,6 +55,11 @@ export class TerminalUriLinkDetector implements ITerminalLinkDetector { const text = computedLink.url?.toString() || ''; + // Don't try resolve any links of excessive length + if (text.length > this.maxLinkLength) { + continue; + } + // Handle non-file scheme links if (uri.scheme !== Schemas.file) { links.push({ @@ -66,11 +71,6 @@ export class TerminalUriLinkDetector implements ITerminalLinkDetector { continue; } - // Don't try resolve any links of excessive length - if (text.length > this.maxLinkLength) { - continue; - } - // Filter out URI with unrecognized authorities if (uri.authority.length !== 2 && uri.authority.endsWith(':')) { continue; diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalUriLinkDetector.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalUriLinkDetector.test.ts index bc867788d4b86..2b6becbb4c5e1 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalUriLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalUriLinkDetector.test.ts @@ -79,17 +79,14 @@ suite('Workbench - TerminalUriLinkDetector', () => { { range: [[16, 1], [29, 1]], text: 'http://bar.foo' } ]); }); - test('should not filtrer out https:// link that exceed 1024 characters', async () => { - // 8 + 101 * 10 = 1018 characters - await assertLink(TerminalBuiltinLinkType.Url, `https://${'foobarbaz/'.repeat(101)}`, [{ - range: [[1, 1], [58, 13]], - text: `https://${'foobarbaz/'.repeat(101)}` - }]); - // 8 + 102 * 10 = 1028 characters - await assertLink(TerminalBuiltinLinkType.Url, `https://${'foobarbaz/'.repeat(102)}`, [{ - range: [[1, 1], [68, 13]], - text: `https://${'foobarbaz/'.repeat(102)}` + test('should filter out https:// link that exceed 4096 characters', async () => { + // 8 + 200 * 10 = 2008 characters + await assertLink(TerminalBuiltinLinkType.Url, `https://${'foobarbaz/'.repeat(200)}`, [{ + range: [[1, 1], [8, 26]], + text: `https://${'foobarbaz/'.repeat(200)}` }]); + // 8 + 450 * 10 = 4508 characters + await assertLink(TerminalBuiltinLinkType.Url, `https://${'foobarbaz/'.repeat(450)}`, []); }); test('should filter out file:// links that exceed 4096 characters', async () => { // 8 + 200 * 10 = 2008 characters From 9c1926d72676a90733232fee94f949acb466aa33 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 25 May 2022 12:51:23 -0700 Subject: [PATCH 330/942] xterm@4.19.0-beta.52 Fixes #148061 --- package.json | 4 ++-- remote/package.json | 4 ++-- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 ++++---- remote/yarn.lock | 18 +++++++++--------- yarn.lock | 18 +++++++++--------- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index 3768321d2bb04..2728502f1fb31 100644 --- a/package.json +++ b/package.json @@ -85,12 +85,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.49", + "xterm": "4.19.0-beta.52", "xterm-addon-search": "0.9.0-beta.37", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.36", - "xterm-headless": "4.19.0-beta.49", + "xterm-headless": "4.19.0-beta.52", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 3e833d3c5f64f..fe99f034f835c 100644 --- a/remote/package.json +++ b/remote/package.json @@ -24,12 +24,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.49", + "xterm": "4.19.0-beta.52", "xterm-addon-search": "0.9.0-beta.37", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.36", - "xterm-headless": "4.19.0-beta.49", + "xterm-headless": "4.19.0-beta.52", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index 4ec42b01f1267..470499091e3f1 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -10,7 +10,7 @@ "tas-client-umd": "0.1.5", "vscode-oniguruma": "1.6.1", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.49", + "xterm": "4.19.0-beta.52", "xterm-addon-search": "0.9.0-beta.37", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.36" diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index d8eac016d98df..0b6e2d9535aa2 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -128,7 +128,7 @@ xterm-addon-webgl@0.12.0-beta.36: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.36.tgz#460f80829a78c979a448d5b764699af3f0366ff1" integrity sha512-sgX7OHSGZQZE5b4xtPqd/5NEcll0Z+00tnTVxKZlXf5XEENcG0tnBF4I4f+k9K3cmjE1UIUVG2yYPrqWlYCdpA== -xterm@4.19.0-beta.49: - version "4.19.0-beta.49" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.49.tgz#96d0d748c97a2f0cfa4c6112bc4de8b0d5d559fe" - integrity sha512-qPhOeGtnB357mZOvfDckVlPDR7EDkSASQrB3aujhjgJlOiBBqcNRJcuSvF7v1DOho7xdEI9UQxiSiT5diHhMlg== +xterm@4.19.0-beta.52: + version "4.19.0-beta.52" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.52.tgz#21c7ad0bd905f3fddaa0583951514c8271dcd822" + integrity sha512-2itY6dyiimOEkgVwc31TngYQ6npHt7or1wxU0Flbv4PjH4ke8K26NDB7KAi0YYkn2g0/qzIjQc7ykdrrTaEgMg== diff --git a/remote/yarn.lock b/remote/yarn.lock index 52a1cc813ed7d..8f228e501bfa7 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -934,15 +934,15 @@ xterm-addon-webgl@0.12.0-beta.36: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.36.tgz#460f80829a78c979a448d5b764699af3f0366ff1" integrity sha512-sgX7OHSGZQZE5b4xtPqd/5NEcll0Z+00tnTVxKZlXf5XEENcG0tnBF4I4f+k9K3cmjE1UIUVG2yYPrqWlYCdpA== -xterm-headless@4.19.0-beta.49: - version "4.19.0-beta.49" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.49.tgz#8e4178620be9a9dee25a586ef992a6fc621a829a" - integrity sha512-uRnHpR2pv1MJPbE3sa7XbP6x0uHubAWVMbiD26e3a5ICtWJQVVpLojkA0t4qU7CoMX85pRL1rIclg9CKY0B0xA== - -xterm@4.19.0-beta.49: - version "4.19.0-beta.49" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.49.tgz#96d0d748c97a2f0cfa4c6112bc4de8b0d5d559fe" - integrity sha512-qPhOeGtnB357mZOvfDckVlPDR7EDkSASQrB3aujhjgJlOiBBqcNRJcuSvF7v1DOho7xdEI9UQxiSiT5diHhMlg== +xterm-headless@4.19.0-beta.52: + version "4.19.0-beta.52" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.52.tgz#87298cb205694cfdd701a9a96dcd06434a9773fe" + integrity sha512-9s8VtGa9JUdEWxrNDJpEng2mEFn0KjWM3R9nxezA8mXHsJ/0cMqoK00s/fzhAEXZqHycWASSECMWgFOaT0MrnQ== + +xterm@4.19.0-beta.52: + version "4.19.0-beta.52" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.52.tgz#21c7ad0bd905f3fddaa0583951514c8271dcd822" + integrity sha512-2itY6dyiimOEkgVwc31TngYQ6npHt7or1wxU0Flbv4PjH4ke8K26NDB7KAi0YYkn2g0/qzIjQc7ykdrrTaEgMg== yallist@^4.0.0: version "4.0.0" diff --git a/yarn.lock b/yarn.lock index e483a32e50059..5f155fdde196a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12218,15 +12218,15 @@ xterm-addon-webgl@0.12.0-beta.36: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.36.tgz#460f80829a78c979a448d5b764699af3f0366ff1" integrity sha512-sgX7OHSGZQZE5b4xtPqd/5NEcll0Z+00tnTVxKZlXf5XEENcG0tnBF4I4f+k9K3cmjE1UIUVG2yYPrqWlYCdpA== -xterm-headless@4.19.0-beta.49: - version "4.19.0-beta.49" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.49.tgz#8e4178620be9a9dee25a586ef992a6fc621a829a" - integrity sha512-uRnHpR2pv1MJPbE3sa7XbP6x0uHubAWVMbiD26e3a5ICtWJQVVpLojkA0t4qU7CoMX85pRL1rIclg9CKY0B0xA== - -xterm@4.19.0-beta.49: - version "4.19.0-beta.49" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.49.tgz#96d0d748c97a2f0cfa4c6112bc4de8b0d5d559fe" - integrity sha512-qPhOeGtnB357mZOvfDckVlPDR7EDkSASQrB3aujhjgJlOiBBqcNRJcuSvF7v1DOho7xdEI9UQxiSiT5diHhMlg== +xterm-headless@4.19.0-beta.52: + version "4.19.0-beta.52" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.52.tgz#87298cb205694cfdd701a9a96dcd06434a9773fe" + integrity sha512-9s8VtGa9JUdEWxrNDJpEng2mEFn0KjWM3R9nxezA8mXHsJ/0cMqoK00s/fzhAEXZqHycWASSECMWgFOaT0MrnQ== + +xterm@4.19.0-beta.52: + version "4.19.0-beta.52" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.52.tgz#21c7ad0bd905f3fddaa0583951514c8271dcd822" + integrity sha512-2itY6dyiimOEkgVwc31TngYQ6npHt7or1wxU0Flbv4PjH4ke8K26NDB7KAi0YYkn2g0/qzIjQc7ykdrrTaEgMg== y18n@^3.2.1: version "3.2.2" From 22806eaa8e961ca5dff67410e8bbe7ff7cc5e77b Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 25 May 2022 12:54:31 -0700 Subject: [PATCH 331/942] re-enable terminal tabs test (#148966) --- test/smoke/src/areas/terminal/terminal-tabs.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/areas/terminal/terminal-tabs.test.ts b/test/smoke/src/areas/terminal/terminal-tabs.test.ts index 1f962540d75be..cc4573978e888 100644 --- a/test/smoke/src/areas/terminal/terminal-tabs.test.ts +++ b/test/smoke/src/areas/terminal/terminal-tabs.test.ts @@ -67,7 +67,7 @@ export function setup() { await terminal.assertSingleTab({ name }); }); - it.skip('should reset the tab name to the default value when no name is provided', async () => { // https://github.com/microsoft/vscode/issues/146796 + it('should reset the tab name to the default value when no name is provided', async () => { await terminal.createTerminal(); const defaultName = await terminal.getSingleTabName(); const name = 'my terminal name'; From 3ed15398de076e1ef26cce6cc3532a1c21183078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 25 May 2022 22:22:30 +0200 Subject: [PATCH 332/942] update endgame notebook (#150407) --- .vscode/notebooks/endgame.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 90cc4971f430d..48195c79000dd 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-unpkg\n\n$MILESTONE=milestone:\"April 2022\"" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-unpkg\r\n\r\n$MILESTONE=milestone:\"May 2022\"" }, { "kind": 1, From e7833fe75c5ca9e10e89c67dc201362da623cda0 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 25 May 2022 13:38:11 -0700 Subject: [PATCH 333/942] show terminal find history placeholder on focus (#150409) --- .../contrib/codeEditor/browser/find/simpleFindWidget.css | 4 +++- .../contrib/codeEditor/browser/find/simpleFindWidget.ts | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.css b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.css index 46b11d7eedfe8..b280e5847798a 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.css +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.css @@ -9,7 +9,7 @@ position: absolute; top: 0; right: 18px; - width: 220px; + width: 360px; max-width: calc(100% - 28px - 28px - 8px); pointer-events: none; padding: 0 10px 10px; @@ -29,6 +29,7 @@ align-items: center; pointer-events: all; transition: top 200ms linear; + min-width: 360px; } .monaco-workbench.reduce-motion .monaco-editor .find-widget { @@ -45,6 +46,7 @@ .monaco-workbench .simple-find-part .monaco-findInput { flex: 1; + min-width: 220px; } .monaco-workbench .simple-find-part .button { diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index 5fa3bb659ab0c..d31078de06722 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -22,6 +22,7 @@ import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; import * as strings from 'vs/base/common/strings'; import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { showHistoryKeybindingHint } from 'vs/platform/history/browser/historyWidgetKeybindingHint'; const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find (\u21C5 for history)"); @@ -80,9 +81,9 @@ export abstract class SimpleFindWidget extends Widget { }, appendCaseSensitiveLabel: options.appendCaseSensitiveLabel && options.type === 'Terminal' ? this._getKeybinding(TerminalCommandId.ToggleFindCaseSensitive) : undefined, appendRegexLabel: options.appendRegexLabel && options.type === 'Terminal' ? this._getKeybinding(TerminalCommandId.ToggleFindRegex) : undefined, - appendWholeWordsLabel: options.appendWholeWordsLabel && options.type === 'Terminal' ? this._getKeybinding(TerminalCommandId.ToggleFindWholeWord) : undefined + appendWholeWordsLabel: options.appendWholeWordsLabel && options.type === 'Terminal' ? this._getKeybinding(TerminalCommandId.ToggleFindWholeWord) : undefined, + showHistoryHint: () => showHistoryKeybindingHint(_keybindingService) }, contextKeyService, options.showOptionButtons)); - // Find History with update delayer this._updateHistoryDelayer = new Delayer(500); From eb3dcea9528c19bddd3f94213e0a90819cec5f54 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 25 May 2022 22:56:37 +0200 Subject: [PATCH 334/942] Adopt `setTimeout0` which doesn't suffer from the 4ms artificial delay that browsers introduce when the nesting level is > 5 --- src/vs/base/test/common/timeTravelScheduler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/test/common/timeTravelScheduler.ts b/src/vs/base/test/common/timeTravelScheduler.ts index 9081b7c5632cc..470d93ac86486 100644 --- a/src/vs/base/test/common/timeTravelScheduler.ts +++ b/src/vs/base/test/common/timeTravelScheduler.ts @@ -5,6 +5,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { setTimeout0 } from 'vs/base/common/platform'; interface PriorityQueue { length: number; @@ -178,7 +179,7 @@ export class AsyncSchedulerProcessor extends Disposable { if (this.useSetImmediate) { originalGlobalValues.setImmediate(() => this.process()); } else { - originalGlobalValues.setTimeout(() => this.process()); + setTimeout0(() => this.process()); } }); } From b180649a62f01a5ccb8c271dc8490c05aba29de3 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 25 May 2022 23:23:43 +0200 Subject: [PATCH 335/942] Fix unit tests on node --- src/vs/base/common/platform.ts | 4 +++- src/vs/base/test/common/timeTravelScheduler.ts | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index e0ea1d62c6485..948b62e0a93ef 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -195,6 +195,8 @@ export const locale = _locale; */ export const translationsConfigFile = _translationsConfigFile; +export const setTimeout0IsFaster = (typeof globals.postMessage === 'function' && !globals.importScripts); + /** * See https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#:~:text=than%204%2C%20then-,set%20timeout%20to%204,-. * @@ -202,7 +204,7 @@ export const translationsConfigFile = _translationsConfigFile; * that browsers set when the nesting level is > 5. */ export const setTimeout0 = (() => { - if (typeof globals.postMessage === 'function' && !globals.importScripts) { + if (setTimeout0IsFaster) { interface IQueueElement { id: number; callback: () => void; diff --git a/src/vs/base/test/common/timeTravelScheduler.ts b/src/vs/base/test/common/timeTravelScheduler.ts index 470d93ac86486..797f308dc065c 100644 --- a/src/vs/base/test/common/timeTravelScheduler.ts +++ b/src/vs/base/test/common/timeTravelScheduler.ts @@ -5,7 +5,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { setTimeout0 } from 'vs/base/common/platform'; +import { setTimeout0, setTimeout0IsFaster } from 'vs/base/common/platform'; interface PriorityQueue { length: number; @@ -178,8 +178,10 @@ export class AsyncSchedulerProcessor extends Disposable { Promise.resolve().then(() => { if (this.useSetImmediate) { originalGlobalValues.setImmediate(() => this.process()); - } else { + } else if (setTimeout0IsFaster) { setTimeout0(() => this.process()); + } else { + originalGlobalValues.setTimeout(() => this.process()); } }); } From 6844f0b7d15e881ded03e93b828d85a4aa7417e6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 25 May 2022 16:01:25 -0700 Subject: [PATCH 336/942] Clear activation timeout if terminal is disposed Fixes #150419 --- .../common/xterm/shellIntegrationAddon.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 8119de5aa5234..3e48db34a630e 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IShellIntegration } from 'vs/platform/terminal/common/terminal'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; import { CommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; import { CwdDetectionCapability } from 'vs/platform/terminal/common/capabilities/cwdDetectionCapability'; @@ -133,6 +133,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati @ILogService private readonly _logService: ILogService ) { super(); + this._register(toDisposable(() => this._clearActivationTimeout())); } activate(xterm: Terminal) { @@ -147,10 +148,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati if (!this._hasUpdatedTelemetry && didHandle) { this._telemetryService?.publicLog2<{}, { owner: 'meganrogge'; comment: 'Indicates shell integration was activated' }>('terminal/shellIntegrationActivationSucceeded'); this._hasUpdatedTelemetry = true; - if (this._activationTimeout !== undefined) { - clearTimeout(this._activationTimeout); - this._activationTimeout = undefined; - } + this._clearActivationTimeout(); } return didHandle; } @@ -168,6 +166,13 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati }, 10000); } + private _clearActivationTimeout(): void { + if (this._activationTimeout !== undefined) { + clearTimeout(this._activationTimeout); + this._activationTimeout = undefined; + } + } + private _doHandleVSCodeSequence(data: string): boolean { if (!this._terminal) { return false; From 12cb9932382bcde504d05477c5049f22e5f07d26 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 25 May 2022 17:30:39 -0700 Subject: [PATCH 337/942] Create cell execution beforehand (#150410) --- .../api/browser/mainThreadNotebookKernels.ts | 13 ++++++- .../browser/notebookExecutionServiceImpl.ts | 37 ++++++++++++------- .../notebookExecutionStateServiceImpl.ts | 9 +---- .../common/notebookExecutionStateService.ts | 2 +- .../notebookExecutionStateService.test.ts | 8 ++-- .../test/browser/testNotebookEditor.ts | 2 +- 6 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index 6b7349827873e..c02518f694c6c 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -18,6 +18,7 @@ import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workb import { INotebookKernel, INotebookKernelChangeEvent, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol'; +import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; abstract class MainThreadKernel implements INotebookKernel { private readonly _onDidChange = new Emitter(); @@ -112,6 +113,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape @ILanguageService private readonly _languageService: ILanguageService, @INotebookKernelService private readonly _notebookKernelService: INotebookKernelService, @INotebookExecutionStateService private readonly _notebookExecutionStateService: INotebookExecutionStateService, + @INotebookService private readonly _notebookService: INotebookService, @INotebookEditorService notebookEditorService: INotebookEditorService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebookKernels); @@ -246,7 +248,16 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape $createExecution(handle: number, controllerId: string, rawUri: UriComponents, cellHandle: number): void { const uri = URI.revive(rawUri); - const execution = this._notebookExecutionStateService.createCellExecution(controllerId, uri, cellHandle); + const notebook = this._notebookService.getNotebookTextModel(uri); + if (!notebook) { + throw new Error(`Notebook not found: ${uri.toString()}`); + } + + const kernel = this._notebookKernelService.getMatchingKernel(notebook); + if (!kernel.selected || kernel.selected.id !== controllerId) { + throw new Error(`Kernel is not selected: ${kernel.selected?.id} !== ${controllerId}`); + } + const execution = this._notebookExecutionStateService.createCellExecution(uri, cellHandle); execution.confirm(); this._executions.set(handle, execution); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts index 0be1459d36a07..de0c18f7638bf 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts @@ -13,7 +13,7 @@ import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controll import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { CellKind, INotebookTextModel, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; -import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; +import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; export class NotebookExecutionService implements INotebookExecutionService, IDisposable { @@ -38,6 +38,16 @@ export class NotebookExecutionService implements INotebookExecutionService, IDis return; } + // create cell executions + const cellExecutions: [NotebookCellTextModel, INotebookCellExecution][] = []; + for (const cell of cellsArr) { + const cellExe = this._notebookExecutionStateService.getCellExecution(cell.uri); + if (cell.cellKind !== CellKind.Code || !!cellExe) { + continue; + } + cellExecutions.push([cell, this._notebookExecutionStateService.createCellExecution(notebook.uri, cell.handle)]); + } + let kernel = this._notebookKernelService.getSelectedOrSuggestedKernel(notebook); if (!kernel) { kernel = await this.resolveSourceActions(notebook); @@ -49,28 +59,27 @@ export class NotebookExecutionService implements INotebookExecutionService, IDis } if (!kernel) { + // clear all pending cell executions + cellExecutions.forEach(cellExe => cellExe[1].complete({})); return; } - const executeCells: NotebookCellTextModel[] = []; - for (const cell of cellsArr) { - const cellExe = this._notebookExecutionStateService.getCellExecution(cell.uri); - if (cell.cellKind !== CellKind.Code || !!cellExe) { - continue; - } + // filter cell executions based on selected kernel + const validCellExecutions: INotebookCellExecution[] = []; + for (const [cell, cellExecution] of cellExecutions) { if (!kernel.supportedLanguages.includes(cell.language)) { - continue; + cellExecution.complete({}); + } else { + validCellExecutions.push(cellExecution); } - executeCells.push(cell); } - if (executeCells.length > 0) { + // request execution + if (validCellExecutions.length > 0) { this._notebookKernelService.selectKernelForNotebook(kernel, notebook); - - const exes = executeCells.map(c => this._notebookExecutionStateService.createCellExecution(kernel!.id, notebook.uri, c.handle)); - await kernel.executeNotebookCellsRequest(notebook.uri, executeCells.map(c => c.handle)); + await kernel.executeNotebookCellsRequest(notebook.uri, validCellExecutions.map(c => c.cellHandle)); // the connecting state can change before the kernel resolves executeNotebookCellsRequest - const unconfirmed = exes.filter(exe => exe.state === NotebookCellExecutionState.Unconfirmed); + const unconfirmed = validCellExecutions.filter(exe => exe.state === NotebookCellExecutionState.Unconfirmed); if (unconfirmed.length) { this._logService.debug(`NotebookExecutionService#executeNotebookCells completing unconfirmed executions ${JSON.stringify(unconfirmed.map(exe => exe.cellHandle))}`); unconfirmed.forEach(exe => exe.complete({})); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookExecutionStateServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookExecutionStateServiceImpl.ts index be0522ab323e0..3b487ec768480 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookExecutionStateServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookExecutionStateServiceImpl.ts @@ -14,7 +14,6 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { CellEditType, CellUri, ICellEditOperation, NotebookCellExecutionState, NotebookCellInternalMetadata, NotebookTextModelWillAddRemoveEvent } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CellExecutionUpdateType, INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { ICellExecuteUpdate, ICellExecutionComplete, ICellExecutionStateChangedEvent, ICellExecutionStateUpdate, INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; export class NotebookExecutionStateService extends Disposable implements INotebookExecutionStateService { @@ -28,7 +27,6 @@ export class NotebookExecutionStateService extends Disposable implements INotebo onDidChangeCellExecution = this._onDidChangeCellExecution.event; constructor( - @INotebookKernelService private readonly _notebookKernelService: INotebookKernelService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILogService private readonly _logService: ILogService, @INotebookService private readonly _notebookService: INotebookService, @@ -91,17 +89,12 @@ export class NotebookExecutionStateService extends Disposable implements INotebo this._onDidChangeCellExecution.fire(new NotebookExecutionEvent(notebookUri, cellHandle)); } - createCellExecution(controllerId: string, notebookUri: URI, cellHandle: number): INotebookCellExecution { + createCellExecution(notebookUri: URI, cellHandle: number): INotebookCellExecution { const notebook = this._notebookService.getNotebookTextModel(notebookUri); if (!notebook) { throw new Error(`Notebook not found: ${notebookUri.toString()}`); } - const kernel = this._notebookKernelService.getMatchingKernel(notebook); - if (!kernel.selected || kernel.selected.id !== controllerId) { - throw new Error(`Kernel is not selected: ${kernel.selected?.id} !== ${controllerId}`); - } - let notebookExecutionMap = this._executions.get(notebookUri); if (!notebookExecutionMap) { const listeners = this._instantiationService.createInstance(NotebookExecutionListeners, notebookUri); diff --git a/src/vs/workbench/contrib/notebook/common/notebookExecutionStateService.ts b/src/vs/workbench/contrib/notebook/common/notebookExecutionStateService.ts index 0a9902a5ff438..f6317245bb18e 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookExecutionStateService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookExecutionStateService.ts @@ -50,7 +50,7 @@ export interface INotebookExecutionStateService { forceCancelNotebookExecutions(notebookUri: URI): void; getCellExecutionStatesForNotebook(notebook: URI): INotebookCellExecution[]; getCellExecution(cellUri: URI): INotebookCellExecution | undefined; - createCellExecution(controllerId: string, notebook: URI, cellHandle: number): INotebookCellExecution; + createCellExecution(notebook: URI, cellHandle: number): INotebookCellExecution; } export interface INotebookCellExecution { diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts index 0b05037e51eba..e925ef9e0118e 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts @@ -94,7 +94,7 @@ suite('NotebookExecutionStateService', () => { const executionStateService: INotebookExecutionStateService = instantiationService.get(INotebookExecutionStateService); const cell = insertCellAtIndex(viewModel, 0, 'var c = 3', 'javascript', CellKind.Code, {}, [], true, true); - executionStateService.createCellExecution(kernel.id, viewModel.uri, cell.handle); + executionStateService.createCellExecution(viewModel.uri, cell.handle); assert.strictEqual(didCancel, false); viewModel.notebookDocument.applyEdits([{ editType: CellEditType.Replace, index: 0, count: 1, cells: [] @@ -113,7 +113,7 @@ suite('NotebookExecutionStateService', () => { const executionStateService: INotebookExecutionStateService = instantiationService.get(INotebookExecutionStateService); const cell = insertCellAtIndex(viewModel, 0, 'var c = 3', 'javascript', CellKind.Code, {}, [], true, true); - const exe = executionStateService.createCellExecution(kernel.id, viewModel.uri, cell.handle); + const exe = executionStateService.createCellExecution(viewModel.uri, cell.handle); let didFire = false; disposables.add(executionStateService.onDidChangeCellExecution(e => { @@ -154,7 +154,7 @@ suite('NotebookExecutionStateService', () => { deferred.complete(); })); - executionStateService.createCellExecution(kernel.id, viewModel.uri, cell.handle); + executionStateService.createCellExecution(viewModel.uri, cell.handle); return deferred.p; }); @@ -170,7 +170,7 @@ suite('NotebookExecutionStateService', () => { const executionStateService: INotebookExecutionStateService = instantiationService.get(INotebookExecutionStateService); const cell = insertCellAtIndex(viewModel, 0, 'var c = 3', 'javascript', CellKind.Code, {}, [], true, true); - executionStateService.createCellExecution(kernel.id, viewModel.uri, cell.handle); + executionStateService.createCellExecution(viewModel.uri, cell.handle); const exe = executionStateService.getCellExecution(cell.uri); assert.ok(exe); diff --git a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts index 514c523144300..8570bf9146a1a 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts @@ -422,7 +422,7 @@ class TestNotebookExecutionStateService implements INotebookExecutionStateServic return this._executions.get(cellUri); } - createCellExecution(controllerId: string, notebook: URI, cellHandle: number): INotebookCellExecution { + createCellExecution(notebook: URI, cellHandle: number): INotebookCellExecution { const onComplete = () => this._executions.delete(CellUri.generate(notebook, cellHandle)); const exe = new TestCellExecution(notebook, cellHandle, onComplete); this._executions.set(CellUri.generate(notebook, cellHandle), exe); From e262c88fb16e1d4ec5c7d5cf82ac78dad7961091 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 25 May 2022 18:28:46 -0700 Subject: [PATCH 338/942] Fix process.platform for picomatch (#150430) The `picomatch` library currently checks `process.platform`. This check fails on web, which causes the markdown extension to not load To fix this, I'm replacing `process.platform` with the string`'web'` --- extensions/shared.webpack.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js index 84aa4259a3b94..3ebb9c62b5214 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -151,6 +151,7 @@ const browserPlugins = [ ] }), new DefinePlugin({ + 'process.platform': JSON.stringify('web'), 'process.env': JSON.stringify({}), 'process.env.BROWSER_ENV': JSON.stringify('true') }) From 528ee1ae3daabe30c1307cf9dcd6e77eb96094bc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 25 May 2022 18:29:28 -0700 Subject: [PATCH 339/942] Allow multiple entries with the same mimetype in dataTransfer (#150425) Currently our data transfer implementation only allows a single entry of each mimeType. There can only be a single `image/gif` file for example. However this doesn't match how the DOM apis work. If you drop multiple gifs into VS Code for example, the DataTransfer you get contains entries for each of the gifs. This change allows us to also support DataTransfers that have multiple entries with the same mime type. Just like with the DOM, we support constructing these duplicate mime data transfers internally, but do not allow extensions to create them As part of this change, I've also made a few clean ups: - Add helpers for creating dataTransfer items - Clarify when adding a data transfer item should `append` or `replace` - Adopt some helper functions in a few more places --- src/vs/base/common/dataTransfer.ts | 71 ++++++++++++------- src/vs/editor/browser/dnd.ts | 19 +++-- .../copyPaste/browser/copyPasteController.ts | 12 ++-- .../browser/dropIntoEditorContribution.ts | 27 ++----- .../api/browser/mainThreadLanguageFeatures.ts | 4 +- .../api/browser/mainThreadTreeViews.ts | 4 +- .../api/common/extHostTypeConverters.ts | 10 +-- src/vs/workbench/api/common/extHostTypes.ts | 23 ++++-- .../workbench/browser/parts/views/treeView.ts | 16 ++--- 9 files changed, 102 insertions(+), 84 deletions(-) diff --git a/src/vs/base/common/dataTransfer.ts b/src/vs/base/common/dataTransfer.ts index d5d99bd283b32..5074f30c75ee4 100644 --- a/src/vs/base/common/dataTransfer.ts +++ b/src/vs/base/common/dataTransfer.ts @@ -17,55 +17,74 @@ export interface IDataTransferItem { value: any; } +export function createStringDataTransferItem(stringOrPromise: string | Promise): IDataTransferItem { + return { + asString: async () => stringOrPromise, + asFile: () => undefined, + value: typeof stringOrPromise === 'string' ? stringOrPromise : undefined, + }; +} + +export function createFileDataTransferItem(fileName: string, uri: URI | undefined, data: () => Promise): IDataTransferItem { + return { + asString: async () => '', + asFile: () => ({ name: fileName, uri, data }), + value: undefined, + }; +} + export class VSDataTransfer { - private readonly _data = new Map(); + private readonly _entries = new Map(); public get size(): number { - return this._data.size; + return this._entries.size; } public has(mimeType: string): boolean { - return this._data.has(mimeType); + return this._entries.has(this.toKey(mimeType)); } public get(mimeType: string): IDataTransferItem | undefined { - return this._data.get(mimeType); + return this._entries.get(this.toKey(mimeType))?.[0]; } - public set(mimeType: string, value: IDataTransferItem): void { - this._data.set(mimeType, value); + public append(mimeType: string, value: IDataTransferItem): void { + const existing = this._entries.get(mimeType); + if (existing) { + existing.push(value); + } else { + this._entries.set(this.toKey(mimeType), [value]); + } } - public delete(mimeType: string) { - this._data.delete(mimeType); + public replace(mimeType: string, value: IDataTransferItem): void { + this._entries.set(this.toKey(mimeType), [value]); } - public setString(mimeType: string, stringOrPromise: string | Promise) { - this.set(mimeType, { - asString: async () => stringOrPromise, - asFile: () => undefined, - value: typeof stringOrPromise === 'string' ? stringOrPromise : undefined, - }); + public delete(mimeType: string) { + this._entries.delete(this.toKey(mimeType)); } - public setFile(mimeType: string, fileName: string, uri: URI | undefined, data: () => Promise) { - this.set(mimeType, { - asString: async () => '', - asFile: () => ({ name: fileName, uri, data }), - value: undefined, - }); + public *entries(): Iterable<[string, IDataTransferItem]> { + for (const [mine, items] of this._entries.entries()) { + for (const item of items) { + yield [mine, item]; + } + } } - public entries(): IterableIterator<[string, IDataTransferItem]> { - return this._data.entries(); + public values(): Iterable { + return Array.from(this._entries.values()).flat(); } - public values(): IterableIterator { - return this._data.values(); + public forEach(f: (value: IDataTransferItem, key: string) => void) { + for (const [mime, item] of this.entries()) { + f(item, mime); + } } - public forEach(f: (value: IDataTransferItem, key: string) => void) { - this._data.forEach(f); + private toKey(mimeType: string): string { + return mimeType.toLowerCase(); } } diff --git a/src/vs/editor/browser/dnd.ts b/src/vs/editor/browser/dnd.ts index 08a3f55878640..80a5958504d70 100644 --- a/src/vs/editor/browser/dnd.ts +++ b/src/vs/editor/browser/dnd.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { createFileDataTransferItem, createStringDataTransferItem, IDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; import { URI } from 'vs/base/common/uri'; +import { FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd'; export function toVSDataTransfer(dataTransfer: DataTransfer) { @@ -13,16 +14,20 @@ export function toVSDataTransfer(dataTransfer: DataTransfer) { const type = item.type; if (item.kind === 'string') { const asStringValue = new Promise(resolve => item.getAsString(resolve)); - vsDataTransfer.setString(type, asStringValue); + vsDataTransfer.append(type, createStringDataTransferItem(asStringValue)); } else if (item.kind === 'file') { - const file = item.getAsFile() as null | (File & { path?: string }); + const file = item.getAsFile(); if (file) { - const uri = file.path ? URI.parse(file.path) : undefined; - vsDataTransfer.setFile(type, file.name, uri, async () => { - return new Uint8Array(await file.arrayBuffer()); - }); + vsDataTransfer.append(type, createFileDataTransferItemFromFile(file)); } } } return vsDataTransfer; } + +export function createFileDataTransferItemFromFile(file: File): IDataTransferItem { + const uri = (file as FileAdditionalNativeProperties).path ? URI.parse((file as FileAdditionalNativeProperties).path!) : undefined; + return createFileDataTransferItem(file.name, uri, async () => { + return new Uint8Array(await file.arrayBuffer()); + }); +} diff --git a/src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts b/src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts index 6c203af56cd52..74ddf65d392d9 100644 --- a/src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts +++ b/src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts @@ -6,7 +6,7 @@ import { addDisposableListener } from 'vs/base/browser/dom'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Disposable } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import { generateUuid } from 'vs/base/common/uuid'; @@ -103,7 +103,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi for (const result of results) { result?.forEach((value, key) => { - dataTransfer.set(key, value); + dataTransfer.replace(key, value); }); } @@ -148,7 +148,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi if (handle && this._currentClipboardItem?.handle === handle) { const toMergeDataTransfer = await this._currentClipboardItem.dataTransferPromise; toMergeDataTransfer.forEach((value, key) => { - dataTransfer.set(key, value); + dataTransfer.append(key, value); }); } @@ -156,11 +156,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi const resources = await this._clipboardService.readResources(); if (resources.length) { const value = resources.join('\n'); - dataTransfer.set(Mimes.uriList, { - value, - asString: async () => value, - asFile: () => undefined, - }); + dataTransfer.append(Mimes.uriList, createStringDataTransferItem(value)); } } diff --git a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts index b713b71a7dd16..4abda27de6c13 100644 --- a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts +++ b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts @@ -5,11 +5,12 @@ import { distinct } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Disposable } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import { relativePath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; +import { toVSDataTransfer } from 'vs/editor/browser/dnd'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IPosition } from 'vs/editor/common/core/position'; @@ -20,7 +21,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { performSnippetEdit } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { extractEditorsDropData, FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd'; +import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -94,27 +95,11 @@ export class DropIntoEditorController extends Disposable implements IEditorContr } public async extractDataTransferData(dragEvent: DragEvent): Promise { - const textEditorDataTransfer = new VSDataTransfer(); if (!dragEvent.dataTransfer) { - return textEditorDataTransfer; - } - - for (const item of dragEvent.dataTransfer.items) { - const type = item.type; - if (item.kind === 'string') { - const asStringValue = new Promise(resolve => item.getAsString(resolve)); - textEditorDataTransfer.setString(type, asStringValue); - } else if (item.kind === 'file') { - const file = item.getAsFile(); - if (file) { - const uri = (file as FileAdditionalNativeProperties).path ? URI.parse((file as FileAdditionalNativeProperties).path!) : undefined; - textEditorDataTransfer.setFile(type, file.name, uri, async () => { - return new Uint8Array(await file.arrayBuffer()); - }); - } - } + return new VSDataTransfer(); } + const textEditorDataTransfer = toVSDataTransfer(dragEvent.dataTransfer); if (!textEditorDataTransfer.has(Mimes.uriList)) { const editorData = (await this._instantiationService.invokeFunction(extractEditorsDropData, dragEvent)) .filter(input => input.resource) @@ -122,7 +107,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr if (editorData.length) { const str = distinct(editorData).join('\n'); - textEditorDataTransfer.setString(Mimes.uriList.toLowerCase(), str); + textEditorDataTransfer.replace(Mimes.uriList, createStringDataTransferItem(str)); } } diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index f89e0698b9d95..ff55fc5844fa7 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -5,7 +5,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; import { CancellationError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { combinedDisposable, Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -385,7 +385,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread const dataTransferOut = new VSDataTransfer(); result.items.forEach(([type, item]) => { - dataTransferOut.setString(type, item.asString); + dataTransferOut.replace(type, createStringDataTransferItem(item.asString)); }); return dataTransferOut; } diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 56133dc4b206d..7c8aa9d232362 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -14,7 +14,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; import { VSBuffer } from 'vs/base/common/buffer'; import { DataTransferCache } from 'vs/workbench/api/common/shared/dataTransferCache'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; @@ -225,7 +225,7 @@ class TreeViewDragAndDropController implements ITreeViewDragAndDropController { const additionalDataTransfer = new VSDataTransfer(); additionalDataTransferDTO.items.forEach(([type, item]) => { - additionalDataTransfer.setString(type, item.asString); + additionalDataTransfer.replace(type, createStringDataTransferItem(item.asString)); }); return additionalDataTransfer; } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 95e24e8692aa8..18d71be8a26b7 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -40,6 +40,7 @@ import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/ed import type * as vscode from 'vscode'; import * as types from './extHostTypes'; import { once } from 'vs/base/common/functional'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; export namespace Command { @@ -1980,14 +1981,13 @@ export namespace DataTransferItem { export namespace DataTransfer { export function toDataTransfer(value: extHostProtocol.DataTransferDTO, resolveFileData: (dataItemIndex: number) => Promise): types.DataTransfer { - const newDataTransfer = new types.DataTransfer(); - value.items.forEach(([type, item], index) => { - newDataTransfer.set(type, DataTransferItem.toDataTransferItem(item, () => resolveFileData(index))); + const init = value.items.map(([type, item], index) => { + return [type, DataTransferItem.toDataTransferItem(item, () => resolveFileData(index))] as const; }); - return newDataTransfer; + return new types.DataTransfer(init); } - export async function toDataTransferDTO(value: vscode.DataTransfer): Promise { + export async function toDataTransferDTO(value: vscode.DataTransfer | VSDataTransfer): Promise { const newDTO: extHostProtocol.DataTransferDTO = { items: [] }; const promises: Promise[] = []; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 76ae9232db9ae..a1c2a3ea107ae 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2451,18 +2451,33 @@ export class DataTransferItem { @es5ClassCompat export class DataTransfer { - #items = new Map(); + #items = new Map(); + + constructor(init?: Iterable) { + for (const [mime, item] of init ?? []) { + const existing = this.#items.get(mime); + if (existing) { + existing.push(item); + } else { + this.#items.set(mime, [item]); + } + } + } get(mimeType: string): DataTransferItem | undefined { - return this.#items.get(mimeType); + return this.#items.get(mimeType)?.[0]; } set(mimeType: string, value: DataTransferItem): void { - this.#items.set(mimeType, value); + // This intentionally overwrites all entries for a given mimetype. + // This is similar to how the DOM DataTransfer type works + this.#items.set(mimeType, [value]); } forEach(callbackfn: (value: DataTransferItem, key: string) => void): void { - this.#items.forEach(callbackfn); + for (const [mime, items] of this.#items) { + items.forEach(item => callbackfn(item, mime)); + } } } diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 051926ebcb402..524979149fe92 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -31,7 +31,7 @@ import { isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import 'vs/css!./media/views'; -import { VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Command } from 'vs/editor/common/languages'; import { localize } from 'vs/nls'; import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -66,7 +66,8 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; import { ThemeSettings } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITreeViewsService } from 'vs/workbench/services/views/browser/treeViewsService'; -import { CodeDataTransfers, FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd'; +import { CodeDataTransfers } from 'vs/platform/dnd/browser/dnd'; +import { createFileDataTransferItemFromFile } from 'vs/editor/browser/dnd'; export class TreeViewPane extends ViewPane { @@ -1524,7 +1525,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { } if (dataValue) { const converted = this.convertKnownMimes(type, kind, dataValue); - treeDataTransfer.setString(converted.type, converted.value + ''); + treeDataTransfer.append(converted.type, createStringDataTransferItem(converted.value + '')); } resolve(); })); @@ -1532,11 +1533,8 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { const file = dataItem.getAsFile(); if (file) { uris.push(URI.file(file.path)); - const uri = (file as FileAdditionalNativeProperties).path ? URI.parse((file as FileAdditionalNativeProperties).path!) : undefined; if (dndController.supportsFileDataTransfers) { - treeDataTransfer.setFile(type, file.name, uri, async () => { - return new Uint8Array(await file.arrayBuffer()); - }); + treeDataTransfer.append(type, createFileDataTransferItemFromFile(file)); } } } @@ -1545,7 +1543,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { // Check if there are uris to add and add them if (uris.length) { - treeDataTransfer.setString(Mimes.uriList, uris.map(uri => uri.toString()).join('\n')); + treeDataTransfer.replace(Mimes.uriList, createStringDataTransferItem(uris.map(uri => uri.toString()).join('\n'))); } const additionalWillDropPromise = this.treeViewsDragAndDropService.removeDragOperationTransfer(willDropUuid); @@ -1555,7 +1553,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { return additionalWillDropPromise.then(additionalDataTransfer => { if (additionalDataTransfer) { for (const item of additionalDataTransfer.entries()) { - treeDataTransfer.set(item[0], item[1]); + treeDataTransfer.append(item[0], item[1]); } } return dndController.handleDrop(treeDataTransfer, targetNode, new CancellationTokenSource().token, willDropUuid, treeSourceInfo?.id, treeSourceInfo?.itemHandles); From f86bbaa2a510b411ce4fb29da5766d8870880924 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 26 May 2022 07:36:10 -0700 Subject: [PATCH 340/942] Fix terminal find styles Fixes #150453 --- .../contrib/codeEditor/browser/find/simpleFindWidget.css | 3 --- .../workbench/contrib/terminal/browser/media/terminal.css | 7 +++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.css b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.css index b280e5847798a..cc120227164fa 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.css +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.css @@ -9,7 +9,6 @@ position: absolute; top: 0; right: 18px; - width: 360px; max-width: calc(100% - 28px - 28px - 8px); pointer-events: none; padding: 0 10px 10px; @@ -29,7 +28,6 @@ align-items: center; pointer-events: all; transition: top 200ms linear; - min-width: 360px; } .monaco-workbench.reduce-motion .monaco-editor .find-widget { @@ -46,7 +44,6 @@ .monaco-workbench .simple-find-part .monaco-findInput { flex: 1; - min-width: 220px; } .monaco-workbench .simple-find-part .button { diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 945161cad7f48..e3ec361345635 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -87,13 +87,12 @@ .monaco-workbench .simple-find-part-wrapper.result-count { z-index: 33 !important; - padding-right: 80px; } .result-count .simple-find-part { - width: 280px; - max-width: 280px; - min-width: 280px; + width: 330px; + max-width: 330px; + min-width: 330px; } .result-count .matchesCount { From 27e10113dc53dace848912a4975f15087e0c584e Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 26 May 2022 07:42:03 -0700 Subject: [PATCH 341/942] improve configure display language & add clear display language preference (#150433) --- .../contrib/localizationsUpdater.ts | 4 +- .../sharedProcess/sharedProcessMain.ts | 4 +- src/vs/code/node/cliProcessMain.ts | 4 +- .../languagePacks/common/languagePacks.ts | 76 +++++++- .../languagePacks/node/languagePacks.ts | 39 ++-- .../node/remoteExtensionHostAgentCli.ts | 4 +- src/vs/server/node/serverServices.ts | 4 +- .../browser/localizationsActions.ts | 166 ++++++++++++------ .../localization.contribution.ts | 20 +-- .../services/localization/common/locale.ts | 13 ++ .../electron-sandbox/localeService.ts | 36 ++++ src/vs/workbench/workbench.sandbox.main.ts | 1 + 12 files changed, 289 insertions(+), 82 deletions(-) create mode 100644 src/vs/workbench/services/localization/common/locale.ts create mode 100644 src/vs/workbench/services/localization/electron-sandbox/localeService.ts diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater.ts b/src/vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater.ts index 68085dcefcc8d..f035c7d8e4fe5 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater.ts @@ -5,12 +5,12 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; -import { LanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; +import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; export class LocalizationsUpdater extends Disposable { constructor( - @ILanguagePackService private readonly localizationsService: LanguagePackService + @ILanguagePackService private readonly localizationsService: NativeLanguagePackService ) { super(); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index fb6cac109a97d..3d5d20b8c7124 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -47,7 +47,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { MessagePortMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; -import { LanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; +import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; import { ConsoleLogger, ILoggerService, ILogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { FollowerLogService, LoggerChannelClient, LogLevelChannelClient } from 'vs/platform/log/common/logIpc'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; @@ -311,7 +311,7 @@ class SharedProcessMain extends Disposable { services.set(IExtensionTipsService, new SyncDescriptor(ExtensionTipsService)); // Localizations - services.set(ILanguagePackService, new SyncDescriptor(LanguagePackService)); + services.set(ILanguagePackService, new SyncDescriptor(NativeLanguagePackService)); // Diagnostics services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 88f9dc88a7a5a..32d5fb9c6a341 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -37,7 +37,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; -import { LanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; +import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; import { ConsoleLogger, getLogLevel, ILogger, ILogService, LogLevel, MultiplexLogService } from 'vs/platform/log/common/log'; import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; @@ -161,7 +161,7 @@ class CliMain extends Disposable { services.set(IExtensionManagementCLIService, new SyncDescriptor(ExtensionManagementCLIService)); // Localizations - services.set(ILanguagePackService, new SyncDescriptor(LanguagePackService)); + services.set(ILanguagePackService, new SyncDescriptor(NativeLanguagePackService)); // Telemetry const appenders: AppInsightsAppender[] = []; diff --git a/src/vs/platform/languagePacks/common/languagePacks.ts b/src/vs/platform/languagePacks/common/languagePacks.ts index 473e358148a7e..61c6c04553aa9 100644 --- a/src/vs/platform/languagePacks/common/languagePacks.ts +++ b/src/vs/platform/languagePacks/common/languagePacks.ts @@ -3,10 +3,84 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { language } from 'vs/base/common/platform'; +import { IQuickPickItem } from 'vs/base/parts/quickinput/common/quickInput'; +import { localize } from 'vs/nls'; +import { IExtensionGalleryService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const ILanguagePackService = createDecorator('languagePackService'); + +export interface ILanguagePackItem extends IQuickPickItem { + readonly extensionId: string; + readonly galleryExtension?: IGalleryExtension; +} + export interface ILanguagePackService { readonly _serviceBrand: undefined; - getInstalledLanguages(): Promise; + getAvailableLanguages(): Promise>; + getInstalledLanguages(): Promise>; +} + +export abstract class LanguagePackBaseService extends Disposable implements ILanguagePackService { + _serviceBrand: undefined; + + constructor(@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService) { + super(); + } + + abstract getInstalledLanguages(): Promise>; + + async getAvailableLanguages(): Promise { + const timeout = new CancellationTokenSource(); + setTimeout(() => timeout.cancel(), 1000); + + let result; + try { + result = await this.extensionGalleryService.query({ + text: 'category:"language packs"', + pageSize: 20 + }, timeout.token); + } catch (_) { + // This method is best effort. So, we ignore any errors. + return []; + } + + const languagePackExtensions = result.firstPage.filter(e => e.properties.localizedLanguages?.length && e.tags.some(t => t.startsWith('lp-'))); + const allFromMarketplace: ILanguagePackItem[] = languagePackExtensions.map(lp => { + const languageName = lp.properties.localizedLanguages?.[0]; + const locale = lp.tags.find(t => t.startsWith('lp-'))!.split('lp-')[1]; + const baseQuickPick = this.createQuickPickItem({ locale, label: languageName }); + return { + ...baseQuickPick, + extensionId: lp.identifier.id, + galleryExtension: lp + }; + }); + + allFromMarketplace.push({ + ...this.createQuickPickItem({ locale: 'en', label: 'English' }), + extensionId: 'default', + }); + + return allFromMarketplace; + } + + protected createQuickPickItem(languageItem: { locale: string; label?: string | undefined }): IQuickPickItem { + const label = languageItem.label ?? languageItem.locale; + let description: string | undefined = languageItem.locale !== languageItem.label ? languageItem.locale : undefined; + if (languageItem.locale.toLowerCase() === language.toLowerCase()) { + if (!description) { + description = ''; + } + description += localize('currentDisplayLanguage', " (Current)"); + } + return { + id: languageItem.locale, + label, + description + }; + } } diff --git a/src/vs/platform/languagePacks/node/languagePacks.ts b/src/vs/platform/languagePacks/node/languagePacks.ts index 009c936bdb061..369f4bb32f61d 100644 --- a/src/vs/platform/languagePacks/node/languagePacks.ts +++ b/src/vs/platform/languagePacks/node/languagePacks.ts @@ -4,21 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import { createHash } from 'crypto'; -import { distinct, equals } from 'vs/base/common/arrays'; +import { equals } from 'vs/base/common/arrays'; import { Queue } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { join } from 'vs/base/common/path'; import { Promises } from 'vs/base/node/pfs'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IExtensionIdentifier, IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { ILocalizationContribution } from 'vs/platform/extensions/common/extensions'; -import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; import { ILogService } from 'vs/platform/log/common/log'; +import { ILocalizationContribution } from 'vs/platform/extensions/common/extensions'; +import { ILanguagePackItem, LanguagePackBaseService } from 'vs/platform/languagePacks/common/languagePacks'; interface ILanguagePack { hash: string; + label: string | undefined; extensions: { extensionIdentifier: IExtensionIdentifier; version: string; @@ -26,7 +27,7 @@ interface ILanguagePack { translations: { [id: string]: string }; } -export class LanguagePackService extends Disposable implements ILanguagePackService { +export class NativeLanguagePackService extends LanguagePackBaseService { declare readonly _serviceBrand: undefined; @@ -35,9 +36,10 @@ export class LanguagePackService extends Disposable implements ILanguagePackServ constructor( @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @INativeEnvironmentService environmentService: INativeEnvironmentService, + @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, @ILogService private readonly logService: ILogService ) { - super(); + super(extensionGalleryService); this.cache = this._register(new LanguagePacksCache(environmentService, logService)); this.extensionManagementService.registerParticipant({ postInstall: async (extension: ILocalExtension): Promise => { @@ -49,11 +51,21 @@ export class LanguagePackService extends Disposable implements ILanguagePackServ }); } - async getInstalledLanguages(): Promise { + async getInstalledLanguages(): Promise> { const languagePacks = await this.cache.getLanguagePacks(); - // Contributed languages are those installed via extension packs, so does not include English - const languages = ['en', ...Object.keys(languagePacks)]; - return distinct(languages); + const languages = Object.keys(languagePacks).map(locale => { + const languagePack = languagePacks[locale]; + const baseQuickPick = this.createQuickPickItem({ locale, label: languagePack.label }); + return { + ...baseQuickPick, + extensionId: languagePack.extensions[0].extensionIdentifier.id, + }; + }); + languages.push({ + ...this.createQuickPickItem({ locale: 'en', label: 'English' }), + extensionId: 'default', + }); + return languages; } private async postInstallExtension(extension: ILocalExtension): Promise { @@ -126,7 +138,12 @@ class LanguagePacksCache extends Disposable { if (extension.location.scheme === Schemas.file && isValidLocalization(localizationContribution)) { let languagePack = languagePacks[localizationContribution.languageId]; if (!languagePack) { - languagePack = { hash: '', extensions: [], translations: {} }; + languagePack = { + hash: '', + extensions: [], + translations: {}, + label: localizationContribution.localizedLanguageName ?? localizationContribution.languageName + }; languagePacks[localizationContribution.languageId] = languagePack; } let extensionInLanguagePack = languagePack.extensions.filter(e => areSameExtensions(e.extensionIdentifier, extensionIdentifier))[0]; diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index 2d8bde94e63de..898ff7d56b9a4 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -29,7 +29,7 @@ import { RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/ import { IServerEnvironmentService, ServerEnvironmentService, ServerParsedArgs } from 'vs/server/node/serverEnvironmentService'; import { ExtensionManagementCLIService } from 'vs/platform/extensionManagement/common/extensionManagementCLIService'; import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; -import { LanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; +import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; import { getErrorMessage } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { isAbsolute, join } from 'vs/base/common/path'; @@ -104,7 +104,7 @@ class CliMain extends Disposable { services.set(IExtensionsScannerService, new SyncDescriptor(ExtensionsScannerService)); services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionManagementCLIService, new SyncDescriptor(ExtensionManagementCLIService)); - services.set(ILanguagePackService, new SyncDescriptor(LanguagePackService)); + services.set(ILanguagePackService, new SyncDescriptor(NativeLanguagePackService)); return new InstantiationService(services); } diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 9a4bdee0c1ad9..a1f9cd11405e6 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -36,7 +36,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; -import { LanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; +import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; import { AbstractLogger, DEFAULT_LOG_LEVEL, getLogLevel, ILogService, LogLevel, LogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { LogLevelChannel } from 'vs/platform/log/common/logIpc'; import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; @@ -159,7 +159,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); const instantiationService: IInstantiationService = new InstantiationService(services); - services.set(ILanguagePackService, instantiationService.createInstance(LanguagePackService)); + services.set(ILanguagePackService, instantiationService.createInstance(NativeLanguagePackService)); const extensionManagementCLIService = instantiationService.createInstance(ExtensionManagementCLIService); services.set(IExtensionManagementCLIService, extensionManagementCLIService); diff --git a/src/vs/workbench/contrib/localization/browser/localizationsActions.ts b/src/vs/workbench/contrib/localization/browser/localizationsActions.ts index afebaf8f1e13e..45df8ab135eb5 100644 --- a/src/vs/workbench/contrib/localization/browser/localizationsActions.ts +++ b/src/vs/workbench/contrib/localization/browser/localizationsActions.ts @@ -4,88 +4,154 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; -import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; +import { IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { language } from 'vs/base/common/platform'; -import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; -import { ViewContainerLocation } from 'vs/workbench/common/views'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { Action2, MenuId } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ILanguagePackItem, ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; +import { ILocaleService } from 'vs/workbench/services/localization/common/locale'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; +import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; +import { ViewContainerLocation } from 'vs/workbench/common/views'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { isWeb } from 'vs/base/common/platform'; + +const restart = localize('restart', "&&Restart"); + +export class ConfigureDisplayLanguageAction extends Action2 { + public static readonly ID = 'workbench.action.configureLocale'; + public static readonly LABEL = localize('configureLocale', "Configure Display Language"); -export class ConfigureLocaleAction extends Action2 { constructor() { super({ - id: 'workbench.action.configureLocale', - title: { original: 'Configure Display Language', value: localize('configureLocale', "Configure Display Language") }, + id: ConfigureDisplayLanguageAction.ID, + title: { original: 'Configure Display Language', value: ConfigureDisplayLanguageAction.LABEL }, menu: { id: MenuId.CommandPalette } }); } - private async getLanguageOptions(localizationService: ILanguagePackService): Promise { - const availableLanguages = await localizationService.getInstalledLanguages(); - availableLanguages.sort(); - - return availableLanguages - .map(language => { return { label: language }; }) - .concat({ label: localize('installAdditionalLanguages', "Install Additional Languages...") }); - } - - public override async run(accessor: ServicesAccessor): Promise { - const environmentService: IEnvironmentService = accessor.get(IEnvironmentService); + public async run(accessor: ServicesAccessor): Promise { const languagePackService: ILanguagePackService = accessor.get(ILanguagePackService); const quickInputService: IQuickInputService = accessor.get(IQuickInputService); - const jsonEditingService: IJSONEditingService = accessor.get(IJSONEditingService); const hostService: IHostService = accessor.get(IHostService); - const notificationService: INotificationService = accessor.get(INotificationService); - const paneCompositeService: IPaneCompositePartService = accessor.get(IPaneCompositePartService); const dialogService: IDialogService = accessor.get(IDialogService); const productService: IProductService = accessor.get(IProductService); + const localeService: ILocaleService = accessor.get(ILocaleService); + const extensionManagementService: IExtensionManagementService = accessor.get(IExtensionManagementService); + const paneCompositePartService: IPaneCompositePartService = accessor.get(IPaneCompositePartService); + const notificationService: INotificationService = accessor.get(INotificationService); - const languageOptions = await this.getLanguageOptions(languagePackService); - const currentLanguageIndex = languageOptions.findIndex(l => l.label === language); + const installedLanguages = await languagePackService.getInstalledLanguages(); - try { - const selectedLanguage = await quickInputService.pick(languageOptions, - { - canPickMany: false, - placeHolder: localize('chooseDisplayLanguage', "Select Display Language"), - activeItem: languageOptions[currentLanguageIndex] - }); + const qp = quickInputService.createQuickPick(); + qp.placeholder = localize('chooseLocale', "Select Display Language"); + + if (installedLanguages?.length) { + const items: Array = [{ type: 'separator', label: localize('installed', "Installed languages") }]; + qp.items = items.concat(installedLanguages); + } - if (selectedLanguage === languageOptions[languageOptions.length - 1]) { - return paneCompositeService.openPaneComposite(EXTENSIONS_VIEWLET_ID, ViewContainerLocation.Sidebar, true) - .then(viewlet => viewlet?.getViewPaneContainer()) - .then(viewlet => { - const extensionsViewlet = viewlet as IExtensionsViewPaneContainer; - extensionsViewlet.search('@category:"language packs"'); - extensionsViewlet.focus(); - }); + const disposables = new DisposableStore(); + const source = new CancellationTokenSource(); + disposables.add(qp.onDispose(() => { + source.cancel(); + disposables.dispose(); + })); + + const installedSet = new Set(installedLanguages?.map(language => language.id!) ?? []); + languagePackService.getAvailableLanguages().then(availableLanguages => { + const newLanguages = availableLanguages.filter(l => l.id && !installedSet.has(l.id)); + if (newLanguages.length) { + qp.items = [ + ...qp.items, + { type: 'separator', label: localize('available', "Available languages") }, + ...newLanguages + ]; } + qp.busy = false; + }); + + disposables.add(qp.onDidAccept(async () => { + const selectedLanguage = qp.activeItems[0]; + qp.hide(); + + // Only Desktop has the concept of installing language packs so we only do this for Desktop + // and only if the language pack is not installed + if (!isWeb && !installedSet.has(selectedLanguage.id!)) { + try { + // Show the view so the user can see the language pack to be installed + let viewlet = await paneCompositePartService.openPaneComposite(EXTENSIONS_VIEWLET_ID, ViewContainerLocation.Sidebar); + (viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer).search(`@id:${selectedLanguage.extensionId}`); + + // Only actually install a language pack from Microsoft + if (selectedLanguage.galleryExtension?.publisher.toLowerCase() !== 'ms-ceintl') { + return; + } - if (selectedLanguage) { - await jsonEditingService.write(environmentService.argvResource, [{ path: ['locale'], value: selectedLanguage.label }], true); - const restart = await dialogService.confirm({ + await extensionManagementService.installFromGallery(selectedLanguage.galleryExtension); + } catch (err) { + notificationService.error(err); + return; + } + } + + if (await localeService.setLocale(selectedLanguage.id!)) { + const restartDialog = await dialogService.confirm({ type: 'info', message: localize('relaunchDisplayLanguageMessage', "A restart is required for the change in display language to take effect."), detail: localize('relaunchDisplayLanguageDetail', "Press the restart button to restart {0} and change the display language.", productService.nameLong), - primaryButton: localize('restart', "&&Restart") + primaryButton: restart }); - if (restart.confirmed) { + if (restartDialog.confirmed) { hostService.restart(); } } - } catch (e) { - notificationService.error(e); + })); + + qp.show(); + qp.busy = true; + } +} + +export class ClearDisplayLanguageAction extends Action2 { + public static readonly ID = 'workbench.action.clearLocalePreference'; + public static readonly LABEL = localize('clearDisplayLanguage', "Clear Display Language Preference"); + + constructor() { + super({ + id: ClearDisplayLanguageAction.ID, + title: { original: 'Clear Display Language Preference', value: ClearDisplayLanguageAction.LABEL }, + menu: { + id: MenuId.CommandPalette + } + }); + } + + public async run(accessor: ServicesAccessor): Promise { + const localeService: ILocaleService = accessor.get(ILocaleService); + const dialogService: IDialogService = accessor.get(IDialogService); + const productService: IProductService = accessor.get(IProductService); + const hostService: IHostService = accessor.get(IHostService); + + if (await localeService.setLocale(undefined)) { + const restartDialog = await dialogService.confirm({ + type: 'info', + message: localize('relaunchAfterClearDisplayLanguageMessage', "A restart is required for the change in display language to take effect."), + detail: localize('relaunchAfterClearDisplayLanguageDetail', "Press the restart button to restart {0} and change the display language.", productService.nameLong), + primaryButton: restart + }); + + if (restartDialog.confirmed) { + hostService.restart(); + } } } } diff --git a/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts b/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts index c725565a55de7..b04105ab3c920 100644 --- a/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts +++ b/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts @@ -24,10 +24,11 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; import { registerAction2 } from 'vs/platform/actions/common/actions'; -import { ConfigureLocaleAction } from 'vs/workbench/contrib/localization/browser/localizationsActions'; +import { ClearDisplayLanguageAction, ConfigureDisplayLanguageAction } from 'vs/workbench/contrib/localization/browser/localizationsActions'; // Register action to configure locale and related settings -registerAction2(ConfigureLocaleAction); +registerAction2(ConfigureDisplayLanguageAction); +registerAction2(ClearDisplayLanguageAction); const LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY = 'extensionsAssistant/languagePackSuggestionIgnore'; @@ -195,14 +196,13 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo } - private isLanguageInstalled(language: string | undefined): Promise { - return this.extensionManagementService.getInstalled() - .then(installed => installed.some(i => - !!(i.manifest - && i.manifest.contributes - && i.manifest.contributes.localizations - && i.manifest.contributes.localizations.length - && i.manifest.contributes.localizations.some(l => l.languageId.toLowerCase() === language)))); + private async isLanguageInstalled(language: string | undefined): Promise { + const installed = await this.extensionManagementService.getInstalled(); + return installed.some(i => !!(i.manifest + && i.manifest.contributes + && i.manifest.contributes.localizations + && i.manifest.contributes.localizations.length + && i.manifest.contributes.localizations.some(l => l.languageId.toLowerCase() === language))); } private installExtension(extension: IGalleryExtension): Promise { diff --git a/src/vs/workbench/services/localization/common/locale.ts b/src/vs/workbench/services/localization/common/locale.ts new file mode 100644 index 0000000000000..1b67af98c0459 --- /dev/null +++ b/src/vs/workbench/services/localization/common/locale.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const ILocaleService = createDecorator('localizationService'); + +export interface ILocaleService { + readonly _serviceBrand: undefined; + setLocale(languagePackItem: string | undefined): Promise; +} diff --git a/src/vs/workbench/services/localization/electron-sandbox/localeService.ts b/src/vs/workbench/services/localization/electron-sandbox/localeService.ts new file mode 100644 index 0000000000000..d435b3725d500 --- /dev/null +++ b/src/vs/workbench/services/localization/electron-sandbox/localeService.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { language } from 'vs/base/common/platform'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; +import { ILocaleService } from 'vs/workbench/services/localization/common/locale'; + +export class NativeLocaleService implements ILocaleService { + _serviceBrand: undefined; + + constructor( + @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @INotificationService private readonly notificationService: INotificationService, + ) { } + + async setLocale(locale: string | undefined): Promise { + try { + if (locale === language || (!locale && language === 'en')) { + return false; + } + await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: locale }], true); + return true; + } catch (err) { + this.notificationService.error(err); + return false; + } + } +} + +registerSingleton(ILocaleService, NativeLocaleService, true); diff --git a/src/vs/workbench/workbench.sandbox.main.ts b/src/vs/workbench/workbench.sandbox.main.ts index dd9ddd715b108..41435143c4854 100644 --- a/src/vs/workbench/workbench.sandbox.main.ts +++ b/src/vs/workbench/workbench.sandbox.main.ts @@ -83,6 +83,7 @@ import 'vs/workbench/services/files/electron-sandbox/elevatedFileService'; import 'vs/workbench/services/search/electron-sandbox/searchService'; import 'vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService'; import 'vs/workbench/services/userDataSync/browser/userDataSyncEnablementService'; +import 'vs/workbench/services/localization/electron-sandbox/localeService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IUserDataInitializationService, UserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; From cc86b15a44fe28300180108f4d95cacee98717dc Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 26 May 2022 08:19:15 -0700 Subject: [PATCH 342/942] Enable grabbing translations from an alternate location for server distro/serverful scenarios (#150436) * nls web story * better handling of urls * clean up code and don't do nls in dev * use version instead of quality * revert changes in workbench-dev.html * update nls from changes in vscode-loader * sanitize url a bit * revert loader change --- src/vs/base/common/product.ts | 1 + src/vs/code/browser/workbench/workbench.html | 16 +++++++++++++++- src/vs/server/node/webClientServer.ts | 7 ++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index c66cf41aa6cee..731c77b88933e 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -74,6 +74,7 @@ export interface IProductConfiguration { readonly resourceUrlTemplate: string; readonly controlUrl: string; readonly recommendationsUrl: string; + readonly nlsBaseUrl: string; }; readonly extensionTips?: { [id: string]: string }; diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 2e4eddb19b46f..d8914408e4ab4 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -40,6 +40,19 @@ Object.keys(self.webPackagePaths).map(function (key, index) { self.webPackagePaths[key] = `${baseUrl}/node_modules/${key}/${self.webPackagePaths[key]}`; }); + + // Set up nls if the user is not using the default language (English) + const nlsConfig = {}; + const locale = navigator.language; + if (!locale.startsWith('en')) { + nlsConfig['vs/nls'] = { + availableLanguages: { + '*': locale + }, + baseUrl: '{{WORKBENCH_NLS_BASE_URL}}' + }; + } + require.config({ baseUrl: `${baseUrl}/out`, recordStats: true, @@ -48,7 +61,8 @@ return value; } }), - paths: self.webPackagePaths + paths: self.webPackagePaths, + ...nlsConfig }); ', '\n\n\n \n\n\n\n', options); + const options : FormattingOptions = FormattingOptions.create(2, true); + options.insertFinalNewline = true; + + await assertFormat('

    Hello

    ', '\n\n\n

    Hello

    \n\n\n\n', {}, options); + await assertFormat('|

    Hello

    |', '\n

    Hello

    \n', {}, options); + await assertFormat('', '\n\n\n \n\n\n\n', {}, options); }); test('Inside script', async () => { From 4e53b01452f2c053ce38c685abd339c57a2ef0f1 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 17 Jun 2022 13:17:09 +0200 Subject: [PATCH 929/942] Pull in cpp grammar fixes (#152449) --- extensions/cpp/cgmanifest.json | 4 +- .../cpp.embedded.macro.tmLanguage.json | 57 +- extensions/cpp/syntaxes/cpp.tmLanguage.json | 115 +- .../test/colorize-results/test-78769_cpp.json | 1026 ++++++++--------- .../test/colorize-results/test_cpp.json | 8 +- 5 files changed, 606 insertions(+), 604 deletions(-) diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index d0f4963916722..29876c8523b5e 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jeff-hykin/better-cpp-syntax", "repositoryUrl": "https://github.com/jeff-hykin/better-cpp-syntax", - "commitHash": "156fc0eef532928c9dbf22f622d4a8efc7d97a6b" + "commitHash": "ddcaa65af8a578881e0d38f3c1cf5259a1128ab5" } }, "license": "MIT", - "version": "1.15.13", + "version": "1.15.17", "description": "The original JSON grammars were derived from https://github.com/atom/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { diff --git a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json index 7f41320ec40c9..c85993386c5c0 100644 --- a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json +++ b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jeff-hykin/better-cpp-syntax/commit/156fc0eef532928c9dbf22f622d4a8efc7d97a6b", + "version": "https://github.com/jeff-hykin/better-cpp-syntax/commit/ddcaa65af8a578881e0d38f3c1cf5259a1128ab5", "name": "C++", "scopeName": "source.cpp.embedded.macro", "patterns": [ @@ -519,7 +519,7 @@ "name": "comment.block.cpp" }, "builtin_storage_type_initilizer": { - "begin": "(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "\\s*+((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", "end": "\\}|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:error|warning)))\\b(?:(?:\\s)+)?", - "end": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", "end": "\\)|(?=(?))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", + "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", + "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "punctuation.definition.function.return-type.cpp" @@ -4240,7 +4240,7 @@ ] }, "function_pointer": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -5368,7 +5368,7 @@ }, "line": { "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?line\\b", - "end": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?define\\b)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:new)|(?:\\->\\*)|(?:<<=)|(?:>>=)|(?:<=>)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:\\-\\-)|(?:<<)|(?:>>)|(?:<=)|(?:>=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|(?:\\/=)|(?:%=)|(?:&=)|(?:\\^=)|(?:\\|=)|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=|,))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", + "begin": "(?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -7350,7 +7351,7 @@ "include": "source.cpp#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -7702,7 +7703,7 @@ }, "pragma": { "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?pragma\\b", - "end": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "\\s*+((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -2176,7 +2176,7 @@ ] }, "control_flow_keywords": { - "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", "end": "\\}", "beginCaptures": { "1": { @@ -3363,7 +3363,7 @@ ] }, "destructor_root": { - "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -3655,7 +3655,7 @@ }, "diagnostic": { "begin": "(^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:error|warning)))\\b(?:(?:\\s)+)?", - "end": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -4519,7 +4519,7 @@ ] }, "function_call": { - "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", "end": "\\)", "beginCaptures": { "1": { @@ -4593,7 +4593,7 @@ ] }, "function_definition": { - "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", + "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -5156,7 +5156,7 @@ ] }, { - "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", + "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "punctuation.definition.function.return-type.cpp" @@ -5404,7 +5404,7 @@ ] }, "function_pointer": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { @@ -5755,7 +5755,7 @@ ] }, "function_pointer_parameter": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { @@ -6476,7 +6476,7 @@ "name": "storage.type.modifier.virtual.cpp" }, { - "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -6674,7 +6674,7 @@ ] }, "inline_builtin_storage_type": { - "match": "(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?line\\b", - "end": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?define\\b)(?:(?:\\s)+)?((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(\\b(?!uint_least16_t[^\\w]|uint_least32_t[^\\w]|uint_least64_t[^\\w]|int_least16_t[^\\w]|int_least32_t[^\\w]|int_least64_t[^\\w]|uint_least8_t[^\\w]|uint_fast16_t[^\\w]|uint_fast32_t[^\\w]|uint_fast64_t[^\\w]|int_least8_t[^\\w]|int_fast16_t[^\\w]|int_fast32_t[^\\w]|int_fast64_t[^\\w]|uint_fast8_t[^\\w]|suseconds_t[^\\w]|int_fast8_t[^\\w]|useconds_t[^\\w]|blksize_t[^\\w]|in_addr_t[^\\w]|in_port_t[^\\w]|uintptr_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|unsigned[^\\w]|u_quad_t[^\\w]|blkcnt_t[^\\w]|uint16_t[^\\w]|uint32_t[^\\w]|uint64_t[^\\w]|intptr_t[^\\w]|intmax_t[^\\w]|intmax_t[^\\w]|wchar_t[^\\w]|u_short[^\\w]|qaddr_t[^\\w]|caddr_t[^\\w]|daddr_t[^\\w]|fixpt_t[^\\w]|nlink_t[^\\w]|segsz_t[^\\w]|swblk_t[^\\w]|clock_t[^\\w]|ssize_t[^\\w]|int16_t[^\\w]|int32_t[^\\w]|int64_t[^\\w]|uint8_t[^\\w]|signed[^\\w]|double[^\\w]|u_char[^\\w]|u_long[^\\w]|ushort[^\\w]|quad_t[^\\w]|mode_t[^\\w]|size_t[^\\w]|time_t[^\\w]|int8_t[^\\w]|short[^\\w]|float[^\\w]|u_int[^\\w]|div_t[^\\w]|dev_t[^\\w]|gid_t[^\\w]|ino_t[^\\w]|key_t[^\\w]|pid_t[^\\w]|off_t[^\\w]|uid_t[^\\w]|auto[^\\w]|void[^\\w]|char[^\\w]|long[^\\w]|bool[^\\w]|uint[^\\w]|id_t[^\\w]|id_t[^\\w]|int[^\\w])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", + "match": "(?:((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(\\b(?!uint_least32_t[^\\w]|uint_least16_t[^\\w]|uint_least64_t[^\\w]|int_least32_t[^\\w]|int_least64_t[^\\w]|uint_fast32_t[^\\w]|uint_fast64_t[^\\w]|uint_least8_t[^\\w]|uint_fast16_t[^\\w]|int_least16_t[^\\w]|int_fast16_t[^\\w]|int_least8_t[^\\w]|uint_fast8_t[^\\w]|int_fast64_t[^\\w]|int_fast32_t[^\\w]|int_fast8_t[^\\w]|suseconds_t[^\\w]|useconds_t[^\\w]|in_addr_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|in_port_t[^\\w]|uintptr_t[^\\w]|blksize_t[^\\w]|uint32_t[^\\w]|uint64_t[^\\w]|u_quad_t[^\\w]|intmax_t[^\\w]|intmax_t[^\\w]|unsigned[^\\w]|blkcnt_t[^\\w]|uint16_t[^\\w]|intptr_t[^\\w]|swblk_t[^\\w]|wchar_t[^\\w]|u_short[^\\w]|qaddr_t[^\\w]|caddr_t[^\\w]|daddr_t[^\\w]|fixpt_t[^\\w]|nlink_t[^\\w]|segsz_t[^\\w]|clock_t[^\\w]|ssize_t[^\\w]|int16_t[^\\w]|int32_t[^\\w]|int64_t[^\\w]|uint8_t[^\\w]|int8_t[^\\w]|mode_t[^\\w]|quad_t[^\\w]|ushort[^\\w]|u_long[^\\w]|u_char[^\\w]|double[^\\w]|signed[^\\w]|time_t[^\\w]|size_t[^\\w]|key_t[^\\w]|div_t[^\\w]|ino_t[^\\w]|uid_t[^\\w]|gid_t[^\\w]|off_t[^\\w]|pid_t[^\\w]|float[^\\w]|dev_t[^\\w]|u_int[^\\w]|short[^\\w]|bool[^\\w]|id_t[^\\w]|uint[^\\w]|long[^\\w]|char[^\\w]|void[^\\w]|auto[^\\w]|id_t[^\\w]|int[^\\w])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", "captures": { "1": { "patterns": [ @@ -7574,7 +7575,7 @@ ] }, "namespace_alias": { - "match": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<8>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<8>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:new)|(?:\\->\\*)|(?:<<=)|(?:>>=)|(?:<=>)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:\\-\\-)|(?:<<)|(?:>>)|(?:<=)|(?:>=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|(?:\\/=)|(?:%=)|(?:&=)|(?:\\^=)|(?:\\|=)|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=|,))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", + "begin": "(?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -10440,7 +10441,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -11478,7 +11479,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -12488,7 +12489,7 @@ }, "pragma": { "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?pragma\\b", - "end": "(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)?(?![\\w<:.])", + "match": "\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)?(?![\\w<:.])", "captures": { "0": { "patterns": [ @@ -13083,7 +13084,7 @@ } }, "scope_resolution": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13105,7 +13106,7 @@ } }, "scope_resolution_function_call": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13127,7 +13128,7 @@ } }, "scope_resolution_function_call_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13165,7 +13166,7 @@ } }, "scope_resolution_function_definition": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13187,7 +13188,7 @@ } }, "scope_resolution_function_definition_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13225,7 +13226,7 @@ } }, "scope_resolution_function_definition_operator_overload": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13247,7 +13248,7 @@ } }, "scope_resolution_function_definition_operator_overload_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13285,7 +13286,7 @@ } }, "scope_resolution_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13323,7 +13324,7 @@ } }, "scope_resolution_namespace_alias": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13345,7 +13346,7 @@ } }, "scope_resolution_namespace_alias_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13383,7 +13384,7 @@ } }, "scope_resolution_namespace_block": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13405,7 +13406,7 @@ } }, "scope_resolution_namespace_block_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13443,7 +13444,7 @@ } }, "scope_resolution_namespace_using": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13465,7 +13466,7 @@ } }, "scope_resolution_namespace_using_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13503,7 +13504,7 @@ } }, "scope_resolution_parameter": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13525,7 +13526,7 @@ } }, "scope_resolution_parameter_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13563,7 +13564,7 @@ } }, "scope_resolution_template_call": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13585,7 +13586,7 @@ } }, "scope_resolution_template_call_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13623,7 +13624,7 @@ } }, "scope_resolution_template_definition": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13645,7 +13646,7 @@ } }, "scope_resolution_template_definition_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13687,7 +13688,7 @@ "name": "punctuation.terminator.statement.cpp" }, "simple_type": { - "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?", + "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -16491,7 +16492,7 @@ } }, "type_alias": { - "match": "(using)(?:(?:\\s)+)?(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))(?:(?:\\s)+)?(\\=)(?:(?:\\s)+)?((?:typename)?)(?:(?:\\s)+)?((?:(?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))|(.*(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)?(?:(?:\\s)+)?(?:(;)|\\n)", + "match": "(using)(?:(?:\\s)+)?(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))(?:(?:\\s)+)?(\\=)(?:(?:\\s)+)?((?:typename)?)(?:(?:\\s)+)?((?:(?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))|(.*(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)?(?:(?:\\s)+)?(?:(;)|\\n)", "captures": { "1": { "name": "keyword.other.using.directive.cpp" @@ -17581,7 +17582,7 @@ "endCaptures": {}, "patterns": [ { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { @@ -18866,7 +18867,7 @@ ] }, "typename": { - "match": "(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)?(?![\\w<:.]))", + "match": "(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "storage.modifier.cpp" @@ -19755,7 +19756,7 @@ } }, "using_namespace": { - "begin": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((? Date: Fri, 17 Jun 2022 04:21:15 -0700 Subject: [PATCH 930/942] Try aligning tree view drop handling (#150468) * Try aligning external drop handling Tree views provided the first public drag and drop api. We've since also adopted drag and drop for dropping into editors as well, which resulted in some duplicated code between it and the tree view implementation This PR tried to better align the two by: - Use `extractEditorsDropData` to add the uriList - In the tree view, use `toVSDataTransfer` to convert the event to a `VSDataTransfer` - Remove `convertKnownMimes` as `extractEditorsDropData` handles this now * Add uriList for drag * Also handle 'DataTransfers.RESOURCES' * Fix monaco --- src/vs/editor/browser/dnd.ts | 36 ++++++- .../browser/dropIntoEditorContribution.ts | 18 +--- src/vs/platform/dnd/browser/dnd.ts | 32 +++--- src/vs/workbench/browser/dnd.ts | 4 +- .../workbench/browser/parts/views/treeView.ts | 99 +++++++------------ .../extensions/browser/extensionsViewlet.ts | 4 +- .../contrib/files/browser/fileImportExport.ts | 4 +- 7 files changed, 98 insertions(+), 99 deletions(-) diff --git a/src/vs/editor/browser/dnd.ts b/src/vs/editor/browser/dnd.ts index 80a5958504d70..b90bf922695b7 100644 --- a/src/vs/editor/browser/dnd.ts +++ b/src/vs/editor/browser/dnd.ts @@ -3,9 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { DataTransfers } from 'vs/base/browser/dnd'; +import { distinct } from 'vs/base/common/arrays'; import { createFileDataTransferItem, createStringDataTransferItem, IDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { Mimes } from 'vs/base/common/mime'; import { URI } from 'vs/base/common/uri'; -import { FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd'; +import { CodeDataTransfers, extractEditorsDropData, FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd'; export function toVSDataTransfer(dataTransfer: DataTransfer) { @@ -31,3 +34,34 @@ export function createFileDataTransferItemFromFile(file: File): IDataTransferIte return new Uint8Array(await file.arrayBuffer()); }); } + +const INTERNAL_DND_MIME_TYPES = Object.freeze([ + CodeDataTransfers.EDITORS, + CodeDataTransfers.FILES, + DataTransfers.RESOURCES, +]); + +export function addExternalEditorsDropData(dataTransfer: VSDataTransfer, dragEvent: DragEvent) { + if (dragEvent.dataTransfer && !dataTransfer.has(Mimes.uriList)) { + const editorData = extractEditorsDropData(dragEvent) + .filter(input => input.resource) + .map(input => input.resource!.toString()); + + // Also add in the files + for (const item of dragEvent.dataTransfer?.items) { + const file = item.getAsFile(); + if (file) { + editorData.push((file as FileAdditionalNativeProperties).path ? URI.file((file as FileAdditionalNativeProperties).path!).toString() : file.name); + } + } + + if (editorData.length) { + const str = distinct(editorData).join('\n'); + dataTransfer.replace(Mimes.uriList, createStringDataTransferItem(str)); + } + } + + for (const internal of INTERNAL_DND_MIME_TYPES) { + dataTransfer.delete(internal); + } +} diff --git a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts index 8254c5faf12e6..0c050ea25f63b 100644 --- a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts +++ b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts @@ -3,14 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { distinct } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Disposable } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import { relativePath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { toVSDataTransfer } from 'vs/editor/browser/dnd'; +import { addExternalEditorsDropData, toVSDataTransfer } from 'vs/editor/browser/dnd'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; @@ -25,8 +24,6 @@ import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/edit import { performSnippetEdit } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -37,7 +34,6 @@ export class DropIntoEditorController extends Disposable implements IEditorContr constructor( editor: ICodeEditor, @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IBulkEditService private readonly _bulkEditService: IBulkEditService, @@ -111,15 +107,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr } const textEditorDataTransfer = toVSDataTransfer(dragEvent.dataTransfer); - const editorData = (await this._instantiationService.invokeFunction(extractEditorsDropData, dragEvent)) - .filter(input => input.resource) - .map(input => input.resource!.toString()); - - if (editorData.length) { - const str = distinct(editorData).join('\n'); - textEditorDataTransfer.replace(Mimes.uriList, createStringDataTransferItem(str)); - } - + addExternalEditorsDropData(textEditorDataTransfer, dragEvent); return textEditorDataTransfer; } } diff --git a/src/vs/platform/dnd/browser/dnd.ts b/src/vs/platform/dnd/browser/dnd.ts index 14c6c8d11d322..4fafc45e2ec70 100644 --- a/src/vs/platform/dnd/browser/dnd.ts +++ b/src/vs/platform/dnd/browser/dnd.ts @@ -56,7 +56,7 @@ export interface IDraggedResourceEditorInput extends IBaseTextResourceEditorInpu allowWorkspaceOpen?: boolean; } -export async function extractEditorsDropData(accessor: ServicesAccessor, e: DragEvent): Promise> { +export function extractEditorsDropData(e: DragEvent): Array { const editors: IDraggedResourceEditorInput[] = []; if (e.dataTransfer && e.dataTransfer.types.length > 0) { @@ -107,18 +107,6 @@ export async function extractEditorsDropData(accessor: ServicesAccessor, e: Drag } } - // Web: Check for file transfer - if (isWeb && containsDragType(e, DataTransfers.FILES)) { - const files = e.dataTransfer.items; - if (files) { - const instantiationService = accessor.get(IInstantiationService); - const filesData = await instantiationService.invokeFunction(accessor => extractFilesDropData(accessor, e)); - for (const fileData of filesData) { - editors.push({ resource: fileData.resource, contents: fileData.contents?.toString(), isExternal: true, allowWorkspaceOpen: fileData.isDirectory }); - } - } - } - // Workbench contributions const contributions = Registry.as(Extensions.DragAndDropContribution).getAll(); for (const contribution of contributions) { @@ -136,6 +124,24 @@ export async function extractEditorsDropData(accessor: ServicesAccessor, e: Drag return editors; } +export async function extractEditorsAndFilesDropData(accessor: ServicesAccessor, e: DragEvent): Promise> { + const editors = extractEditorsDropData(e); + + // Web: Check for file transfer + if (e.dataTransfer && isWeb && containsDragType(e, DataTransfers.FILES)) { + const files = e.dataTransfer.items; + if (files) { + const instantiationService = accessor.get(IInstantiationService); + const filesData = await instantiationService.invokeFunction(accessor => extractFilesDropData(accessor, e)); + for (const fileData of filesData) { + editors.push({ resource: fileData.resource, contents: fileData.contents?.toString(), isExternal: true, allowWorkspaceOpen: fileData.isDirectory }); + } + } + } + + return editors; +} + export function createDraggedEditorInputFromRawResourcesData(rawResourcesData: string | undefined): IDraggedResourceEditorInput[] { const editors: IDraggedResourceEditorInput[] = []; diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 3a186e54ab4b5..dc877136e377b 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -19,7 +19,7 @@ import { FileAccess, Schemas } from 'vs/base/common/network'; import { isWindows } from 'vs/base/common/platform'; import { basename, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { CodeDataTransfers, createDraggedEditorInputFromRawResourcesData, Extensions, extractEditorsDropData, IDragAndDropContributionRegistry, IDraggedResourceEditorInput, IResourceStat } from 'vs/platform/dnd/browser/dnd'; +import { CodeDataTransfers, createDraggedEditorInputFromRawResourcesData, Extensions, extractEditorsAndFilesDropData, IDragAndDropContributionRegistry, IDraggedResourceEditorInput, IResourceStat } from 'vs/platform/dnd/browser/dnd'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -105,7 +105,7 @@ export class ResourcesDropHandler { } async handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup | undefined, afterDrop: (targetGroup: IEditorGroup | undefined) => void, targetIndex?: number): Promise { - const editors = await this.instantiationService.invokeFunction(accessor => extractEditorsDropData(accessor, event)); + const editors = await this.instantiationService.invokeFunction(accessor => extractEditorsAndFilesDropData(accessor, event)); if (!editors.length) { return; } diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 32078d1bf2bc6..fefe8a7eade78 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -31,7 +31,7 @@ import { isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import 'vs/css!./media/views'; -import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Command } from 'vs/editor/common/languages'; import { localize } from 'vs/nls'; import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -54,7 +54,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder, textCodeBlockBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; import { ColorScheme } from 'vs/platform/theme/common/theme'; import { FileThemeIcon, FolderThemeIcon, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { convertResourceUrlsToUriList, DraggedTreeItemsIdentifier, fillEditorsDragData, LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; +import { DraggedTreeItemsIdentifier, fillEditorsDragData, LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { API_OPEN_DIFF_EDITOR_COMMAND_ID, API_OPEN_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; @@ -66,7 +66,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; import { ITreeViewsService } from 'vs/workbench/services/views/browser/treeViewsService'; import { CodeDataTransfers } from 'vs/platform/dnd/browser/dnd'; -import { createFileDataTransferItemFromFile } from 'vs/editor/browser/dnd'; +import { addExternalEditorsDropData, toVSDataTransfer } from 'vs/editor/browser/dnd'; export class TreeViewPane extends ViewPane { @@ -1332,8 +1332,6 @@ interface TreeDragSourceInfo { itemHandles: string[]; } -const INTERNAL_MIME_TYPES = [CodeDataTransfers.EDITORS.toLowerCase(), CodeDataTransfers.FILES.toLowerCase()]; - export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { private readonly treeMimeType: string; private readonly treeItemsTransfer = LocalSelectionTransfer.getInstance(); @@ -1432,14 +1430,23 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { } onDragOver(data: IDragAndDropData, targetElement: ITreeItem, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction { - const types: Set = new Set(); - originalEvent.dataTransfer?.types.forEach((value, index) => { - if (INTERNAL_MIME_TYPES.indexOf(value) < 0) { - types.add(this.convertKnownMimes(value).type); + const dataTransfer = toVSDataTransfer(originalEvent.dataTransfer!); + addExternalEditorsDropData(dataTransfer, originalEvent); + + const types = new Set(Array.from(dataTransfer.entries()).map(x => x[0])); + + if (originalEvent.dataTransfer) { + // Also add uri-list if we have any files. At this stage we can't actually access the file itself though. + for (const item of originalEvent.dataTransfer.items) { + if (item.kind === 'file' || item.type === DataTransfers.RESOURCES.toLowerCase()) { + types.add(Mimes.uriList); + break; + } } - }); + } this.debugLog(types); + const dndController = this.dndController; if (!dndController || !originalEvent.dataTransfer || (dndController.dropMimeTypes.length === 0)) { return false; @@ -1475,26 +1482,11 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { return element.label ? element.label.label : (element.resourceUri ? this.labelService.getUriLabel(URI.revive(element.resourceUri)) : undefined); } - private convertKnownMimes(type: string, kind?: string, value?: string | FileSystemHandle): { type: string; value?: string } { - let convertedValue = undefined; - let convertedType = type; - if (type === DataTransfers.RESOURCES.toLowerCase()) { - convertedValue = value ? convertResourceUrlsToUriList(value as string) : undefined; - convertedType = Mimes.uriList; - } else if ((type === 'Files') || (kind === 'file')) { - convertedType = Mimes.uriList; - convertedValue = value ? (value as FileSystemHandle).name : undefined; - } - return { type: convertedType, value: convertedValue }; - } - async drop(data: IDragAndDropData, targetNode: ITreeItem | undefined, targetIndex: number | undefined, originalEvent: DragEvent): Promise { const dndController = this.dndController; if (!originalEvent.dataTransfer || !dndController) { return; } - const treeDataTransfer = new VSDataTransfer(); - const uris: URI[] = []; let treeSourceInfo: TreeDragSourceInfo | undefined; let willDropUuid: string | undefined; @@ -1502,51 +1494,30 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { willDropUuid = this.treeItemsTransfer.getData(DraggedTreeItemsIdentifier.prototype)![0].identifier; } - await Promise.all([...originalEvent.dataTransfer.items].map(async dataItem => { - const type = dataItem.type; - const kind = dataItem.kind; - const convertedType = this.convertKnownMimes(type, kind).type; - if ((INTERNAL_MIME_TYPES.indexOf(convertedType) < 0) - && (convertedType === this.treeMimeType) || (dndController.dropMimeTypes.indexOf(convertedType) >= 0)) { - if (dataItem.kind === 'string') { - await new Promise(resolve => - dataItem.getAsString(dataValue => { - if (convertedType === this.treeMimeType) { - treeSourceInfo = JSON.parse(dataValue); - } - if (dataValue) { - const converted = this.convertKnownMimes(type, kind, dataValue); - treeDataTransfer.append(converted.type, createStringDataTransferItem(converted.value + '')); - } - resolve(); - })); - } else if (dataItem.kind === 'file') { - const file = dataItem.getAsFile(); - if (file) { - uris.push(URI.file(file.path)); - treeDataTransfer.append(type, createFileDataTransferItemFromFile(file)); + const originalDataTransfer = toVSDataTransfer(originalEvent.dataTransfer); + addExternalEditorsDropData(originalDataTransfer, originalEvent); + + const outDataTransfer = new VSDataTransfer(); + for (const [type, item] of originalDataTransfer.entries()) { + if (type === this.treeMimeType || dndController.dropMimeTypes.indexOf(type) >= 0) { + outDataTransfer.append(type, item); + if (type === this.treeMimeType) { + try { + treeSourceInfo = JSON.parse(await item.asString()); + } catch { + // noop } } } - })); - - // Check if there are uris to add and add them - if (uris.length) { - treeDataTransfer.replace(Mimes.uriList, createStringDataTransferItem(uris.map(uri => uri.toString()).join('\n'))); } - const additionalWillDropPromise = this.treeViewsDragAndDropService.removeDragOperationTransfer(willDropUuid); - if (!additionalWillDropPromise) { - return dndController.handleDrop(treeDataTransfer, targetNode, new CancellationTokenSource().token, willDropUuid, treeSourceInfo?.id, treeSourceInfo?.itemHandles); - } - return additionalWillDropPromise.then(additionalDataTransfer => { - if (additionalDataTransfer) { - for (const item of additionalDataTransfer.entries()) { - treeDataTransfer.append(item[0], item[1]); - } + const additionalDataTransfer = await this.treeViewsDragAndDropService.removeDragOperationTransfer(willDropUuid); + if (additionalDataTransfer) { + for (const item of additionalDataTransfer.entries()) { + outDataTransfer.append(item[0], item[1]); } - return dndController.handleDrop(treeDataTransfer, targetNode, new CancellationTokenSource().token, willDropUuid, treeSourceInfo?.id, treeSourceInfo?.itemHandles); - }); + } + return dndController.handleDrop(outDataTransfer, targetNode, new CancellationTokenSource().token, willDropUuid, treeSourceInfo?.id, treeSourceInfo?.itemHandles); } onDragEnd(originalEvent: DragEvent): void { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index aaf033d7bd513..ae5a743a29074 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -58,7 +58,7 @@ import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/act import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { coalesce } from 'vs/base/common/arrays'; -import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd'; +import { extractEditorsAndFilesDropData } from 'vs/platform/dnd/browser/dnd'; import { extname } from 'vs/base/common/resources'; const SearchMarketplaceExtensionsContext = new RawContextKey('searchMarketplaceExtensions', false); @@ -541,7 +541,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE if (this.isSupportedDragElement(e)) { hide(overlay); - const vsixs = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsDropData(accessor, e))) + const vsixs = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsAndFilesDropData(accessor, e))) .map(editor => editor.resource && extname(editor.resource) === '.vsix' ? editor.resource : undefined)); if (vsixs.length > 0) { diff --git a/src/vs/workbench/contrib/files/browser/fileImportExport.ts b/src/vs/workbench/contrib/files/browser/fileImportExport.ts index 2215ecd93ad56..647334b791c3a 100644 --- a/src/vs/workbench/contrib/files/browser/fileImportExport.ts +++ b/src/vs/workbench/contrib/files/browser/fileImportExport.ts @@ -20,7 +20,7 @@ import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { URI } from 'vs/base/common/uri'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd'; +import { extractEditorsAndFilesDropData } from 'vs/platform/dnd/browser/dnd'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { isWeb } from 'vs/base/common/platform'; import { triggerDownload } from 'vs/base/browser/dom'; @@ -427,7 +427,7 @@ export class ExternalFileImport { private async doImport(target: ExplorerItem, source: DragEvent, token: CancellationToken): Promise { // Activate all providers for the resources dropped - const candidateFiles = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsDropData(accessor, source))).map(editor => editor.resource)); + const candidateFiles = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsAndFilesDropData(accessor, source))).map(editor => editor.resource)); await Promise.all(candidateFiles.map(resource => this.fileService.activateProvider(resource.scheme))); // Check for dropped external files to be folders From 5ff4901e69b7b76666ff5f4368632dc2bf9a03ca Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 17 Jun 2022 14:12:50 +0200 Subject: [PATCH 931/942] joh/issue150907 (#152447) * Use variants of the foreground color as CC border * use panel border color for CC border default --- .../browser/parts/titlebar/commandCenterControl.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts index fc9f14b7ea808..dd0207b43a2b6 100644 --- a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts @@ -24,7 +24,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import * as colors from 'vs/platform/theme/common/colorRegistry'; import { WindowTitle } from 'vs/workbench/browser/parts/titlebar/windowTitle'; -import { MENUBAR_SELECTION_BACKGROUND, MENUBAR_SELECTION_FOREGROUND, TITLE_BAR_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; +import { MENUBAR_SELECTION_BACKGROUND, MENUBAR_SELECTION_FOREGROUND, PANEL_BORDER, TITLE_BAR_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; export class CommandCenterControl { @@ -186,7 +186,7 @@ colors.registerColor( localize('commandCenter-background', "Background color of the command center"), false ); -const activeBackground = colors.registerColor( +colors.registerColor( 'commandCenter.activeBackground', { dark: MENUBAR_SELECTION_BACKGROUND, hcDark: MENUBAR_SELECTION_BACKGROUND, light: MENUBAR_SELECTION_BACKGROUND, hcLight: MENUBAR_SELECTION_BACKGROUND }, localize('commandCenter-activeBackground', "Active background color of the command center"), @@ -194,8 +194,7 @@ const activeBackground = colors.registerColor( ); // border: defaults to active background colors.registerColor( - 'commandCenter.border', - { dark: activeBackground, hcDark: colors.inputBorder, light: activeBackground, hcLight: colors.inputBorder }, + 'commandCenter.border', { dark: PANEL_BORDER, hcDark: PANEL_BORDER, light: PANEL_BORDER, hcLight: PANEL_BORDER }, localize('commandCenter-border', "Border color of the command center"), false ); From d20d9d6558788be53a970914323a9dac12083336 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 17 Jun 2022 14:34:11 +0200 Subject: [PATCH 932/942] add `cross-origin-isolated` to web-worker ext host iframe (#152462) add `cross-origin-isolated` to web-worker ext host iframe, related https://github.com/microsoft/vscode/issues/137884 --- .../services/extensions/browser/webWorkerExtensionHost.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index f387703969b88..ec07d5de4b46f 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -132,7 +132,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost const iframe = document.createElement('iframe'); iframe.setAttribute('class', 'web-worker-ext-host-iframe'); iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin'); - iframe.setAttribute('allow', 'usb'); + iframe.setAttribute('allow', 'usb; cross-origin-isolated;'); iframe.setAttribute('aria-hidden', 'true'); iframe.style.display = 'none'; From 0f05ed4758246bdf3efcdab610a3a87401f4a465 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 17 Jun 2022 14:34:37 +0200 Subject: [PATCH 933/942] Fix `workbench.action.submitComment` (#152464) Fixes #151739 --- src/vs/workbench/contrib/comments/browser/commentNode.ts | 6 ++++++ .../contrib/comments/browser/commentThreadWidget.ts | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index e37c1bfd5fe36..d1d118f5c15ed 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -273,6 +273,12 @@ export class CommentNode extends Disposable { } } + async submitComment(): Promise { + if (this._commentEditor && this._commentFormActions) { + this._commentFormActions.triggerDefaultAction(); + } + } + private createReactionPicker(reactionGroup: languages.CommentReaction[]): ToggleReactionsAction { const toggleReactionAction = this._register(new ToggleReactionsAction(() => { if (toggleReactionActionViewItem) { diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index c5fd1206a9617..f4f972a1a009f 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -18,7 +18,6 @@ import { ICommentService } from 'vs/workbench/contrib/comments/browser/commentSe import { CommentThreadBody } from 'vs/workbench/contrib/comments/browser/commentThreadBody'; import { CommentThreadHeader } from 'vs/workbench/contrib/comments/browser/commentThreadHeader'; import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys'; -import { CommentNode } from 'vs/workbench/contrib/comments/common/commentModel'; import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/commentThreadWidget'; import { IColorTheme } from 'vs/platform/theme/common/themeService'; import { contrastBorder, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -273,7 +272,9 @@ export class CommentThreadWidget extends async submitComment() { const activeComment = this._body.activeComment; - if (activeComment && !(activeComment instanceof CommentNode)) { + if (activeComment) { + activeComment.submitComment(); + } else if ((this._commentReply?.getPendingComment()?.length ?? 0) > 0) { this._commentReply?.submitComment(); } } From 47652af0b6c5fb18133688708e41d4ef3ce5e239 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 17 Jun 2022 15:20:53 +0200 Subject: [PATCH 934/942] Improve env variable handling around extension host connection type (#152466) --- src/vs/server/node/extensionHostConnection.ts | 5 +- .../api/node/extensionHostProcess.ts | 9 +- .../extensions/common/extensionHostEnv.ts | 93 +++++++++++++++++++ .../nativeLocalProcessExtensionHost.ts | 3 +- .../localProcessExtensionHost.ts | 3 +- 5 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 src/vs/workbench/services/extensions/common/extensionHostEnv.ts diff --git a/src/vs/server/node/extensionHostConnection.ts b/src/vs/server/node/extensionHostConnection.ts index 9e0a646f2256e..b2ed76583cea7 100644 --- a/src/vs/server/node/extensionHostConnection.ts +++ b/src/vs/server/node/extensionHostConnection.ts @@ -22,6 +22,7 @@ import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteCo import { removeDangerousEnvVariables } from 'vs/base/common/processes'; import { IExtensionHostStatusService } from 'vs/server/node/extensionHostStatusService'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { IPCExtHostConnection, writeExtHostConnection, SocketExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv'; export async function buildUserEnvironment(startParamsEnv: { [key: string]: string | null } = {}, withUserShellEnvironment: boolean, language: string, isDebug: boolean, environmentService: IServerEnvironmentService, logService: ILogService): Promise { const nlsConfig = await getNLSConfiguration(language, environmentService.userDataPath); @@ -244,11 +245,11 @@ export class ExtensionHostConnection { let extHostNamedPipeServer: net.Server | null; if (this._canSendSocket) { - env['VSCODE_EXTHOST_WILL_SEND_SOCKET'] = 'true'; + writeExtHostConnection(new SocketExtHostConnection(), env); extHostNamedPipeServer = null; } else { const { namedPipeServer, pipeName } = await this._listenOnPipe(); - env['VSCODE_IPC_HOOK_EXTHOST'] = pipeName; + writeExtHostConnection(new IPCExtHostConnection(pipeName), env); extHostNamedPipeServer = namedPipeServer; } diff --git a/src/vs/workbench/api/node/extensionHostProcess.ts b/src/vs/workbench/api/node/extensionHostProcess.ts index 331b384e3946f..bad55575c87b5 100644 --- a/src/vs/workbench/api/node/extensionHostProcess.ts +++ b/src/vs/workbench/api/node/extensionHostProcess.ts @@ -24,6 +24,7 @@ import { ProcessTimeRunOnceScheduler } from 'vs/base/common/async'; import { boolean } from 'vs/editor/common/config/editorOptions'; import { createURITransformer } from 'vs/workbench/api/node/uriTransformer'; import { MessagePortMain } from 'electron'; +import { ExtHostConnectionType, readExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv'; import 'vs/workbench/api/common/extHost.common.services'; import 'vs/workbench/api/node/extHost.node.services'; @@ -110,7 +111,9 @@ let onTerminate = function (reason: string) { }; function _createExtHostProtocol(): Promise { - if (process.env.VSCODE_WILL_SEND_MESSAGE_PORT) { + const extHostConnection = readExtHostConnection(process.env); + + if (extHostConnection.type === ExtHostConnectionType.MessagePort) { return new Promise((resolve, reject) => { @@ -139,7 +142,7 @@ function _createExtHostProtocol(): Promise { }); - } else if (process.env.VSCODE_EXTHOST_WILL_SEND_SOCKET) { + } else if (extHostConnection.type === ExtHostConnectionType.Socket) { return new Promise((resolve, reject) => { @@ -208,7 +211,7 @@ function _createExtHostProtocol(): Promise { } else { - const pipeName = process.env.VSCODE_IPC_HOOK_EXTHOST!; + const pipeName = extHostConnection.pipeName; return new Promise((resolve, reject) => { diff --git a/src/vs/workbench/services/extensions/common/extensionHostEnv.ts b/src/vs/workbench/services/extensions/common/extensionHostEnv.ts new file mode 100644 index 0000000000000..9b952930ecd49 --- /dev/null +++ b/src/vs/workbench/services/extensions/common/extensionHostEnv.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IProcessEnvironment } from 'vs/base/common/platform'; + +export const enum ExtHostConnectionType { + IPC = 1, + Socket = 2, + MessagePort = 3 +} + +/** + * The extension host will connect via named pipe / domain socket to its renderer. + */ +export class IPCExtHostConnection { + public static ENV_KEY = 'VSCODE_EXTHOST_IPC_HOOK'; + + public readonly type = ExtHostConnectionType.IPC; + + constructor( + public readonly pipeName: string + ) { } + + public serialize(env: IProcessEnvironment): void { + env[IPCExtHostConnection.ENV_KEY] = this.pipeName; + } +} + +/** + * The extension host will receive via nodejs IPC the socket to its renderer. + */ +export class SocketExtHostConnection { + public static ENV_KEY = 'VSCODE_EXTHOST_WILL_SEND_SOCKET'; + + public readonly type = ExtHostConnectionType.Socket; + + public serialize(env: IProcessEnvironment): void { + env[SocketExtHostConnection.ENV_KEY] = '1'; + } +} + +/** + * The extension host will receive via nodejs IPC the MessagePort to its renderer. + */ +export class MessagePortExtHostConnection { + public static ENV_KEY = 'VSCODE_WILL_SEND_MESSAGE_PORT'; + + public readonly type = ExtHostConnectionType.MessagePort; + + public serialize(env: IProcessEnvironment): void { + env[MessagePortExtHostConnection.ENV_KEY] = '1'; + } +} + +export type ExtHostConnection = IPCExtHostConnection | SocketExtHostConnection | MessagePortExtHostConnection; + +function clean(env: IProcessEnvironment): void { + delete env[IPCExtHostConnection.ENV_KEY]; + delete env[SocketExtHostConnection.ENV_KEY]; + delete env[MessagePortExtHostConnection.ENV_KEY]; +} + +/** + * Write `connection` into `env` and clean up `env`. + */ +export function writeExtHostConnection(connection: ExtHostConnection, env: IProcessEnvironment): void { + // Avoid having two different keys that might introduce amiguity or problems. + clean(env); + connection.serialize(env); +} + +/** + * Read `connection` from `env` and clean up `env`. + */ +export function readExtHostConnection(env: IProcessEnvironment): ExtHostConnection { + if (env[IPCExtHostConnection.ENV_KEY]) { + return cleanAndReturn(env, new IPCExtHostConnection(env[IPCExtHostConnection.ENV_KEY]!)); + } + if (env[SocketExtHostConnection.ENV_KEY]) { + return cleanAndReturn(env, new SocketExtHostConnection()); + } + if (env[MessagePortExtHostConnection.ENV_KEY]) { + return cleanAndReturn(env, new MessagePortExtHostConnection()); + } + throw new Error(`No connection information defined in environment!`); +} + +function cleanAndReturn(env: IProcessEnvironment, result: ExtHostConnection): ExtHostConnection { + clean(env); + return result; +} diff --git a/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts index d5bcf07f0392a..888db16060986 100644 --- a/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts @@ -12,6 +12,7 @@ import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import { IExtensionHostProcessOptions } from 'vs/platform/extensions/common/extensionHostStarter'; import { ILogService } from 'vs/platform/log/common/log'; +import { IPCExtHostConnection, writeExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv'; import { createMessageOfType, MessageType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ExtensionHostProcess, ExtHostMessagePortCommunication, IExtHostCommunication, SandboxLocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost'; @@ -64,7 +65,7 @@ class ExtHostNamedPipeCommunication extends Disposable implements IExtHostCommun establishProtocol(prepared: INamedPipePreparedData, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { const { namedPipeServer, pipeName } = prepared; - opts.env['VSCODE_IPC_HOOK_EXTHOST'] = pipeName; + writeExtHostConnection(new IPCExtHostConnection(pipeName), opts.env); return new Promise((resolve, reject) => { diff --git a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts index cb7abfcbf5326..2932c6be3ef37 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts @@ -45,6 +45,7 @@ import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/use import { generateUuid } from 'vs/base/common/uuid'; import { acquirePort } from 'vs/base/parts/ipc/electron-sandbox/ipc.mp'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { MessagePortExtHostConnection, writeExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv'; export interface ILocalProcessExtensionHostInitData { readonly autoStart: boolean; @@ -620,7 +621,7 @@ export class ExtHostMessagePortCommunication extends Disposable implements IExtH establishProtocol(prepared: void, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { - opts.env['VSCODE_WILL_SEND_MESSAGE_PORT'] = 'true'; + writeExtHostConnection(new MessagePortExtHostConnection(), opts.env); // Get ready to acquire the message port from the shared process worker const portPromise = acquirePort(undefined /* we trigger the request via service call! */, opts.responseChannel, opts.responseNonce); From a10626ee067e69d02aea8cbb2c9756ad8ae62e2f Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 17 Jun 2022 15:36:49 +0200 Subject: [PATCH 935/942] No warning on unknown product icon name (#152468) --- .../workbench/services/themes/common/productIconThemeSchema.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/services/themes/common/productIconThemeSchema.ts b/src/vs/workbench/services/themes/common/productIconThemeSchema.ts index a4c26ca6d6a93..f401690b666a4 100644 --- a/src/vs/workbench/services/themes/common/productIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/productIconThemeSchema.ts @@ -79,8 +79,7 @@ const schema: IJSONSchema = { }, iconDefinitions: { description: nls.localize('schema.iconDefinitions', 'Association of icon name to a font character.'), - $ref: iconsSchemaId, - additionalProperties: false + $ref: iconsSchemaId } } }; From a6c39bc1fcd8152a1447a22b2f1bf99ed494331b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 17 Jun 2022 15:51:15 +0200 Subject: [PATCH 936/942] Extract hover range computation to a separate method --- .../contrib/hover/browser/contentHover.ts | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index bf93d3b9d945b..6ea647ff75d24 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -199,17 +199,7 @@ export class ContentHoverController extends Disposable { } private _renderMessages(anchor: HoverAnchor, messages: IHoverPart[]): void { - // update column from which to show - let renderColumn = Constants.MAX_SAFE_SMALL_INTEGER; - let highlightRange: Range = messages[0].range; - let forceShowAtRange: Range | null = null; - for (const msg of messages) { - renderColumn = Math.min(renderColumn, msg.range.startColumn); - highlightRange = Range.plusRange(highlightRange, msg.range); - if (msg.forceShowAtRange) { - forceShowAtRange = msg.range; - } - } + const { showAtPosition, showAtRange, highlightRange } = ContentHoverController.computeHoverRanges(anchor.range, messages); const disposables = new DisposableStore(); const statusBar = disposables.add(new EditorHoverStatusBar(this._keybindingService)); @@ -247,8 +237,8 @@ export class ContentHoverController extends Disposable { this._widget.showAt(fragment, new ContentHoverVisibleData( colorPicker, - forceShowAtRange ? forceShowAtRange.getStartPosition() : new Position(anchor.range.startLineNumber, renderColumn), - forceShowAtRange ? forceShowAtRange : highlightRange, + showAtPosition, + showAtRange, this._editor.getOption(EditorOption.hover).above, this._computer.shouldFocus, disposables @@ -262,6 +252,24 @@ export class ContentHoverController extends Disposable { description: 'content-hover-highlight', className: 'hoverHighlight' }); + + public static computeHoverRanges(anchorRange: Range, messages: IHoverPart[]) { + let renderColumn = Constants.MAX_SAFE_SMALL_INTEGER; + let highlightRange: Range = messages[0].range; + let forceShowAtRange: Range | null = null; + for (const msg of messages) { + renderColumn = Math.min(renderColumn, msg.range.startColumn); + highlightRange = Range.plusRange(highlightRange, msg.range); + if (msg.forceShowAtRange) { + forceShowAtRange = msg.range; + } + } + return { + showAtPosition: forceShowAtRange ? forceShowAtRange.getStartPosition() : new Position(anchorRange.startLineNumber, renderColumn), + showAtRange: forceShowAtRange ? forceShowAtRange : highlightRange, + highlightRange + }; + } } class ContentHoverVisibleData { From 5ca287646df9260abed6dc8afea9965f83d5e8a1 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 17 Jun 2022 16:02:21 +0200 Subject: [PATCH 937/942] Fixes #151235: Always render the hover on the line of the anchor even if the hover provider returns a larger hover range --- .../contrib/hover/browser/contentHover.ts | 18 +++++++++---- .../hover/test/browser/contentHover.test.ts | 27 +++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 src/vs/editor/contrib/hover/test/browser/contentHover.test.ts diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index 6ea647ff75d24..586e15584c50f 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -9,7 +9,6 @@ import { coalesce } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { Constants } from 'vs/base/common/uint'; import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; @@ -254,19 +253,28 @@ export class ContentHoverController extends Disposable { }); public static computeHoverRanges(anchorRange: Range, messages: IHoverPart[]) { - let renderColumn = Constants.MAX_SAFE_SMALL_INTEGER; + // The anchor range is always on a single line + const anchorLineNumber = anchorRange.startLineNumber; + let renderStartColumn = anchorRange.startColumn; + let renderEndColumn = anchorRange.endColumn; let highlightRange: Range = messages[0].range; let forceShowAtRange: Range | null = null; + for (const msg of messages) { - renderColumn = Math.min(renderColumn, msg.range.startColumn); highlightRange = Range.plusRange(highlightRange, msg.range); + if (msg.range.startLineNumber === anchorLineNumber && msg.range.endLineNumber === anchorLineNumber) { + // this message has a range that is completely sitting on the line of the anchor + renderStartColumn = Math.min(renderStartColumn, msg.range.startColumn); + renderEndColumn = Math.max(renderEndColumn, msg.range.endColumn); + } if (msg.forceShowAtRange) { forceShowAtRange = msg.range; } } + return { - showAtPosition: forceShowAtRange ? forceShowAtRange.getStartPosition() : new Position(anchorRange.startLineNumber, renderColumn), - showAtRange: forceShowAtRange ? forceShowAtRange : highlightRange, + showAtPosition: forceShowAtRange ? forceShowAtRange.getStartPosition() : new Position(anchorRange.startLineNumber, renderStartColumn), + showAtRange: forceShowAtRange ? forceShowAtRange : new Range(anchorLineNumber, renderStartColumn, anchorLineNumber, renderEndColumn), highlightRange }; } diff --git a/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts b/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts new file mode 100644 index 0000000000000..7e35cea943b4b --- /dev/null +++ b/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ContentHoverController } from 'vs/editor/contrib/hover/browser/contentHover'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; + +suite('Content Hover', () => { + test('issue #151235: Gitlens hover shows up in the wrong place', () => { + const actual = ContentHoverController.computeHoverRanges( + new Range(5, 5, 5, 5), + [{ range: new Range(4, 1, 5, 6) }] + ); + assert.deepStrictEqual( + actual, + { + showAtPosition: new Position(5, 5), + showAtRange: new Range(5, 5, 5, 5), + highlightRange: new Range(4, 1, 5, 6) + } + ); + }); +}); From 2e91c26f1dd2278166dea0327c1ff8a87cd1730a Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 17 Jun 2022 16:15:55 +0200 Subject: [PATCH 938/942] Avoid old import syntax (#152471) --- .../inlineCompletions/test/browser/suggestWidgetModel.test.ts | 2 +- .../model/bracketPairColorizer/beforeEditPositionMapper.test.ts | 2 +- .../test/common/model/bracketPairColorizer/brackets.test.ts | 2 +- .../common/model/bracketPairColorizer/concat23Trees.test.ts | 2 +- .../model/bracketPairColorizer/getBracketPairsInRange.test.ts | 2 +- .../test/common/model/bracketPairColorizer/length.test.ts | 2 +- .../common/model/bracketPairColorizer/smallImmutableSet.test.ts | 2 +- .../test/common/model/bracketPairColorizer/tokenizer.test.ts | 2 +- src/vs/editor/test/common/viewModel/lineBreakData.test.ts | 2 +- src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts | 2 +- src/vs/workbench/contrib/extensions/browser/extensionEditor.ts | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts index 909e62f727c71..6d93e308c954e 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts @@ -27,7 +27,7 @@ import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { InMemoryStorageService, IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import assert = require('assert'); +import * as assert from 'assert'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts index 27fe72154f801..2fc81e3e176a3 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { splitLines } from 'vs/base/common/strings'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts index 16c59a7bfe214..abf6200a8fa69 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { LanguageAgnosticBracketTokens } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets'; import { SmallImmutableSet, DenseKeyProvider } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet'; diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts index f0b19c8a90f1c..c0c81ff209650 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { AstNode, AstNodeKind, ListAstNode, TextAstNode } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/ast'; import { toLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; import { concat23Trees } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/concat23Trees'; diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts index 4ee07976cb06f..e74219407a6ca 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { DisposableStore, disposeOnReturn } from 'vs/base/common/lifecycle'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts index 20e317d08431a..b1932043dddaf 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { Length, lengthAdd, lengthDiffNonNegative, lengthToObj, toLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; suite('Bracket Pair Colorizer - Length', () => { diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts index f36fe4db187c6..de3168139c512 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { DenseKeyProvider, SmallImmutableSet } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet'; suite('Bracket Pair Colorizer - ImmutableSet', () => { diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts index 2615475d9f0a0..d1800b09f0d3a 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { LanguageAgnosticBracketTokens } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets'; import { Length, lengthAdd, lengthsToRange, lengthZero } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; diff --git a/src/vs/editor/test/common/viewModel/lineBreakData.test.ts b/src/vs/editor/test/common/viewModel/lineBreakData.test.ts index 9b2715c1c2331..9de24985297e3 100644 --- a/src/vs/editor/test/common/viewModel/lineBreakData.test.ts +++ b/src/vs/editor/test/common/viewModel/lineBreakData.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { PositionAffinity } from 'vs/editor/common/model'; import { ModelDecorationInjectedTextOptions } from 'vs/editor/common/model/textModel'; import { ModelLineProjectionData } from 'vs/editor/common/modelLineProjectionData'; diff --git a/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts b/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts index 4947ad75e2e91..4c2501f46922a 100644 --- a/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts +++ b/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import type * as vscode from 'vscode'; -import assert = require('assert'); +import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; import { IEditorTabDto, IEditorTabGroupDto, MainThreadEditorTabsShape, TabInputKind, TabModelOperationKind, TextInputDto } from 'vs/workbench/api/common/extHost.protocol'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 13058dbd848bb..381096c914f6d 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -69,7 +69,7 @@ import { errorIcon, infoIcon, preReleaseIcon, verifiedPublisherIcon as verifiedP import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; import { IExtensionGalleryService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import semver = require('vs/base/common/semver/semver'); +import * as semver from 'vs/base/common/semver/semver'; class NavBar extends Disposable { From 083cf01e105b1ed93f8fef4e3deae462d54df37c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 17 Jun 2022 16:31:03 +0200 Subject: [PATCH 939/942] json indent pattern: handle escape characters (#152475) --- extensions/json/language-configuration.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/json/language-configuration.json b/extensions/json/language-configuration.json index 2dc97d50dbb3b..f9ec3fec78102 100644 --- a/extensions/json/language-configuration.json +++ b/extensions/json/language-configuration.json @@ -16,7 +16,7 @@ { "open": "`", "close": "`", "notIn": ["string", "comment"] } ], "indentationRules": { - "increaseIndentPattern": "({+(?=([^\"]*\"[^\"]*\")*[^\"}]*$))|(\\[+(?=([^\"]*\"[^\"]*\")*[^\"\\]]*$))", + "increaseIndentPattern": "({+(?=((\\\\.|[^\"\\\\])*\"(\\\\.|[^\"\\\\])*\")*[^\"}]*)$)|(\\[+(?=((\\\\.|[^\"\\\\])*\"(\\\\.|[^\"\\\\])*\")*[^\"\\]]*)$)", "decreaseIndentPattern": "^\\s*[}\\]],?\\s*$" } } From b70e64d36bba461132055b1d6309f15d6d05eabb Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 17 Jun 2022 07:54:16 -0700 Subject: [PATCH 940/942] Increase color contrast for list highlight (#152351) Increase color contrast (Refs #152184) --- src/vs/platform/theme/common/colorRegistry.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index c167a9b97da7c..6d57911f12d8b 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -427,7 +427,7 @@ export const diffDiagonalFill = registerColor('diffEditor.diagonalFill', { dark: export const listFocusBackground = registerColor('list.focusBackground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusForeground = registerColor('list.focusForeground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('listFocusForeground', "List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusOutline = registerColor('list.focusOutline', { dark: focusBorder, light: focusBorder, hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('listFocusOutline', "List/Tree outline color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); -export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#0060C0', hcDark: null, hcLight: Color.fromHex('#0F4A85').transparent(0.1) }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); +export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#04395E', light: '#0060C0', hcDark: null, hcLight: Color.fromHex('#0F4A85').transparent(0.1) }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionForeground = registerColor('list.activeSelectionForeground', { dark: Color.white, light: Color.white, hcDark: null, hcLight: null }, nls.localize('listActiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionIconForeground = registerColor('list.activeSelectionIconForeground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('listActiveSelectionIconForeground', "List/Tree icon foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionBackground = registerColor('list.inactiveSelectionBackground', { dark: '#37373D', light: '#E4E6F1', hcDark: null, hcLight: Color.fromHex('#0F4A85').transparent(0.1) }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); @@ -438,8 +438,8 @@ export const listInactiveFocusOutline = registerColor('list.inactiveFocusOutline export const listHoverBackground = registerColor('list.hoverBackground', { dark: '#2A2D2E', light: '#F0F0F0', hcDark: null, hcLight: Color.fromHex('#0F4A85').transparent(0.1) }, nls.localize('listHoverBackground', "List/Tree background when hovering over items using the mouse.")); export const listHoverForeground = registerColor('list.hoverForeground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('listHoverForeground', "List/Tree foreground when hovering over items using the mouse.")); export const listDropBackground = registerColor('list.dropBackground', { dark: '#062F4A', light: '#D6EBFF', hcDark: null, hcLight: null }, nls.localize('listDropBackground', "List/Tree drag and drop background when moving items around using the mouse.")); -export const listHighlightForeground = registerColor('list.highlightForeground', { dark: '#18A3FF', light: '#0066BF', hcDark: focusBorder, hcLight: focusBorder }, nls.localize('highlight', 'List/Tree foreground color of the match highlights when searching inside the list/tree.')); -export const listFocusHighlightForeground = registerColor('list.focusHighlightForeground', { dark: listHighlightForeground, light: ifDefinedThenElse(listActiveSelectionBackground, listHighlightForeground, '#9DDDFF'), hcDark: listHighlightForeground, hcLight: listHighlightForeground }, nls.localize('listFocusHighlightForeground', 'List/Tree foreground color of the match highlights on actively focused items when searching inside the list/tree.')); +export const listHighlightForeground = registerColor('list.highlightForeground', { dark: '#2AAAFF', light: '#0066BF', hcDark: focusBorder, hcLight: focusBorder }, nls.localize('highlight', 'List/Tree foreground color of the match highlights when searching inside the list/tree.')); +export const listFocusHighlightForeground = registerColor('list.focusHighlightForeground', { dark: listHighlightForeground, light: ifDefinedThenElse(listActiveSelectionBackground, listHighlightForeground, '#BBE7FF'), hcDark: listHighlightForeground, hcLight: listHighlightForeground }, nls.localize('listFocusHighlightForeground', 'List/Tree foreground color of the match highlights on actively focused items when searching inside the list/tree.')); export const listInvalidItemForeground = registerColor('list.invalidItemForeground', { dark: '#B89500', light: '#B89500', hcDark: '#B89500', hcLight: '#B5200D' }, nls.localize('invalidItemForeground', 'List/Tree foreground color for invalid items, for example an unresolved root in explorer.')); export const listErrorForeground = registerColor('list.errorForeground', { dark: '#F88070', light: '#B01011', hcDark: null, hcLight: null }, nls.localize('listErrorForeground', 'Foreground color of list items containing errors.')); export const listWarningForeground = registerColor('list.warningForeground', { dark: '#CCA700', light: '#855F00', hcDark: null, hcLight: null }, nls.localize('listWarningForeground', 'Foreground color of list items containing warnings.')); From 252c65540d8714a29c0b3ab27ecc119538773d23 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 17 Jun 2022 17:12:27 +0200 Subject: [PATCH 941/942] Adopt the same export patterns in `vs/css` as in `vs/nls` and bring over tests (#152396) * Adopt the same export patterns in `vs/css` as in `vs/nls` and bring over tests * Fix problem with loading nodejs modules --- .eslintrc.json | 1 + build/gulpfile.editor.js | 2 +- src/vs/base/test/node/css.build.test.ts | 313 +++++++++++++++ src/vs/css.build.ts | 482 ++++++++++++------------ src/vs/css.ts | 138 ++++--- src/vs/nls.build.ts | 12 + src/vs/nls.ts | 8 +- 7 files changed, 644 insertions(+), 312 deletions(-) create mode 100644 src/vs/base/test/node/css.build.test.ts diff --git a/.eslintrc.json b/.eslintrc.json index e6493f3815eaf..3e1b349becc0a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -277,6 +277,7 @@ // imports that are allowed in all /test/ files "when": "test", "allow": [ + "vs/css.build", "assert", "sinon", "sinon-test" diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index eb4f55f080158..fcc35c88b0147 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -30,7 +30,7 @@ const editorEntryPoints = [ include: [], exclude: ['vs/css', 'vs/nls'], prepend: [ - { path: 'out-editor-build/vs/css.js' }, + { path: 'out-editor-build/vs/css.js', amdModuleId: 'vs/css' }, { path: 'out-editor-build/vs/nls.js', amdModuleId: 'vs/nls' } ], }, diff --git a/src/vs/base/test/node/css.build.test.ts b/src/vs/base/test/node/css.build.test.ts new file mode 100644 index 0000000000000..9e7323b7f7a3c --- /dev/null +++ b/src/vs/base/test/node/css.build.test.ts @@ -0,0 +1,313 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { CSSPluginUtilities, rewriteUrls } from 'vs/css.build'; + +suite('CSSPlugin', () => { + + test('Utilities.pathOf', () => { + assert.strictEqual(CSSPluginUtilities.pathOf(''), ''); + assert.strictEqual(CSSPluginUtilities.pathOf('/a'), '/'); + assert.strictEqual(CSSPluginUtilities.pathOf('a/b/c.css'), 'a/b/'); + assert.strictEqual(CSSPluginUtilities.pathOf('a'), ''); + assert.strictEqual(CSSPluginUtilities.pathOf('a.com/a.css'), 'a.com/'); + assert.strictEqual(CSSPluginUtilities.pathOf('http://a.com/a.css'), 'http://a.com/'); + assert.strictEqual(CSSPluginUtilities.pathOf('https://a.com/a.css'), 'https://a.com/'); + assert.strictEqual(CSSPluginUtilities.pathOf('http://a.com/a/b/c.css'), 'http://a.com/a/b/'); + assert.strictEqual(CSSPluginUtilities.pathOf('https://a.com/a/b/c.css'), 'https://a.com/a/b/'); + assert.strictEqual(CSSPluginUtilities.pathOf('/a.css'), '/'); + assert.strictEqual(CSSPluginUtilities.pathOf('/a/b/c.css'), '/a/b/'); + }); + + test('Utilities.joinPaths', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.joinPaths(a, b), expected, '<' + a + '> + <' + b + '> = <' + expected + '>'); + } + mytest('', 'a.css', 'a.css'); + mytest('', './a.css', 'a.css'); + mytest('', '././././a.css', 'a.css'); + mytest('', './../a.css', '../a.css'); + mytest('', '../../a.css', '../../a.css'); + mytest('', '../../a/b/c.css', '../../a/b/c.css'); + mytest('/', 'a.css', '/a.css'); + mytest('/', './a.css', '/a.css'); + mytest('/', '././././a.css', '/a.css'); + mytest('/', './../a.css', '/a.css'); + mytest('/', '../../a.css', '/a.css'); + mytest('/', '../../a/b/c.css', '/a/b/c.css'); + mytest('x/y/z/', 'a.css', 'x/y/z/a.css'); + mytest('x/y/z/', './a.css', 'x/y/z/a.css'); + mytest('x/y/z/', '././././a.css', 'x/y/z/a.css'); + mytest('x/y/z/', './../a.css', 'x/y/a.css'); + mytest('x/y/z/', '../../a.css', 'x/a.css'); + mytest('x/y/z/', '../../a/b/c.css', 'x/a/b/c.css'); + + mytest('//a.com/', 'a.css', '//a.com/a.css'); + mytest('//a.com/', './a.css', '//a.com/a.css'); + mytest('//a.com/', '././././a.css', '//a.com/a.css'); + mytest('//a.com/', './../a.css', '//a.com/a.css'); + mytest('//a.com/', '../../a.css', '//a.com/a.css'); + mytest('//a.com/', '../../a/b/c.css', '//a.com/a/b/c.css'); + mytest('//a.com/x/y/z/', 'a.css', '//a.com/x/y/z/a.css'); + mytest('//a.com/x/y/z/', './a.css', '//a.com/x/y/z/a.css'); + mytest('//a.com/x/y/z/', '././././a.css', '//a.com/x/y/z/a.css'); + mytest('//a.com/x/y/z/', './../a.css', '//a.com/x/y/a.css'); + mytest('//a.com/x/y/z/', '../../a.css', '//a.com/x/a.css'); + mytest('//a.com/x/y/z/', '../../a/b/c.css', '//a.com/x/a/b/c.css'); + + mytest('http://a.com/', 'a.css', 'http://a.com/a.css'); + mytest('http://a.com/', './a.css', 'http://a.com/a.css'); + mytest('http://a.com/', '././././a.css', 'http://a.com/a.css'); + mytest('http://a.com/', './../a.css', 'http://a.com/a.css'); + mytest('http://a.com/', '../../a.css', 'http://a.com/a.css'); + mytest('http://a.com/', '../../a/b/c.css', 'http://a.com/a/b/c.css'); + mytest('http://a.com/x/y/z/', 'a.css', 'http://a.com/x/y/z/a.css'); + mytest('http://a.com/x/y/z/', './a.css', 'http://a.com/x/y/z/a.css'); + mytest('http://a.com/x/y/z/', '././././a.css', 'http://a.com/x/y/z/a.css'); + mytest('http://a.com/x/y/z/', './../a.css', 'http://a.com/x/y/a.css'); + mytest('http://a.com/x/y/z/', '../../a.css', 'http://a.com/x/a.css'); + mytest('http://a.com/x/y/z/', '../../a/b/c.css', 'http://a.com/x/a/b/c.css'); + + mytest('https://a.com/', 'a.css', 'https://a.com/a.css'); + mytest('https://a.com/', './a.css', 'https://a.com/a.css'); + mytest('https://a.com/', '././././a.css', 'https://a.com/a.css'); + mytest('https://a.com/', './../a.css', 'https://a.com/a.css'); + mytest('https://a.com/', '../../a.css', 'https://a.com/a.css'); + mytest('https://a.com/', '../../a/b/c.css', 'https://a.com/a/b/c.css'); + mytest('https://a.com/x/y/z/', 'a.css', 'https://a.com/x/y/z/a.css'); + mytest('https://a.com/x/y/z/', './a.css', 'https://a.com/x/y/z/a.css'); + mytest('https://a.com/x/y/z/', '././././a.css', 'https://a.com/x/y/z/a.css'); + mytest('https://a.com/x/y/z/', './../a.css', 'https://a.com/x/y/a.css'); + mytest('https://a.com/x/y/z/', '../../a.css', 'https://a.com/x/a.css'); + mytest('https://a.com/x/y/z/', '../../a/b/c.css', 'https://a.com/x/a/b/c.css'); + }); + + test('Utilities.commonPrefix', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.commonPrefix(a, b), expected, 'prefix(<' + a + '>, <' + b + '>) = <' + expected + '>'); + assert.strictEqual(CSSPluginUtilities.commonPrefix(b, a), expected, 'prefix(<' + b + '>, <' + a + '>) = <' + expected + '>'); + } + mytest('', '', ''); + mytest('x', '', ''); + mytest('x', 'x', 'x'); + mytest('aaaa', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaatuv', 'aaaa'); + }); + + test('Utilities.commonFolderPrefix', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.commonFolderPrefix(a, b), expected, 'folderPrefix(<' + a + '>, <' + b + '>) = <' + expected + '>'); + assert.strictEqual(CSSPluginUtilities.commonFolderPrefix(b, a), expected, 'folderPrefix(<' + b + '>, <' + a + '>) = <' + expected + '>'); + } + mytest('', '', ''); + mytest('x', '', ''); + mytest('x', 'x', ''); + mytest('aaaa', 'aaaa', ''); + mytest('aaaaxyz', 'aaaa', ''); + mytest('aaaaxyz', 'aaaatuv', ''); + mytest('/', '/', '/'); + mytest('x/', '', ''); + mytest('x/', 'x/', 'x/'); + mytest('aaaa/', 'aaaa/', 'aaaa/'); + mytest('aaaa/axyz', 'aaaa/a', 'aaaa/'); + mytest('aaaa/axyz', 'aaaa/atuv', 'aaaa/'); + }); + + test('Utilities.relativePath', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.relativePath(a, b), expected, 'relativePath(<' + a + '>, <' + b + '>) = <' + expected + '>'); + } + mytest('', '', ''); + mytest('x', '', ''); + mytest('x', 'x', 'x'); + mytest('aaaa', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaatuv', 'aaaatuv'); + + mytest('x/y/aaaaxyz', 'x/aaaatuv', '../aaaatuv'); + mytest('x/y/aaaaxyz', 'x/y/aaaatuv', 'aaaatuv'); + mytest('z/t/aaaaxyz', 'x/y/aaaatuv', '../../x/y/aaaatuv'); + mytest('aaaaxyz', 'x/y/aaaatuv', 'x/y/aaaatuv'); + + mytest('a', '/a', '/a'); + mytest('/', '/a', '/a'); + mytest('/a/b/c', '/a/b/c', '/a/b/c'); + mytest('/a/b', '/a/b/c/d', '/a/b/c/d'); + + mytest('a', 'http://a', 'http://a'); + mytest('/', 'http://a', 'http://a'); + mytest('/a/b/c', 'http://a/b/c', 'http://a/b/c'); + mytest('/a/b', 'http://a/b/c/d', 'http://a/b/c/d'); + + mytest('a', 'https://a', 'https://a'); + mytest('/', 'https://a', 'https://a'); + mytest('/a/b/c', 'https://a/b/c', 'https://a/b/c'); + mytest('/a/b', 'https://a/b/c/d', 'https://a/b/c/d'); + + mytest('x/', '', '../'); + mytest('x/', '', '../'); + mytest('x/', 'x/', ''); + mytest('x/a', 'x/a', 'a'); + }); + + test('Utilities.rewriteUrls', () => { + function mytest(originalFile: string, newFile: string, url: string, expected: string) { + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(\'' + url + '\'); }'), 'sel { background:url(' + expected + '); }'); + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(\"' + url + '\"); }'), 'sel { background:url(' + expected + '); }'); + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(' + url + '); }'), 'sel { background:url(' + expected + '); }'); + } + + // img/img.png + mytest('a.css', 'b.css', 'img/img.png', 'img/img.png'); + mytest('a.css', 't/b.css', 'img/img.png', '../img/img.png'); + mytest('a.css', 'x/y/b.css', 'img/img.png', '../../img/img.png'); + mytest('x/a.css', 'b.css', 'img/img.png', 'x/img/img.png'); + mytest('x/y/a.css', 'b.css', 'img/img.png', 'x/y/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', 'img/img.png', '../../x/y/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', 'img/img.png', '../y/img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', 'img/img.png', 'img/img.png'); + mytest('/a.css', 'b.css', 'img/img.png', '/img/img.png'); + mytest('/a.css', 'x/b.css', 'img/img.png', '/img/img.png'); + mytest('/a.css', 'x/y/b.css', 'img/img.png', '/img/img.png'); + mytest('/x/a.css', 'b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/a.css', 'x/b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/y/a.css', 'b.css', 'img/img.png', '/x/y/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', 'img/img.png', '/x/y/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', 'img/img.png', '/x/y/img/img.png'); + mytest('/a.css', '/b.css', 'img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', 'img/img.png', '/img/img.png'); + mytest('/x/a.css', '/b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/a.css', '/x/b.css', 'img/img.png', '/x/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', 'img/img.png', 'http://www.example.com/x/y/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example2.com/b.css', 'img/img.png', 'http://www.example.com/x/y/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', 'img/img.png', 'https://www.example.com/x/y/img/img.png'); + + // ../img/img.png + mytest('a.css', 'b.css', '../img/img.png', '../img/img.png'); + mytest('a.css', 't/b.css', '../img/img.png', '../../img/img.png'); + mytest('a.css', 'x/y/b.css', '../img/img.png', '../../../img/img.png'); + mytest('x/a.css', 'b.css', '../img/img.png', 'img/img.png'); + mytest('x/y/a.css', 'b.css', '../img/img.png', 'x/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', '../img/img.png', '../../x/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', '../img/img.png', '../img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', '../img/img.png', '../img/img.png'); + mytest('/a.css', 'b.css', '../img/img.png', '/img/img.png'); + mytest('/a.css', 'x/b.css', '../img/img.png', '/img/img.png'); + mytest('/a.css', 'x/y/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', 'b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'b.css', '../img/img.png', '/x/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', '../img/img.png', '/x/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', '../img/img.png', '/x/img/img.png'); + mytest('/a.css', '/b.css', '../img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', '/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', '/x/b.css', '../img/img.png', '/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', '../img/img.png', 'http://www.example.com/x/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example2.com/b.css', '../img/img.png', 'http://www.example.com/x/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', '../img/img.png', 'https://www.example.com/x/img/img.png'); + + // /img/img.png + mytest('a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('a.css', 't/b.css', '/img/img.png', '/img/img.png'); + mytest('a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('x/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', 'x/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', '/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', '/x/b.css', '/img/img.png', '/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', '/img/img.png', 'http://www.example.com/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example.com/x/y/b.css', '/img/img.png', 'http://www.example.com/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', '/img/img.png', 'https://www.example.com/img/img.png'); + + // http://example.com/img/img.png + mytest('a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('a.css', 't/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', 'x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', 'x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', '/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', '/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', '/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', '/x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example.com/x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + + + }); + + test('Utilities.rewriteUrls - quotes and spaces', () => { + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\'../img/img.png\'); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\t\'../img/img.png\'); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url( \'../img/img.png\'); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\'../img/img.png\'\t); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\'../img/img.png\' ); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url( \t \'../img/img.png\' \t); }'), 'sel { background:url(../../x/img/img.png); }'); + }); + + test('Bug 9601 - css should ignore data urls', () => { + const dataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAACHmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNC40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iPgogICAgICAgICA8ZGM6c3ViamVjdD4KICAgICAgICAgICAgPHJkZjpCYWcvPgogICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eG1wOkNyZWF0b3JUb29sPkFkb2JlIEltYWdlUmVhZHk8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+ClC8oVQAAAGnSURBVDiNrZMxTxNxGMZ///9dZWns9a4dTHSABFiuCU5dGt2d9BsQls6GD2LCd2AiQQfixKIJE0ObdKIUSvDa5uLZihP0Sh+HOw3ipOUZ3zzvL2+e932NJBaRe7/Q8Uw5eMRrzXllDU8A5mJkLB+/TflQ+67JXb+5O0FUNS9deLckns/tn2A7hxtDawZvn37Vp78AX8rmxZLDewf89HGJ+fgKCrkrBeuXKPy44hbGN7e8eTbRZwALcFE2nuOy48j6zmaTYP8Qtxaia9A1uLWQYP8QZ7OJI+s7LjsXZeMBIIlLn61xgEbLnqadtiQp7Z0orq8rrq8r7Z1IkqadtkbLnsYBuvTZkpQBhgF7SRVFJRQ3QqW9bgY5P1V6fpoDu4oboaISSqpoGLD3GzAIOEqqaFBBURHF9TWlZxlEktKzruL6mqJi5kmqaBBwJIl7Wf+7LICBIYBSKGyE+LsHuCurzPo9Zv0e7soq/u4BhY0Qpfn68p6HCbHv4Q0qtBPfarLd1LR1nAVWzDNphJq2jjXZbirxrQYV2n0PT9Lih/Rwp/xLCz3T/+gnd2VVRJs/vngAAAAASUVORK5CYII='; + + function mytest(originalFile: string, newFile: string) { + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(' + dataUrl + '); }'), 'sel { background:url(' + dataUrl + '); }'); + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url( \t' + dataUrl + '\t ); }'), 'sel { background:url(' + dataUrl + '); }'); + } + + mytest('a.css', 'b.css'); + mytest('a.css', 't/b.css'); + mytest('a.css', 'x/y/b.css'); + mytest('x/a.css', 'b.css'); + mytest('x/y/a.css', 'b.css'); + mytest('x/y/a.css', 't/u/b.css'); + mytest('x/y/a.css', 'x/u/b.css'); + mytest('x/y/a.css', 'x/y/b.css'); + mytest('/a.css', 'b.css'); + mytest('/a.css', 'x/b.css'); + mytest('/a.css', 'x/y/b.css'); + mytest('/x/a.css', 'b.css'); + mytest('/x/a.css', 'x/b.css'); + mytest('/x/a.css', 'x/y/b.css'); + mytest('/x/y/a.css', 'b.css'); + mytest('/x/y/a.css', 'x/b.css'); + mytest('/x/y/a.css', 'x/y/b.css'); + mytest('/a.css', '/b.css'); + mytest('/a.css', '/b.css'); + mytest('/x/a.css', '/b.css'); + mytest('/x/a.css', '/x/b.css'); + mytest('http://www.example.com/x/y/a.css', 'b.css'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example.com/x/y/b.css'); + mytest('https://www.example.com/x/y/a.css', 'b.css'); + }); +}); diff --git a/src/vs/css.build.ts b/src/vs/css.build.ts index 2107d144622e5..d4b3fcc96b270 100644 --- a/src/vs/css.build.ts +++ b/src/vs/css.build.ts @@ -3,288 +3,302 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -namespace CSSBuildLoaderPlugin { +interface ICSSPluginConfig { + inlineResources?: boolean | 'base64'; + inlineResourcesLimit?: number; +} - interface ICSSPluginConfig { - inlineResources?: boolean | 'base64'; - inlineResourcesLimit?: number; - } +interface ICSSEntryPointData { + moduleName: string; + contents: string; + fsPath: string; +} + +// This file gets compiled also with the standalone editor, +// so we cannot depend on types from node.d.ts +interface INodeFS { + readFileSync(path: string, encoding: 'utf8'): string; + readFileSync(path: string): INodeBuffer; +} +interface INodeBuffer { + length: number; + toString(encoding?: 'base64'): string; +} +interface INodePath { + dirname(p: string): string; + join(...paths: string[]): string; +} - // This file gets compiled also with the standalone editor, - // so we cannot depend on types from node.d.ts - interface INodeFS { - readFileSync(path: string, encoding: 'utf8'): string; - readFileSync(path: string): INodeBuffer; +const nodeReq = (module: string): T | undefined => { + if (typeof (require).__$__nodeRequire === 'function') { + return (require).__$__nodeRequire(module); } - interface INodeBuffer { - length: number; - toString(encoding?: 'base64'): string; + return undefined; +}; + +const fs = nodeReq('fs'); +const path = nodeReq('path'); + +let inlineResources: boolean | 'base64' = false; +let inlineResourcesLimit: number = 5000; + +const contentsMap: { [moduleName: string]: string } = {}; +const pathMap: { [moduleName: string]: string } = {}; +const entryPoints: { [entryPoint: string]: ICSSEntryPointData[] } = {}; +const inlinedResources: string[] = []; + +/** + * Invoked by the loader at build-time + */ +export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { + if (!fs) { + throw new Error(`Cannot load files without 'fs'!`); } - interface INodePath { - dirname(p: string): string; - join(...paths: string[]): string; + config = config || {}; + const myConfig = (config['vs/css'] || {}); + inlineResources = (typeof myConfig.inlineResources === 'undefined' ? false : myConfig.inlineResources); + inlineResourcesLimit = (myConfig.inlineResourcesLimit || 5000); + const cssUrl = req.toUrl(name + '.css'); + let contents = fs.readFileSync(cssUrl, 'utf8'); + if (contents.charCodeAt(0) === 65279 /* BOM */) { + // Remove BOM + contents = contents.substring(1); } - - const fs: INodeFS = (require).nodeRequire('fs'); - const path: INodePath = (require).nodeRequire('path'); - - interface ICSSEntryPointData { - moduleName: string; - contents: string; - fsPath: string; + if (config.isBuild) { + contentsMap[name] = contents; + pathMap[name] = cssUrl; } + load({}); +} - export class CSSPlugin implements AMDLoader.ILoaderPlugin { - - private inlineResources: boolean | 'base64' = false; - private inlineResourcesLimit: number = 5000; - - private readonly _contentsMap: { [moduleName: string]: string } = {}; - private readonly _pathMap: { [moduleName: string]: string } = {}; - private readonly _entryPoints: { [entryPoint: string]: ICSSEntryPointData[] } = {}; - private readonly _inlinedResources: string[] = []; - - public load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { - config = config || {}; - const myConfig = (config['vs/css'] || {}); - this.inlineResources = (typeof myConfig.inlineResources === 'undefined' ? false : myConfig.inlineResources); - this.inlineResourcesLimit = (myConfig.inlineResourcesLimit || 5000); - const cssUrl = req.toUrl(name + '.css'); - let contents = fs.readFileSync(cssUrl, 'utf8'); - if (contents.charCodeAt(0) === 65279 /* BOM */) { - // Remove BOM - contents = contents.substring(1); - } - if (config.isBuild) { - this._contentsMap[name] = contents; - this._pathMap[name] = cssUrl; +/** + * Invoked by the loader at build-time + */ +export function write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { + const entryPoint = write.getEntryPoint(); + + entryPoints[entryPoint] = entryPoints[entryPoint] || []; + entryPoints[entryPoint].push({ + moduleName: moduleName, + contents: contentsMap[moduleName], + fsPath: pathMap[moduleName], + }); + + write.asModule(pluginName + '!' + moduleName, + 'define([\'vs/css!' + entryPoint + '\'], {});' + ); +} + +/** + * Invoked by the loader at build-time + */ +export function writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { + if (entryPoints && entryPoints.hasOwnProperty(moduleName)) { + const fileName = req.toUrl(moduleName + '.css'); + const contents = [ + '/*---------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' *--------------------------------------------------------*/' + ], + entries = entryPoints[moduleName]; + for (let i = 0; i < entries.length; i++) { + if (inlineResources) { + contents.push(rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, inlineResources === 'base64', inlineResourcesLimit)); + } else { + contents.push(rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents)); } - load({}); } + write(fileName, contents.join('\r\n')); + } +} - public write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { - const entryPoint = write.getEntryPoint(); - - this._entryPoints[entryPoint] = this._entryPoints[entryPoint] || []; - this._entryPoints[entryPoint].push({ - moduleName: moduleName, - contents: this._contentsMap[moduleName], - fsPath: this._pathMap[moduleName], - }); - - write.asModule(pluginName + '!' + moduleName, - 'define([\'vs/css!' + entryPoint + '\'], {});' - ); - } +export function getInlinedResources(): string[] { + return inlinedResources || []; +} - public writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { - if (this._entryPoints && this._entryPoints.hasOwnProperty(moduleName)) { - const fileName = req.toUrl(moduleName + '.css'); - const contents = [ - '/*---------------------------------------------------------', - ' * Copyright (c) Microsoft Corporation. All rights reserved.', - ' *--------------------------------------------------------*/' - ], - entries = this._entryPoints[moduleName]; - for (let i = 0; i < entries.length; i++) { - if (this.inlineResources) { - contents.push(this._rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, this.inlineResources === 'base64', this.inlineResourcesLimit)); - } else { - contents.push(this._rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents)); +function rewriteOrInlineUrls(originalFileFSPath: string, originalFile: string, newFile: string, contents: string, forceBase64: boolean, inlineByteLimit: number): string { + if (!fs || !path) { + throw new Error(`Cannot rewrite or inline urls without 'fs' or 'path'!`); + } + return CSSPluginUtilities.replaceURL(contents, (url) => { + if (/\.(svg|png)$/.test(url)) { + const fsPath = path.join(path.dirname(originalFileFSPath), url); + const fileContents = fs.readFileSync(fsPath); + + if (fileContents.length < inlineByteLimit) { + const normalizedFSPath = fsPath.replace(/\\/g, '/'); + inlinedResources.push(normalizedFSPath); + + const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; + let DATA = ';base64,' + fileContents.toString('base64'); + + if (!forceBase64 && /\.svg$/.test(url)) { + // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris + const newText = fileContents.toString() + .replace(/"/g, '\'') + .replace(/%/g, '%25') + .replace(//g, '%3E') + .replace(/&/g, '%26') + .replace(/#/g, '%23') + .replace(/\s+/g, ' '); + const encodedData = ',' + newText; + if (encodedData.length < DATA.length) { + DATA = encodedData; } } - write(fileName, contents.join('\r\n')); + return '"data:' + MIME + DATA + '"'; } } - public getInlinedResources(): string[] { - return this._inlinedResources || []; - } + const absoluteUrl = CSSPluginUtilities.joinPaths(CSSPluginUtilities.pathOf(originalFile), url); + return CSSPluginUtilities.relativePath(newFile, absoluteUrl); + }); +} - private _rewriteOrInlineUrls(originalFileFSPath: string, originalFile: string, newFile: string, contents: string, forceBase64: boolean, inlineByteLimit: number): string { - return Utilities.replaceURL(contents, (url) => { - if (/\.(svg|png)$/.test(url)) { - const fsPath = path.join(path.dirname(originalFileFSPath), url); - const fileContents = fs.readFileSync(fsPath); - - if (fileContents.length < inlineByteLimit) { - const normalizedFSPath = fsPath.replace(/\\/g, '/'); - this._inlinedResources.push(normalizedFSPath); - - const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; - let DATA = ';base64,' + fileContents.toString('base64'); - - if (!forceBase64 && /\.svg$/.test(url)) { - // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris - const newText = fileContents.toString() - .replace(/"/g, '\'') - .replace(/%/g, '%25') - .replace(//g, '%3E') - .replace(/&/g, '%26') - .replace(/#/g, '%23') - .replace(/\s+/g, ' '); - const encodedData = ',' + newText; - if (encodedData.length < DATA.length) { - DATA = encodedData; - } - } - return '"data:' + MIME + DATA + '"'; - } - } +export function rewriteUrls(originalFile: string, newFile: string, contents: string): string { + return CSSPluginUtilities.replaceURL(contents, (url) => { + const absoluteUrl = CSSPluginUtilities.joinPaths(CSSPluginUtilities.pathOf(originalFile), url); + return CSSPluginUtilities.relativePath(newFile, absoluteUrl); + }); +} - const absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url); - return Utilities.relativePath(newFile, absoluteUrl); - }); - } +export class CSSPluginUtilities { - private _rewriteUrls(originalFile: string, newFile: string, contents: string): string { - return Utilities.replaceURL(contents, (url) => { - const absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url); - return Utilities.relativePath(newFile, absoluteUrl); - }); - } + public static startsWith(haystack: string, needle: string): boolean { + return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; } - export class Utilities { - - public static startsWith(haystack: string, needle: string): boolean { - return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; + /** + * Find the path of a file. + */ + public static pathOf(filename: string): string { + const lastSlash = filename.lastIndexOf('/'); + if (lastSlash !== -1) { + return filename.substr(0, lastSlash + 1); + } else { + return ''; } + } - /** - * Find the path of a file. - */ - public static pathOf(filename: string): string { - const lastSlash = filename.lastIndexOf('/'); - if (lastSlash !== -1) { - return filename.substr(0, lastSlash + 1); - } else { - return ''; + /** + * A conceptual a + b for paths. + * Takes into account if `a` contains a protocol. + * Also normalizes the result: e.g.: a/b/ + ../c => a/c + */ + public static joinPaths(a: string, b: string): string { + + function findSlashIndexAfterPrefix(haystack: string, prefix: string): number { + if (CSSPluginUtilities.startsWith(haystack, prefix)) { + return Math.max(prefix.length, haystack.indexOf('/', prefix.length)); } + return 0; } - /** - * A conceptual a + b for paths. - * Takes into account if `a` contains a protocol. - * Also normalizes the result: e.g.: a/b/ + ../c => a/c - */ - public static joinPaths(a: string, b: string): string { + let aPathStartIndex = 0; + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//'); + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://'); + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://'); - function findSlashIndexAfterPrefix(haystack: string, prefix: string): number { - if (Utilities.startsWith(haystack, prefix)) { - return Math.max(prefix.length, haystack.indexOf('/', prefix.length)); - } - return 0; + function pushPiece(pieces: string[], piece: string): void { + if (piece === './') { + // Ignore + return; } - - let aPathStartIndex = 0; - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//'); - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://'); - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://'); - - function pushPiece(pieces: string[], piece: string): void { - if (piece === './') { + if (piece === '../') { + const prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null); + if (prevPiece && prevPiece === '/') { // Ignore return; } - if (piece === '../') { - const prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null); - if (prevPiece && prevPiece === '/') { - // Ignore - return; - } - if (prevPiece && prevPiece !== '../') { - // Pop - pieces.pop(); - return; - } - } - // Push - pieces.push(piece); - } - - function push(pieces: string[], path: string): void { - while (path.length > 0) { - const slashIndex = path.indexOf('/'); - const piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path); - path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : ''); - pushPiece(pieces, piece); + if (prevPiece && prevPiece !== '../') { + // Pop + pieces.pop(); + return; } } + // Push + pieces.push(piece); + } - let pieces: string[] = []; - push(pieces, a.substr(aPathStartIndex)); - if (b.length > 0 && b.charAt(0) === '/') { - pieces = []; + function push(pieces: string[], path: string): void { + while (path.length > 0) { + const slashIndex = path.indexOf('/'); + const piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path); + path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : ''); + pushPiece(pieces, piece); } - push(pieces, b); + } - return a.substring(0, aPathStartIndex) + pieces.join(''); + let pieces: string[] = []; + push(pieces, a.substr(aPathStartIndex)); + if (b.length > 0 && b.charAt(0) === '/') { + pieces = []; } + push(pieces, b); - public static commonPrefix(str1: string, str2: string): string { - const len = Math.min(str1.length, str2.length); - for (let i = 0; i < len; i++) { - if (str1.charCodeAt(i) !== str2.charCodeAt(i)) { - return str1.substring(0, i); - } + return a.substring(0, aPathStartIndex) + pieces.join(''); + } + + public static commonPrefix(str1: string, str2: string): string { + const len = Math.min(str1.length, str2.length); + for (let i = 0; i < len; i++) { + if (str1.charCodeAt(i) !== str2.charCodeAt(i)) { + return str1.substring(0, i); } - return str1.substring(0, len); } + return str1.substring(0, len); + } - public static commonFolderPrefix(fromPath: string, toPath: string): string { - const prefix = Utilities.commonPrefix(fromPath, toPath); - const slashIndex = prefix.lastIndexOf('/'); - if (slashIndex === -1) { - return ''; - } - return prefix.substring(0, slashIndex + 1); + public static commonFolderPrefix(fromPath: string, toPath: string): string { + const prefix = CSSPluginUtilities.commonPrefix(fromPath, toPath); + const slashIndex = prefix.lastIndexOf('/'); + if (slashIndex === -1) { + return ''; } + return prefix.substring(0, slashIndex + 1); + } - public static relativePath(fromPath: string, toPath: string): string { - if (Utilities.startsWith(toPath, '/') || Utilities.startsWith(toPath, 'http://') || Utilities.startsWith(toPath, 'https://')) { - return toPath; - } + public static relativePath(fromPath: string, toPath: string): string { + if (CSSPluginUtilities.startsWith(toPath, '/') || CSSPluginUtilities.startsWith(toPath, 'http://') || CSSPluginUtilities.startsWith(toPath, 'https://')) { + return toPath; + } - // Ignore common folder prefix - const prefix = Utilities.commonFolderPrefix(fromPath, toPath); - fromPath = fromPath.substr(prefix.length); - toPath = toPath.substr(prefix.length); + // Ignore common folder prefix + const prefix = CSSPluginUtilities.commonFolderPrefix(fromPath, toPath); + fromPath = fromPath.substr(prefix.length); + toPath = toPath.substr(prefix.length); - const upCount = fromPath.split('/').length; - let result = ''; - for (let i = 1; i < upCount; i++) { - result += '../'; - } - return result + toPath; + const upCount = fromPath.split('/').length; + let result = ''; + for (let i = 1; i < upCount; i++) { + result += '../'; } + return result + toPath; + } - public static replaceURL(contents: string, replacer: (url: string) => string): string { - // Use ")" as the terminator as quotes are oftentimes not used at all - return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, (_: string, ...matches: string[]) => { - let url = matches[0]; - // Eliminate starting quotes (the initial whitespace is not captured) - if (url.charAt(0) === '"' || url.charAt(0) === '\'') { - url = url.substring(1); - } - // The ending whitespace is captured - while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) { - url = url.substring(0, url.length - 1); - } - // Eliminate ending quotes - if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') { - url = url.substring(0, url.length - 1); - } + public static replaceURL(contents: string, replacer: (url: string) => string): string { + // Use ")" as the terminator as quotes are oftentimes not used at all + return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, (_: string, ...matches: string[]) => { + let url = matches[0]; + // Eliminate starting quotes (the initial whitespace is not captured) + if (url.charAt(0) === '"' || url.charAt(0) === '\'') { + url = url.substring(1); + } + // The ending whitespace is captured + while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) { + url = url.substring(0, url.length - 1); + } + // Eliminate ending quotes + if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') { + url = url.substring(0, url.length - 1); + } - if (!Utilities.startsWith(url, 'data:') && !Utilities.startsWith(url, 'http://') && !Utilities.startsWith(url, 'https://')) { - url = replacer(url); - } + if (!CSSPluginUtilities.startsWith(url, 'data:') && !CSSPluginUtilities.startsWith(url, 'http://') && !CSSPluginUtilities.startsWith(url, 'https://')) { + url = replacer(url); + } - return 'url(' + url + ')'; - }); - } + return 'url(' + url + ')'; + }); } - - define('vs/css', new CSSPlugin()); } diff --git a/src/vs/css.ts b/src/vs/css.ts index 942040b4af59c..4a5ea48d1741c 100644 --- a/src/vs/css.ts +++ b/src/vs/css.ts @@ -3,91 +3,79 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - -namespace CSSLoaderPlugin { +interface ICSSPluginConfig { + disabled?: boolean; +} - interface ICSSPluginConfig { - disabled?: boolean; +/** + * Invoked by the loader at run-time + */ +export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { + config = config || {}; + const cssConfig = (config['vs/css'] || {}); + + if (cssConfig.disabled) { + // the plugin is asked to not create any style sheets + load({}); + return; } - class BrowserCSSLoader { - - public load(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { - if (this._linkTagExists(name, cssUrl)) { - callback(); - return; - } - this._createLinkTag(name, cssUrl, callback, errorback); - } - - private _linkTagExists(name: string, cssUrl: string): boolean { - const links = document.getElementsByTagName('link'); - for (let i = 0, len = links.length; i < len; i++) { - const nameAttr = links[i].getAttribute('data-name'); - const hrefAttr = links[i].getAttribute('href'); - if (nameAttr === name || hrefAttr === cssUrl) { - return true; - } - } - return false; + const cssUrl = req.toUrl(name + '.css'); + loadCSS(name, cssUrl, () => { + load({}); + }, (err: any) => { + if (typeof load.error === 'function') { + load.error('Could not find ' + cssUrl + '.'); } + }); +} - private _createLinkTag(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { - const linkNode = document.createElement('link'); - linkNode.setAttribute('rel', 'stylesheet'); - linkNode.setAttribute('type', 'text/css'); - linkNode.setAttribute('data-name', name); - - this._attachListeners(name, linkNode, callback, errorback); - linkNode.setAttribute('href', cssUrl); - - const head = document.head || document.getElementsByTagName('head')[0]; - head.appendChild(linkNode); - } +function loadCSS(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { + if (linkTagExists(name, cssUrl)) { + callback(); + return; + } + createLinkTag(name, cssUrl, callback, errorback); +} - private _attachListeners(name: string, linkNode: HTMLLinkElement, callback: () => void, errorback: (err: any) => void): void { - const unbind = () => { - linkNode.removeEventListener('load', loadEventListener); - linkNode.removeEventListener('error', errorEventListener); - }; - const loadEventListener = (e: any) => { - unbind(); - callback(); - }; - const errorEventListener = (e: any) => { - unbind(); - errorback(e); - }; - linkNode.addEventListener('load', loadEventListener); - linkNode.addEventListener('error', errorEventListener); +function linkTagExists(name: string, cssUrl: string): boolean { + const links = document.getElementsByTagName('link'); + for (let i = 0, len = links.length; i < len; i++) { + const nameAttr = links[i].getAttribute('data-name'); + const hrefAttr = links[i].getAttribute('href'); + if (nameAttr === name || hrefAttr === cssUrl) { + return true; } } + return false; +} - export class CSSPlugin implements AMDLoader.ILoaderPlugin { - - private _cssLoader = new BrowserCSSLoader(); - - public load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { - config = config || {}; - const cssConfig = (config['vs/css'] || {}); +function createLinkTag(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { + const linkNode = document.createElement('link'); + linkNode.setAttribute('rel', 'stylesheet'); + linkNode.setAttribute('type', 'text/css'); + linkNode.setAttribute('data-name', name); - if (cssConfig.disabled) { - // the plugin is asked to not create any style sheets - load({}); - return; - } + attachListeners(name, linkNode, callback, errorback); + linkNode.setAttribute('href', cssUrl); - const cssUrl = req.toUrl(name + '.css'); - this._cssLoader.load(name, cssUrl, () => { - load({}); - }, (err: any) => { - if (typeof load.error === 'function') { - load.error('Could not find ' + cssUrl + '.'); - } - }); - } - } + const head = document.head || document.getElementsByTagName('head')[0]; + head.appendChild(linkNode); +} - define('vs/css', new CSSPlugin()); +function attachListeners(name: string, linkNode: HTMLLinkElement, callback: () => void, errorback: (err: any) => void): void { + const unbind = () => { + linkNode.removeEventListener('load', loadEventListener); + linkNode.removeEventListener('error', errorEventListener); + }; + const loadEventListener = (e: any) => { + unbind(); + callback(); + }; + const errorEventListener = (e: any) => { + unbind(); + errorback(e); + }; + linkNode.addEventListener('load', loadEventListener); + linkNode.addEventListener('error', errorEventListener); } diff --git a/src/vs/nls.build.ts b/src/vs/nls.build.ts index 067fba651e389..f73bdc2d1b46c 100644 --- a/src/vs/nls.build.ts +++ b/src/vs/nls.build.ts @@ -16,6 +16,9 @@ export function localize(data: ILocalizeInfo | string, message: string, ...args: throw new Error(`Not supported at build time!`); } +/** + * Invoked by the loader at build-time + */ export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { if (!name || name.length === 0) { load({ localize }); @@ -28,6 +31,9 @@ export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoa } } +/** + * Invoked by the loader at build-time + */ export function write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { const entryPoint = write.getEntryPoint(); @@ -39,6 +45,9 @@ export function write(pluginName: string, moduleName: string, write: AMDLoader.I } } +/** + * Invoked by the loader at build-time + */ export function writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { if (entryPoints.hasOwnProperty(moduleName)) { const fileName = req.toUrl(moduleName + '.nls.js'); @@ -59,6 +68,9 @@ export function writeFile(pluginName: string, moduleName: string, req: AMDLoader } } +/** + * Invoked by the loader at build-time + */ export function finishBuild(write: AMDLoader.IPluginWriteFileCallback): void { write('nls.metadata.json', JSON.stringify({ keys: buildMapKeys, diff --git a/src/vs/nls.ts b/src/vs/nls.ts index ebe6f7038efdd..2a257b368d4ac 100644 --- a/src/vs/nls.ts +++ b/src/vs/nls.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - let isPseudo = (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0); const DEFAULT_TAG = 'i-default'; @@ -115,12 +113,18 @@ export function setPseudoTranslation(value: boolean) { isPseudo = value; } +/** + * Invoked in a built product at run-time + */ export function create(key: string, data: IBundledStrings): IConsumerAPI { return { localize: createScopedLocalize(data[key]) }; } +/** + * Invoked by the loader at run-time + */ export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { config = config || {}; if (!name || name.length === 0) { From 1ca9725a3b4264141e10d6a7697b74eaba955b58 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 17 Jun 2022 08:18:49 -0700 Subject: [PATCH 942/942] Disable terminal smoke tests on desktop/remote Fixes #152451 --- test/smoke/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 3836601534f73..d888604ca6cb7 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -400,7 +400,7 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { setupSearchTests(logger); setupNotebookTests(logger); setupLanguagesTests(logger); - setupTerminalTests(logger); + if (opts.web) { setupTerminalTests(logger); } // Not stable on desktop/remote https://github.com/microsoft/vscode/issues/146811 setupStatusbarTests(logger); if (quality !== Quality.Dev && quality !== Quality.OSS) { setupExtensionTests(logger); } setupMultirootTests(logger);